Advertise temporal gravity fog program

This commit is contained in:
Meta Vens 2026-07-15 21:09:20 +01:00
commit 636cff4878
9 changed files with 106 additions and 10 deletions

View file

@ -226,7 +226,13 @@ internal sealed class AetheriaDaemonYmirSmokeChecks
var shader = AetheriaRuntimeAssets.ProjectManifest(null).Assets.Single(asset =>
asset.Ref.AssetKey == "shader.environment.gravity-fog");
Require(shader.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight" &&
shader.Ref.Metadata["unity.volume.pass.raymarch"] == "0",
shader.Ref.Metadata["unity.volume.pass.raymarch"] == "0" &&
shader.Ref.Metadata["unity.volume.pass.temporal"] == "1" &&
shader.Ref.Metadata["unity.volume.pass.composite"] == "2" &&
shader.Ref.Metadata["unity.volume.texturePort.currentSample"] == "_UndersampleCloudTex" &&
shader.Ref.Metadata["unity.volume.texturePort.history"] == "_MainTex" &&
shader.Ref.Metadata["unity.volume.matrixPort.previousViewProjection"] == "_PrevVP" &&
shader.Ref.Metadata["unity.volume.floatPort.resetHistory"] == "_ResetHistory",
"provider asset metadata must own the concrete Unity volume-program ABI projected into runtime variants");
}

View file

@ -794,7 +794,7 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root)
"\"org.gamecult.cultlib\": \"https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.13\"",
"\"org.gamecult.eve.plugin-fields\": \"https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.0\"",
"\"org.gamecult.eve.surface\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.2\"",
"\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.37\""
"\"org.gamecult.eve.unity-scene\": \"https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.38\""
};
var missingManifestSymbols = requiredManifestSymbols
@ -813,7 +813,7 @@ static void RequireSharedEvePackagesImportedFromEveRepo(string root)
"cultlib-unity-v1.0.13",
"eve-plugin-fields-unity-v0.2.0",
"eveunity-surface-v0.2.2",
"eveunity-scene-v0.3.37",
"eveunity-scene-v0.3.38",
"\"source\": \"git\""
};

View file

@ -9,6 +9,6 @@
"org.gamecult.cultlib": "https://github.com/GameCult/CultLib.git?path=/unity/org.gamecult.cultlib#cultlib-unity-v1.0.13",
"org.gamecult.eve.plugin-fields": "https://github.com/GameCult/EvePlugins.git?path=/plugins/eve-plugin-fields/unity/org.gamecult.eve.plugin-fields#eve-plugin-fields-unity-v0.2.0",
"org.gamecult.eve.surface": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.surface#eveunity-surface-v0.2.2",
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.37"
"org.gamecult.eve.unity-scene": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.38"
}
}

View file

@ -144,7 +144,7 @@
"hash": "140e1bd963a0033e66777a3b2c5fe6e9c97dfe32"
},
"org.gamecult.eve.unity-scene": {
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.37",
"version": "https://github.com/GameCult/EveUnity.git?path=/packages/org.gamecult.eve.unity-scene#eveunity-scene-v0.3.38",
"depth": 0,
"source": "git",
"dependencies": {
@ -154,7 +154,7 @@
"com.unity.mathematics": "1.3.2",
"com.unity.modules.particlesystem": "1.0.0"
},
"hash": "5ce654800760203154132d4b0fd1475294283ba6"
"hash": "de580fe88350f422e30f1e4b64ded5c33070b118"
},
"com.unity.modules.assetbundle": {
"version": "1.0.0",

View file

@ -718,7 +718,10 @@ public class DaemonRuntimeDocumentTests
Assert.IsTrue(assetManifest.Assets.Any(asset =>
asset.Ref.AssetKey == "shader.environment.gravity-fog" &&
asset.Ref.Kind == AetheriaRuntimeAssetKinds.Shader &&
asset.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight"));
asset.Ref.Metadata["unity.volume.texturePort.surfaceHeight"] == "_NebulaSurfaceHeight" &&
asset.Ref.Metadata["unity.volume.pass.temporal"] == "1" &&
asset.Ref.Metadata["unity.volume.texturePort.currentSample"] == "_UndersampleCloudTex" &&
asset.Ref.Metadata["unity.volume.texturePort.history"] == "_MainTex"));
Assert.IsTrue(assetManifest.Assets.Any(asset =>
asset.Ref.AssetKey == "texture.environment.volume-dither" &&
asset.Ref.Kind == AetheriaRuntimeAssetKinds.Texture));

View file

@ -267,9 +267,59 @@ Shader "Aetheria/CloudShader"
float4 frag(v2f i) : SV_Target
{
float3 vspos = float3(i.vsray, 1.0);
// float4 worldPos = mul(unity_CameraToWorld, float4(vspos, 1.0f));
// worldPos /= worldPos.w;
float4 raymarchResult = tex2D(_UndersampleCloudTex, i.uv);
if (_ResetHistory >= 0.5f)
return raymarchResult;
float distance;
float density;
float density2 = 0.0f;
unpack(raymarchResult.a, distance, density);
distance *= _ProjectionParams.z;
half outOfBound;
float3 previousWorldPosition = mul(
unity_CameraToWorld,
float4(normalize(vspos) * distance, 1.0f)).xyz;
float2 prevUV = PrevUV(float4(previousWorldPosition, 1.0f), outOfBound);
float4 prevSample = tex2D(_MainTex, prevUV);
float2 xoffset = float2(_UndersampleCloudTex_TexelSize.x, 0.0f);
float2 yoffset = float2(0.0f, _UndersampleCloudTex_TexelSize.y);
float4 m1 = 0.0f;
float4 m2 = 0.0f;
[unroll]
for (int x = -1; x <= 1; x++)
{
[unroll]
for (int y = -1; y <= 1; y++)
{
float4 value;
if (x == 0 && y == 0)
{
value = float4(raymarchResult.rgb, distance);
}
else
{
value = tex2Dlod(
_UndersampleCloudTex,
float4(i.uv + xoffset * x + yoffset * y, 0.0f, 0.0f));
float neighborDistance;
unpack(value.a, neighborDistance, density2);
value = float4(value.rgb, neighborDistance);
}
m1 += value;
m2 += value * value;
}
}
float4 mean = m1 / 9.0f;
float4 sigma = sqrt(abs(m2 / 9.0f - mean * mean));
prevSample = ClipAABB(mean - 0.5f * sigma, mean + 0.5f * sigma, prevSample);
raymarchResult = lerp(
float4(prevSample.rgb, density),
float4(raymarchResult.rgb, density2),
max(0.05f, outOfBound));
return raymarchResult;
}
ENDCG

View file

@ -224,6 +224,7 @@ namespace GameCult.Aetheria.State.Verse
["unityAssetPath"] = "Assets/Shaders/Raymarching/CloudShader.shader",
["presentationRole"] = "environment.gravity-fog.volume",
["unity.volume.pass.raymarch"] = "0",
["unity.volume.pass.temporal"] = "1",
["unity.volume.pass.composite"] = "2",
["unity.volume.quality.high.keyword"] = "HIGH_QUALITY",
["unity.volume.quality.normal.keyword"] = "MEDIUM_QUALITY",
@ -234,11 +235,15 @@ namespace GameCult.Aetheria.State.Verse
["unity.volume.texturePort.patch"] = "_NebulaPatch",
["unity.volume.texturePort.tint"] = "_NebulaTint",
["unity.volume.texturePort.dither"] = "_DitheringTex",
["unity.volume.texturePort.currentSample"] = "_UndersampleCloudTex",
["unity.volume.texturePort.history"] = "_MainTex",
["unity.volume.texturePort.cloud"] = "_CloudTex",
["unity.volume.vectorPort.viewportTransform"] = "_GridTransform",
["unity.volume.matrixPort.cameraInverseViewProjection"] = "_CamInvProj",
["unity.volume.matrixPort.previousViewProjection"] = "_PrevVP",
["unity.volume.vectorPort.cameraProjectionExtents"] = "_ProjectionExtents",
["unity.volume.floatPort.raymarchOffset"] = "_RaymarchOffset",
["unity.volume.floatPort.resetHistory"] = "_ResetHistory",
["unity.volume.vectorPort.ditherCoordinates"] = "_DitheringCoords",
["unity.volume.floatPort.fillDensity"] = "_NebulaFillDensity",
["unity.volume.floatPort.fillDistance"] = "_NebulaFillDistance",

View file

@ -865,6 +865,38 @@ legacy catalog cache, and legacy UI paths should be migration-only or deleted.
height, and negative body splats. Released cold/warm witnesses exercise the
persisted daemon path.
### Gravity Fog Presentation Contract
- Owner: Aetheria owns the gravity/fog splat values, authored volume tuning, and
provider shader asset. Eve Fields owns the portable splat document and
`field.volume3d` logical ports. EveUnity owns only the native render lifecycle.
- Inputs: the canonical viewport splat document, logical surface-height,
patch-height, patch, tint, scalar/vector parameters, provider asset reference,
and the selected runtime asset variant's native program metadata.
- Outputs: the generic runtime rasterizes logical field layers, executes the
advertised raymarch, optional temporal-history, and composite passes, and
presents pixels. No pass can emit gameplay state or commands.
- Derived state: Unity materials, shader-property names, pass indices, keywords,
raymarch targets, ping-pong history textures, previous camera matrices, and
composite counters are runtime projections. They are neither daemon truth nor
portable Eve state.
- Forbidden writers: the old `VolumeSampling`/`VolumeCloudRenderer` presentation
path, shader code, Unity cameras, and generic lowerers cannot calculate gravity,
move bodies, or publish world truth. EveUnity may not import an Aetheria assembly
or branch on Aetheria asset identities.
- Shared paths: first frame, camera motion, reconnect, viewport resize, and warm
asset reuse all enter the same generic volume lifecycle. History resets on a
new node, program, or render size. A provider that advertises a temporal pass
without all required history ports fails closed.
- Cut line: the portable surface contains logical field and parameter names only.
`_Nebula*`, `_PrevVP`, `_ResetHistory`, shader keywords, and pass indices live
exclusively in the provider's Unity asset-variant metadata.
- Verification layer: EveUnity package tests prove two-pass compatibility,
complete temporal-program acceptance, partial-program rejection, and absence
of Aetheria assembly references. Aetheria daemon smoke proves the exact fossil
shader ABI is advertised outside the portable surface. A live capture is still
required for visual parity.
### Daemon Asset Content Delivery
- Owner: the Aetheria daemon owns bundle content and serves bytes only through

View file

@ -34,7 +34,7 @@ validation proves contract shape, not wiring, exclusivity, lowering, or parity.
| Pilot camera | ARPG follow/dock cameras | Eve view semantics + runtime | camera rig and semantic view id | EveUnity camera rig | authored ARPG follow distance, field of view, screen framing, and damping are provider-owned and generically lowered; dock/view transition parity remains absent | dock transition timeline and reconnect |
| Render channels | Unity layers/culling masks | provider surface policy + runtime asset variant | semantic channel exclusions | EveUnity native layer mapping | wired, exclusive, and lowered: the pilot surface excludes the semantic `map` channel; the Unity asset variant maps `map` to its authored native layer; EveUnity subtracts that layer from the configured camera mask; portable state contains no Unity layer or mask; package tests and the clean-client dependency verifier pass | live pilot capture proving no map glyphs while the map camera retains them |
| Minimap/tactical map | `ZoneRenderer`, map cameras | daemon world facts | explicit map projections | Unity and Electron | facts fragmented; generic lowerers absent | same state in both runtimes |
| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | owner-exists; basic lowering; composition parity absent | visual parity captures |
| Fields/fog/gravity | zone renderer and shaders | daemon/plugin semantics | Fields plugin documents | EveUnity fields lowerer | daemon publishes four canonical gravity/fog splat layers through `field.volume3d`; the provider asset variant owns the fossil shader, native ports, quality/features, and raymarch/temporal/composite pass graph; released EveUnity `0.3.38` lowers the optional temporal-history lifecycle generically and fails closed on partial native programs; the warm released witness executes the provider bundle, four layers, and repeated composites without importing Aetheria code; the current pilot capture remains nearly black/geometric rather than the authored blue fog sea, so visual composition parity is explicitly absent | diagnose field values, colour/exposure/composition and camera-depth behavior against the fossil, then capture temporal stability and visual parity |
| HUD/schematic | ARPG HUD and `SchematicDisplay` | Eve composition from daemon facts | semantic status/components | EveUnity UI | provider-authored transparent cockpit is wired, projected, and lowered through generic UI Toolkit panes, metrics, and progress components for hull, shield, capacitor, temperature, cargo, weapon cooldown/groups, selected target, target lock, target shield, and target hull; the original armor/equipment schematic and transient warning choreography remain reference-only | schematic topology, warning timelines, dynamic action state, and composited live capture |
| Hit/lock/warnings | `ActionGameManager` feedback | daemon event facts | transient feedback stream | VFX/UI/audio | bounded deduplicated game-event ledger and Eve `feedback.stream` wired for pickup outcomes, projectile launch/impact, entity damage, alive-to-dead destruction, authoritative weapon reload chronology, and episode-deduplicated `weapon.fire.refused` facts for insufficient energy or missing ammunition; refusal identity, authored weapon, target, reason, and remaining magazine state are runtime-independent cockpit inputs | add lock transitions, thermal and docking event families, negative transaction fixtures, and runtime effect consumption |
| Assets | `Assets/Resources` | Aetheria provider | asset catalog + CultMesh CDN | runtime cache/load | provider manifests name deterministic bundle-internal paths; the authoring build derives presentation-only prefabs, strips missing and non-Eve `MonoBehaviour` components, verifies both saved prefabs and the loaded bundle, and publishes the immutable bundle through CultMesh; released-client warm-cache loading, content verification, and rendered provider identity are proven, while cold transfer still times out because the 46.1 MB body travels through batched snapshot records | mapped/network body transport followed by empty-cache download, atomic promotion, and cache reuse proof |