diff --git a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs index e7b136727..b9f403f79 100644 --- a/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs +++ b/Assets/Scripts/Tests/DaemonRuntimeDocumentTests.cs @@ -522,6 +522,40 @@ public class DaemonRuntimeDocumentTests "shader.environment.gravity-fog")); Assert.IsTrue(ContainsSurfaceProp(surface.Surface.Root, "documentSchema", AetheriaRuntimeDaemonSchemas.RenderSplatsViewport)); + var gravityFog = FindSurfaceComponent( + surface.Surface.Root, + "aetheria.daemon.game.world.gravity-fog"); + var stardust = FindSurfaceComponent( + surface.Surface.Root, + "aetheria.daemon.game.world.stardust"); + Assert.IsNotNull(gravityFog); + Assert.IsNotNull(stardust); + StringAssert.Contains("flow.value3d", gravityFog!.Props["features"]); + StringAssert.Contains("flow.value3d", stardust!.Props["features"]); + + frame.RenderSettings = new AetheriaRuntimeDaemonRenderSettings( + default, + default, + default, + 0, + 0, + 0, + 0, + 0, + 0, + useValue3DFlow: false); + var triangleSurface = AetheriaRuntimeDaemonGameSurfaceBuilder.Build( + frame, + new AetheriaRuntimeDaemonHealthDocument(), + AetheriaRuntimeDaemonCommandBoundaryDocument.Create("test-daemon")); + var triangleFog = FindSurfaceComponent( + triangleSurface.Surface.Root, + "aetheria.daemon.game.world.gravity-fog"); + var triangleStardust = FindSurfaceComponent( + triangleSurface.Surface.Root, + "aetheria.daemon.game.world.stardust"); + StringAssert.DoesNotContain("flow.value3d", triangleFog!.Props["features"]); + StringAssert.DoesNotContain("flow.value3d", triangleStardust!.Props["features"]); Assert.IsFalse(ContainsSurfaceValueFragment(surface.Surface.Root, "_Nebula"), "The semantic Eve surface must not own Unity shader property names."); Assert.AreEqual(EveEntitySoaViewDocument.SchemaId, portable.Schema); @@ -796,8 +830,13 @@ public class DaemonRuntimeDocumentTests asset.Ref.Metadata["unity.volume.matrixPort.cameraToWorld"] == "_CamToWorld" && asset.Ref.Metadata["unity.volume.quality.bootstrap"] == "ultra" && asset.Ref.Metadata["unity.volume.quality.ultra.keyword"] == "ULTRA_QUALITY" && + asset.Ref.Metadata["unity.volume.feature.flow.value3d.keyword"] == "FLOW_VALUE3D" && !asset.Ref.Metadata.ContainsKey("unity.volume.pass.temporal") && !asset.Ref.Metadata.ContainsKey("unity.volume.texturePort.history"))); + Assert.IsTrue(assetManifest.Assets.Any(asset => + asset.Ref.AssetKey == "compute.environment.stardust" && + asset.Ref.Kind == AetheriaRuntimeAssetKinds.ComputeShader && + asset.Ref.Metadata["unity.particles.feature.flow.value3d.keyword"] == "FLOW_VALUE3D")); Assert.IsTrue(assetManifest.Assets.Any(asset => asset.Ref.AssetKey == "texture.environment.volume-dither" && asset.Ref.Kind == AetheriaRuntimeAssetKinds.Texture)); diff --git a/Assets/Shaders/Compute/Stardust/Stardust.compute b/Assets/Shaders/Compute/Stardust/Stardust.compute index c2698f766..6d310fe91 100644 --- a/Assets/Shaders/Compute/Stardust/Stardust.compute +++ b/Assets/Shaders/Compute/Stardust/Stardust.compute @@ -3,6 +3,7 @@ // but it's broken in compute due to a bug that has been fixed but not backported yet #pragma multi_compile __ FLOW_GLOBAL #pragma multi_compile __ FLOW_SLOPE +#pragma multi_compile __ FLOW_VALUE3D #pragma multi_compile __ NOISE_SLOPE //Tell unity that we have defined a kernel @@ -144,4 +145,4 @@ void UpdateParticles(uint3 id : SV_DispatchThreadID, int3 groupID : SV_GroupThre //const float d = pow(density(particles[id.x].position) + .01, 0.1); particles[id.x].size = lerp(minimumSize, maximumSize, saturate(RandomValue())) * parabola(lifetime, 2); particles[id.x].color = HueTexture.SampleLevel(samplerHueTexture, float2(RandomValue(), 0.0f), 0.0f).rgb * lighting; -} \ No newline at end of file +} diff --git a/Assets/Shaders/Volumetric.cginc b/Assets/Shaders/Volumetric.cginc index 82a7f9458..82d0a1667 100644 --- a/Assets/Shaders/Volumetric.cginc +++ b/Assets/Shaders/Volumetric.cginc @@ -1,5 +1,6 @@ #pragma multi_compile __ FLOW_GLOBAL #pragma multi_compile __ FLOW_SLOPE +#pragma multi_compile __ FLOW_VALUE3D #pragma multi_compile __ NOISE_SLOPE #include "UnityCG.cginc" @@ -73,6 +74,22 @@ float3 Tri3D( float3 P ) return cross(t1,t2); } +float3 Value3D_Deriv( float3 P ) +{ + float3 Pf = frac(P); + float3 Pf2 = frac(P * 1.61803398875); + return normalize(cross(Pf * Pf * (Pf * (Pf * 30.0 - 60.0) + 30.0), Pf2 * Pf2 * (Pf2 * (Pf2 * 30.0 - 60.0) + 30.0))); +} + +float3 FlowBasis3D(float3 P) +{ + #if FLOW_VALUE3D + return Value3D_Deriv(P); + #else + return Tri3D(P); + #endif +} + float parabola( float x, float k ) { return pow( 4.0*x*(1.0-x), k ); @@ -132,7 +149,7 @@ float3 gravNormal (float2 grad) float3 globalFlow(float3 pos) { - float3 flowSample = Tri3D( pos / _FlowScale - float3(0,_FlowScroll,0) ) * _FlowAmplitude; + float3 flowSample = FlowBasis3D( pos / _FlowScale - float3(0,_FlowScroll,0) ) * _FlowAmplitude; flowSample.y *= .5; return flowSample; } diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs index 75d709712..d39978379 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeAssets.cs @@ -240,6 +240,7 @@ namespace GameCult.Aetheria.State.Verse ["unity.volume.quality.high.keyword"] = "HIGH_QUALITY", ["unity.volume.quality.normal.keyword"] = "MEDIUM_QUALITY", ["unity.volume.feature.flow.global.keyword"] = "FLOW_GLOBAL", + ["unity.volume.feature.flow.value3d.keyword"] = "FLOW_VALUE3D", ["unity.volume.feature.noise.slope.keyword"] = "NOISE_SLOPE", ["unity.volume.texturePort.surfaceHeight"] = "_NebulaSurfaceHeight", ["unity.volume.texturePort.patchHeight"] = "_NebulaPatchHeight", @@ -325,6 +326,7 @@ namespace GameCult.Aetheria.State.Verse ["presentationRole"] = "environment.stardust.update", ["unity.particles.kernel.update"] = "UpdateParticles", ["unity.particles.feature.flow.global.keyword"] = "FLOW_GLOBAL", + ["unity.particles.feature.flow.value3d.keyword"] = "FLOW_VALUE3D", ["unity.particles.feature.noise.slope.keyword"] = "NOISE_SLOPE", ["unity.particles.texturePort.surfaceHeight"] = "_NebulaSurfaceHeight", ["unity.particles.texturePort.tint"] = "_NebulaTint", diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs index b7d02ccb8..1cf46f296 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonGameSurfaceBuilder.cs @@ -910,8 +910,12 @@ namespace GameCult.Aetheria.State.Verse var aimPresentation = AimPresentation(run, zone, playerEntityId); if (aimPresentation != null) presentationChildren.Insert(0, aimPresentation); - presentationChildren.Insert(0, GravityFogVolume("aetheria.daemon.game.world.gravity-fog")); - presentationChildren.Insert(1, StardustParticles("aetheria.daemon.game.world.stardust")); + presentationChildren.Insert(0, GravityFogVolume( + "aetheria.daemon.game.world.gravity-fog", + frame.RenderSettings.UseValue3DFlow)); + presentationChildren.Insert(1, StardustParticles( + "aetheria.daemon.game.world.stardust", + frame.RenderSettings.UseValue3DFlow)); return new AetheriaRuntimeSurfaceComponent( id, @@ -934,7 +938,7 @@ namespace GameCult.Aetheria.State.Verse }); } - private static AetheriaRuntimeSurfaceComponent GravityFogVolume(string id) + private static AetheriaRuntimeSurfaceComponent GravityFogVolume(string id, bool useValue3DFlow) { var viewport = DefaultViewport(); var props = new Dictionary(StringComparer.Ordinal) @@ -946,7 +950,9 @@ namespace GameCult.Aetheria.State.Verse ["renderChannel"] = "world.transparent", ["compositeMode"] = "premultiplied-alpha", ["quality"] = "high", - ["features"] = "flow.global;noise.slope", + ["features"] = useValue3DFlow + ? "flow.global;flow.value3d;noise.slope" + : "flow.global;noise.slope", ["textureWidth"] = "1024", ["textureHeight"] = "1024", ["downsample"] = "0", @@ -979,7 +985,7 @@ namespace GameCult.Aetheria.State.Verse AetheriaRuntimeSurfaceStateBindings.FromProps(props)); } - private static AetheriaRuntimeSurfaceComponent StardustParticles(string id) + private static AetheriaRuntimeSurfaceComponent StardustParticles(string id, bool useValue3DFlow) { var viewport = DefaultViewport(); var props = new Dictionary(StringComparer.Ordinal) @@ -990,7 +996,9 @@ namespace GameCult.Aetheria.State.Verse ["computeProgramAssetRef"] = "compute.environment.stardust", ["materialAssetRef"] = "material.environment.stardust", ["renderChannel"] = "world.transparent", - ["features"] = "flow.global;noise.slope", + ["features"] = useValue3DFlow + ? "flow.global;flow.value3d;noise.slope" + : "flow.global;noise.slope", ["span"] = "256", ["threadGroupSize"] = "128", ["particleStrideBytes"] = "28", diff --git a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs index de047055e..0d9f06a4f 100644 --- a/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs +++ b/Packages/org.gamecult.aetheria.state/Runtime/AetheriaRuntimeDaemonRenderQueries.cs @@ -94,7 +94,8 @@ namespace GameCult.Aetheria.State.Verse asteroidMeshCount: 4, bodyRadiusCurve: new AetheriaRuntimeExponentialCurve(0.25, 3.0, 0.0), lightRadiusCurve: new AetheriaRuntimeExponentialCurve(0.25, 300.0, 0.0), - gravityWaveFrequencyCurve: new AetheriaRuntimeExponentialCurve(0.45, 0.2, 0.0)); + gravityWaveFrequencyCurve: new AetheriaRuntimeExponentialCurve(0.45, 0.2, 0.0), + useValue3DFlow: true); public AetheriaRuntimeDaemonRenderSettings( AetheriaRuntimeExponentialCurve temperatureEmissionCurve, @@ -125,7 +126,8 @@ namespace GameCult.Aetheria.State.Verse int asteroidMeshCount = int.MaxValue, AetheriaRuntimeExponentialCurve bodyRadiusCurve = default, AetheriaRuntimeExponentialCurve lightRadiusCurve = default, - AetheriaRuntimeExponentialCurve gravityWaveFrequencyCurve = default) + AetheriaRuntimeExponentialCurve gravityWaveFrequencyCurve = default, + bool useValue3DFlow = false) { TemperatureEmissionCurve = temperatureEmissionCurve; LockIndicatorFrequency = lockIndicatorFrequency; @@ -156,6 +158,7 @@ namespace GameCult.Aetheria.State.Verse BodyRadiusCurve = bodyRadiusCurve; LightRadiusCurve = lightRadiusCurve; GravityWaveFrequencyCurve = gravityWaveFrequencyCurve; + UseValue3DFlow = useValue3DFlow; } public AetheriaRuntimeExponentialCurve TemperatureEmissionCurve { get; } @@ -187,6 +190,7 @@ namespace GameCult.Aetheria.State.Verse public AetheriaRuntimeExponentialCurve BodyRadiusCurve { get; } public AetheriaRuntimeExponentialCurve LightRadiusCurve { get; } public AetheriaRuntimeExponentialCurve GravityWaveFrequencyCurve { get; } + public bool UseValue3DFlow { get; } public int ResolveDefaultMinimapZoomIndex() {