From c3329cecc40218db3e071e3d7f93099129f93ccc Mon Sep 17 00:00:00 2001 From: Reinier Vens Date: Sun, 17 Jul 2022 05:27:50 +0100 Subject: [PATCH] Unified Volumetric Flow Modified stardust & nebula shaders such that flow velocity is consistent: Volumetric:flow now represents global velocity in meters per second --- Assets/Materials/UI/Map Gravity Lines.mat | 2 +- Assets/Resources/Settings.asset | 4 +- Assets/Scenes/ARPG.unity | 2 +- Assets/Scenes/Main Menu.unity | 4 +- Assets/Scripts/ServerShared/Environment.cs | 2 +- .../Zone Display/VolumeCloudRenderer.cs | 2 +- Assets/Scripts/Zone Display/VolumeSampling.cs | 2 +- .../Shaders/Compute/Stardust/Stardust.compute | 19 +- Assets/Shaders/Compute/Stardust/Stardust.cs | 31 +-- Assets/Shaders/StableFluids.meta | 8 + Assets/Shaders/StableFluids/Fluid.compute | 156 +++++++++++++ .../Shaders/StableFluids/Fluid.compute.meta | 8 + Assets/Shaders/StableFluids/Fluid.cs | 215 ++++++++++++++++++ Assets/Shaders/StableFluids/Fluid.cs.meta | 13 ++ Assets/Shaders/StableFluids/Fluid.shader | 80 +++++++ Assets/Shaders/StableFluids/Fluid.shader.meta | 9 + Assets/Shaders/Volumetric.cginc | 31 +-- GameData/PlayerSettings.msgpack | 4 +- UserSettings/EditorUserSettings.asset | 2 +- 19 files changed, 527 insertions(+), 67 deletions(-) create mode 100644 Assets/Shaders/StableFluids.meta create mode 100644 Assets/Shaders/StableFluids/Fluid.compute create mode 100644 Assets/Shaders/StableFluids/Fluid.compute.meta create mode 100644 Assets/Shaders/StableFluids/Fluid.cs create mode 100644 Assets/Shaders/StableFluids/Fluid.cs.meta create mode 100644 Assets/Shaders/StableFluids/Fluid.shader create mode 100644 Assets/Shaders/StableFluids/Fluid.shader.meta diff --git a/Assets/Materials/UI/Map Gravity Lines.mat b/Assets/Materials/UI/Map Gravity Lines.mat index e9e86e3a2..749d876be 100644 --- a/Assets/Materials/UI/Map Gravity Lines.mat +++ b/Assets/Materials/UI/Map Gravity Lines.mat @@ -86,7 +86,7 @@ Material: - _Mode: 0 - _OcclusionStrength: 1 - _Parallax: 0.02 - - _Scale: 4.5182247 + - _Scale: 0.5 - _SmoothnessTextureChannel: 0 - _SpecularHighlights: 1 - _SrcBlend: 1 diff --git a/Assets/Resources/Settings.asset b/Assets/Resources/Settings.asset index 55c405b38..33adefe4c 100644 --- a/Assets/Resources/Settings.asset +++ b/Assets/Resources/Settings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b3f7911e94bcd25ea0278f9dba69b2d106c9612023a509baf524e75272957186 -size 13221 +oid sha256:39962ebfad4d9f2259d1d9ccf169a391b38c4c5af3aa9eb2b7f4304ba484cf09 +size 13220 diff --git a/Assets/Scenes/ARPG.unity b/Assets/Scenes/ARPG.unity index 1dbf21b3b..6ec152ad0 100644 --- a/Assets/Scenes/ARPG.unity +++ b/Assets/Scenes/ARPG.unity @@ -16182,7 +16182,7 @@ MonoBehaviour: ParticleCalculation: {fileID: 7200000, guid: f53a4ba56f0db2a49ad9f68150bf298a, type: 3} ParticleMaterial: {fileID: 2100000, guid: fc00c35c3c370774c88d55240b6538ce, type: 2} Span: 256 - Speed: -0.5 + Period: 2 MinimumSize: 0 MaximumSize: 0.75 Spacing: 6 diff --git a/Assets/Scenes/Main Menu.unity b/Assets/Scenes/Main Menu.unity index 5a7d896f5..342eee19a 100644 --- a/Assets/Scenes/Main Menu.unity +++ b/Assets/Scenes/Main Menu.unity @@ -860,9 +860,9 @@ MonoBehaviour: ParticleCalculation: {fileID: 7200000, guid: f53a4ba56f0db2a49ad9f68150bf298a, type: 3} ParticleMaterial: {fileID: 2100000, guid: fc00c35c3c370774c88d55240b6538ce, type: 2} Span: 128 - Speed: -0.05 + Period: 2 MinimumSize: 0 - MaximumSize: 0.5 + MaximumSize: 0.75 Spacing: 6 Ceiling: -0.1 Floor: -30 diff --git a/Assets/Scripts/ServerShared/Environment.cs b/Assets/Scripts/ServerShared/Environment.cs index 20ddc0f8e..d1b3514af 100644 --- a/Assets/Scripts/ServerShared/Environment.cs +++ b/Assets/Scripts/ServerShared/Environment.cs @@ -43,7 +43,7 @@ public class FlowSettings [JsonProperty("scale"), Key(0)] public float Scale; [JsonProperty("amplitude"), Key(1)] public float Amplitude; [JsonProperty("scrollSpeed"), Key(2)] public float ScrollSpeed; - [JsonProperty("velocity"), Key(3)] public float Speed; + [JsonProperty("period"), Key(3)] public float Period; } [Serializable, MessagePackObject, JsonObject(MemberSerialization.OptIn)] diff --git a/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs b/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs index d7434c113..e601b1feb 100644 --- a/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs +++ b/Assets/Scripts/Zone Display/VolumeCloudRenderer.cs @@ -110,7 +110,7 @@ public class VolumeCloudRenderer : EffectBase /* Some code is from playdead TAA. */ - //1. Pass1, Render a undersampled buffer. The buffer is dithered using bayer matrix(every 3x3 pixel) and halton sequence. + //1. Pass1, Render a undersampled buffer. The buffer is dithered using blue noise and halton sequence. //If it's first frame, force a high quality sample to make the initial history buffer good enough. if (firstFrame || quality == Quality.Ultra) { mat.EnableKeyword("ULTRA_QUALITY"); diff --git a/Assets/Scripts/Zone Display/VolumeSampling.cs b/Assets/Scripts/Zone Display/VolumeSampling.cs index a5efa036a..19d10e0cb 100644 --- a/Assets/Scripts/Zone Display/VolumeSampling.cs +++ b/Assets/Scripts/Zone Display/VolumeSampling.cs @@ -67,6 +67,6 @@ public class VolumeSampling : MonoBehaviour Shader.SetGlobalFloat("_FlowAmplitude", Settings.DefaultEnvironment.Flow.Amplitude); _flowScroll += Settings.DefaultEnvironment.Flow.ScrollSpeed * Time.deltaTime; Shader.SetGlobalFloat("_FlowScroll", _flowScroll); - Shader.SetGlobalFloat("_FlowSpeed", Settings.DefaultEnvironment.Flow.Speed); + Shader.SetGlobalFloat("_FlowPeriod", Settings.DefaultEnvironment.Flow.Period); } } diff --git a/Assets/Shaders/Compute/Stardust/Stardust.compute b/Assets/Shaders/Compute/Stardust/Stardust.compute index 1330115b9..fcce90171 100644 --- a/Assets/Shaders/Compute/Stardust/Stardust.compute +++ b/Assets/Shaders/Compute/Stardust/Stardust.compute @@ -13,16 +13,11 @@ struct Particle RWStructuredBuffer particles; -// Use this texture to spawn particles over terrain -Texture2D Heightmap; -SamplerState samplerHeightmap; - // Use this texture to choose the color of the particle Texture2D HueTexture; -SamplerState samplerHueTexture; - -//uniform float4x4 noiseOffsetMatrix; +SamplerState samplerHueTexture; +float period; float time; float spacing; float ceilingHeight; @@ -121,20 +116,20 @@ void UpdateParticles(uint3 id : SV_DispatchThreadID, int3 groupID : SV_GroupThre uint xp = x + xt + offset; uint yp = y + yt + offset; - const float lifetime = frac(time+RandomFirst(xp, yp)); + const float lifetime = frac(time / period + RandomFirst(xp, yp)); particles[id.x].position = float3( x * spacing + xt * spacing + perlinNoise1D(RandomValue()*1024)*spacing, 0, y * spacing + yt * spacing + perlinNoise1D(RandomValue()*1024)*spacing); - const float2 puv = float2(.5,.5) - (particles[id.x].position.xz - _GridTransform.xy) / _GridTransform.z; - const float gravHeight = -Heightmap.SampleLevel(samplerHeightmap, puv, 0.0f).r + _NebulaFloorOffset; + const float2 puv = getUV(particles[id.x].position.xz); + const float gravHeight = -tex2Dlod(_NebulaSurfaceHeight, float4(puv, 0, 0)).x + _NebulaFloorOffset; const float headroom = ceilingHeight - gravHeight; particles[id.x].position.y = gravHeight + floorHeight + headroom * pow(RandomValue(), max(1, heightExponent/(headroom*headroom))) + perlinNoise1D(RandomValue()*1024)*spacing/2; float noise; - const float3 flowDir = flow(particles[id.x].position, noise); - particles[id.x].position += flowDir * (lifetime * noise); + const float3 flowVec = flow(particles[id.x].position, noise); + particles[id.x].position -= flowVec * lifetime * period; const float fade = smoothstep(0,ceilingHeight, particles[id.x].position.y); const float d = pow(density(particles[id.x].position) + .01, 0.1); particles[id.x].size = lerp(minimumSize, maximumSize, saturate(RandomValue()*fade)) * parabola(lifetime, 2); diff --git a/Assets/Shaders/Compute/Stardust/Stardust.cs b/Assets/Shaders/Compute/Stardust/Stardust.cs index 991bc7ce2..8904b757a 100644 --- a/Assets/Shaders/Compute/Stardust/Stardust.cs +++ b/Assets/Shaders/Compute/Stardust/Stardust.cs @@ -15,7 +15,7 @@ public class Stardust : MonoBehaviour public ComputeShader ParticleCalculation; public Material ParticleMaterial; public int Span = 512; - public float Speed = .25f; + public float Period = .25f; public float MinimumSize = .1f; public float MaximumSize = 2; @@ -99,12 +99,11 @@ public class Stardust : MonoBehaviour { //Bind resources to compute shader ParticleCalculation.SetBuffer(_updateParticlesKernel, "particles", _particlesBuffer); -// ParticleCalculation.SetBuffer(_updateParticlesKernel, "temperature", _temperature); - ParticleCalculation.SetFloat("time", Time.time * Speed); + ParticleCalculation.SetFloat("time", Time.time); var pos = GravityCamera.transform.position; - ParticleCalculation.SetVector("_GridTransform", new Vector4(pos.x,pos.z,GravityCamera.orthographicSize*2)); + ParticleCalculation.SetFloat("period", Period); ParticleCalculation.SetFloat("spacing", Spacing); ParticleCalculation.SetFloat("ceilingHeight", Ceiling); ParticleCalculation.SetFloat("floorHeight", Floor); @@ -113,31 +112,7 @@ public class Stardust : MonoBehaviour ParticleCalculation.SetFloat("minimumSize", MinimumSize); ParticleCalculation.SetInt("span", Span); - ParticleCalculation.SetTexture(_updateParticlesKernel, "Heightmap", Heightmap); ParticleCalculation.SetTexture(_updateParticlesKernel, "HueTexture", ParticleColors); - - ParticleCalculation.SetTexture(_updateParticlesKernel, "_NebulaSurfaceHeight", NebulaSurfaceHeight); - ParticleCalculation.SetTexture(_updateParticlesKernel, "_NebulaPatchHeight", NebulaPatchHeight); - ParticleCalculation.SetTexture(_updateParticlesKernel, "_NebulaPatch", NebulaPatch); - ParticleCalculation.SetTexture(_updateParticlesKernel, "_NebulaTint", NebulaTint); - - ParticleCalculation.SetFloat("_NebulaFillDensity", Settings.DefaultEnvironment.Nebula.FillDensity); - ParticleCalculation.SetFloat("_SafetyDistance", Settings.DefaultEnvironment.Nebula.SafetyDistance); - ParticleCalculation.SetFloat("_NebulaFloorDensity", Settings.DefaultEnvironment.Nebula.FloorDensity); - ParticleCalculation.SetFloat("_NebulaPatchDensity", Settings.DefaultEnvironment.Nebula.PatchDensity); - ParticleCalculation.SetFloat("_NebulaFloorOffset", Settings.DefaultEnvironment.Nebula.FloorOffset); - ParticleCalculation.SetFloat("_NebulaFloorBlend", Settings.DefaultEnvironment.Nebula.FloorBlend); - ParticleCalculation.SetFloat("_NebulaPatchBlend", Settings.DefaultEnvironment.Nebula.PatchBlend); - // Shader.SetGlobalFloat("_TintExponent", Settings.DefaultEnvironment.); - ParticleCalculation.SetFloat("_NoiseScale", Settings.DefaultEnvironment.Noise.Scale); - ParticleCalculation.SetFloat("_NoiseExponent", Settings.DefaultEnvironment.Noise.Exponent); - ParticleCalculation.SetFloat("_NoiseAmplitude", Settings.DefaultEnvironment.Noise.Amplitude); - ParticleCalculation.SetFloat("_NoiseSpeed", Settings.DefaultEnvironment.Noise.Speed); - ParticleCalculation.SetFloat("_FlowScale", Settings.DefaultEnvironment.Flow.Scale); - ParticleCalculation.SetFloat("_FlowAmplitude", Settings.DefaultEnvironment.Flow.Amplitude); - _flowScroll += Settings.DefaultEnvironment.Flow.ScrollSpeed * Time.deltaTime; - ParticleCalculation.SetFloat("_FlowScroll", _flowScroll); - ParticleCalculation.SetFloat("_FlowSpeed", Settings.DefaultEnvironment.Flow.Speed); //Dispatch, launch threads on GPU int numberOfGroups = Mathf.CeilToInt((float)Span * Span / GROUP_SIZE); diff --git a/Assets/Shaders/StableFluids.meta b/Assets/Shaders/StableFluids.meta new file mode 100644 index 000000000..450d38019 --- /dev/null +++ b/Assets/Shaders/StableFluids.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: 992fcf67b7a8501419a78ce324844bce +folderAsset: yes +DefaultImporter: + externalObjects: {} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/StableFluids/Fluid.compute b/Assets/Shaders/StableFluids/Fluid.compute new file mode 100644 index 000000000..e06e49cf4 --- /dev/null +++ b/Assets/Shaders/StableFluids/Fluid.compute @@ -0,0 +1,156 @@ +// Based on https://github.com/keijiro/StableFluids + +#include "Assets/Shaders/Volumetric.cginc" + +#pragma kernel Advect +#pragma kernel Force +#pragma kernel PSetup +#pragma kernel PFinish +#pragma kernel Jacobi1 +#pragma kernel Jacobi2 +#pragma kernel Adjust + +// Common parameter +float Time; +float DeltaTime; + +// Adjustment +int2 Delta; +float Fade; + +// External force +float2 ForceOrigin; +float2 ForceVector; +float ForceExponent; + +// U (velocity field) +Texture2D U_in; +SamplerState samplerU_in; +RWTexture2D U_out; + +// W (velocity field; working) +Texture2D W_in; +RWTexture2D W_out; + +// Div W +RWTexture2D DivW_out; + +// P (pressure field) +Texture2D P_in; +RWTexture2D P_out; + +// Color map +Texture2D C_in; +SamplerState samplerC_in; +RWTexture2D C_out; + +// Jacobi method arguments +float Alpha, Beta; + +Texture2D X1_in; +Texture2D B1_in; +RWTexture2D X1_out; + +Texture2D X2_in; +Texture2D B2_in; +RWTexture2D X2_out; + +// Adjust step (translate & fade towards global velocity field) +[numthreads(8, 8, 1)] +void Adjust(uint2 tid : SV_DispatchThreadID) +{ + uint2 dim; + W_out.GetDimensions(dim.x, dim.y); + + float2 uv = (tid + 0.5) / dim; + float2 globalUV = (uv - .5) * _FluidTransform.z + _FluidTransform.xy; + float2 gravUV = getUV(globalUV); + float3 globalPos = float3(globalUV.x, -tex2Dlod(_NebulaSurfaceHeight, float4(gravUV, 0, 0)).x, globalUV.x); + float noise; + float3 f = flow(globalPos, noise); + + float2 prevUV = uv - Delta; + + if(any(prevUV<0)||any(prevUV>1)) + W_out[tid] = f.xz; + else W_out[tid] = lerp(U_in.SampleLevel(samplerU_in, prevUV, 0), f.xz, Fade); +} + +// Advect step +[numthreads(8, 8, 1)] +void Advect(uint2 tid : SV_DispatchThreadID) +{ + uint2 dim; + W_out.GetDimensions(dim.x, dim.y); + + float2 uv = (tid + 0.5) / dim; + float2 duv = U_in[tid] * float2((float)dim.y / dim.x, 1) * DeltaTime; + + W_out[tid] = U_in.SampleLevel(samplerU_in, uv - duv, 0); +} + +// Add-force step +[numthreads(8, 8, 1)] +void Force(uint2 tid : SV_DispatchThreadID) +{ + uint2 dim; + W_out.GetDimensions(dim.x, dim.y); + + float2 pos = (tid + 0.5) / dim.y; + float amp = exp(-ForceExponent * distance(ForceOrigin, pos)); + + W_out[tid] = W_in[tid] + ForceVector * amp; +} + +// Setup for Project step (divW calculation) +[numthreads(8, 8, 1)] +void PSetup(uint2 tid : SV_DispatchThreadID) +{ + uint2 dim; + W_in.GetDimensions(dim.x, dim.y); + + DivW_out[tid] = (W_in[tid + int2(1, 0)].x - W_in[tid - int2(1, 0)].x + + W_in[tid + int2(0, 1)].y - W_in[tid - int2(0, 1)].y) * dim.y / 2; + + P_out[tid] = 0; +} + +// Finishing for Project step (divergence free field calculation) +[numthreads(8, 8, 1)] +void PFinish(uint2 tid : SV_DispatchThreadID) +{ + uint2 dim; + W_in.GetDimensions(dim.x, dim.y); + + if (any(tid == 0) || any(tid == dim - 1)) return; + + float P1 = P_in[max(tid - int2(1, 0), 1)]; + float P2 = P_in[min(tid + int2(1, 0), dim - 2)]; + float P3 = P_in[max(tid - int2(0, 1), 1)]; + float P4 = P_in[min(tid + int2(0, 1), dim - 2)]; + + float2 u = W_in[tid] - float2(P2 - P1, P4 - P3) * dim.y / 2; + + U_out[tid] = u; + + if (tid.x == 1) U_out[int2(0, tid.y)] = -u; + if (tid.y == 1) U_out[int2(tid.x, 0)] = -u; + if (tid.x == dim.x - 2) U_out[int2(dim.x - 1, tid.y)] = -u; + if (tid.y == dim.y - 2) U_out[int2(tid.x, dim.y - 1)] = -u; +} + +// Jacobi method with a scalar field +[numthreads(8, 8, 1)] +void Jacobi1(uint2 tid : SV_DispatchThreadID) +{ + X1_out[tid] = (X1_in[tid - int2(1, 0)] + X1_in[tid + int2(1, 0)] + + X1_in[tid - int2(0, 1)] + X1_in[tid + int2(0, 1)] + Alpha * B1_in[tid]) / Beta; +} + +// Jacobi method with a vector field +[numthreads(8, 8, 1)] +void Jacobi2(uint2 tid : SV_DispatchThreadID) +{ + X2_out[tid] = (X2_in[tid - int2(1, 0)] + X2_in[tid + int2(1, 0)] + + X2_in[tid - int2(0, 1)] + X2_in[tid + int2(0, 1)] + Alpha * B2_in[tid]) / Beta; +} diff --git a/Assets/Shaders/StableFluids/Fluid.compute.meta b/Assets/Shaders/StableFluids/Fluid.compute.meta new file mode 100644 index 000000000..c8cd7da31 --- /dev/null +++ b/Assets/Shaders/StableFluids/Fluid.compute.meta @@ -0,0 +1,8 @@ +fileFormatVersion: 2 +guid: cd1f70f1f6b4beb45a02e1c088f0a109 +ComputeShaderImporter: + externalObjects: {} + currentAPIMask: 4 + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/StableFluids/Fluid.cs b/Assets/Shaders/StableFluids/Fluid.cs new file mode 100644 index 000000000..0e8188f20 --- /dev/null +++ b/Assets/Shaders/StableFluids/Fluid.cs @@ -0,0 +1,215 @@ +// StableFluids - A GPU implementation of Jos Stam's Stable Fluids on Unity +// https://github.com/keijiro/StableFluids + +using System.Collections.Generic; +using Unity.Mathematics; +using UnityEngine; +using static Unity.Mathematics.math; +using int2 = Unity.Mathematics.int2; +using Random = UnityEngine.Random; + +namespace StableFluids +{ + public class Fluid : MonoBehaviour + { + #region Editable attributes + + [SerializeField] int _resolution = 512; + [SerializeField] float _viscosity = 1e-6f; + [SerializeField] float _range = 512; + [SerializeField] Transform _camera; + + #endregion + + #region Internal resources + + [SerializeField, HideInInspector] ComputeShader _compute; + [SerializeField, HideInInspector] Shader _shader; + + #endregion + + #region Private members + + Material _shaderSheet; + int2 _previousCameraIntPosition = int2.zero; + private List _forces = new List(); + + struct Force + { + public float2 Position; + public float2 Vector; + public float Exponent; + } + + static class Kernels + { + public const int Advect = 0; + public const int Force = 1; + public const int PSetup = 2; + public const int PFinish = 3; + public const int Jacobi1 = 4; + public const int Jacobi2 = 5; + } + + int ThreadCount { get { return (_resolution + 7) / 8; } } + + int Resolution { get { return ThreadCount * 8; } } + + // Vector field buffers + static class VFB + { + public static RenderTexture V1; + public static RenderTexture V2; + public static RenderTexture V3; + public static RenderTexture P1; + public static RenderTexture P2; + } + + RenderTexture AllocateBuffer(int componentCount) + { + var format = RenderTextureFormat.ARGBHalf; + if (componentCount == 1) format = RenderTextureFormat.RHalf; + if (componentCount == 2) format = RenderTextureFormat.RGHalf; + + var rt = new RenderTexture(Resolution, Resolution, 0, format); + rt.enableRandomWrite = true; + rt.Create(); + return rt; + } + + #endregion + + #region MonoBehaviour implementation + + public void AddForce(float2 position, float2 vector, float exponent) + { + _forces.Add(new Force { Position = position, Vector = vector, Exponent = exponent }); + } + + void OnValidate() + { + _resolution = Mathf.Max(_resolution, 8); + } + + void Start() + { + _shaderSheet = new Material(_shader); + + VFB.V1 = AllocateBuffer(2); + VFB.V2 = AllocateBuffer(2); + VFB.V3 = AllocateBuffer(2); + VFB.P1 = AllocateBuffer(1); + VFB.P2 = AllocateBuffer(1); + } + + void OnDestroy() + { + Destroy(_shaderSheet); + + Destroy(VFB.V1); + Destroy(VFB.V2); + Destroy(VFB.V3); + Destroy(VFB.P1); + Destroy(VFB.P2); + } + + void Update() + { + var dt = Time.deltaTime; + var dx = 1.0f / Resolution; + + var snapLength = _range / _resolution; + + // Input point + var cameraIntPosition = int2( + (int)(_camera.position.x / snapLength), + (int)(_camera.position.z / snapLength) + ); + var cameraPosition = float2( + cameraIntPosition.x * snapLength, + cameraIntPosition.y * snapLength + ); + Shader.SetGlobalVector("_FluidTransform", new Vector4(cameraPosition.x, cameraPosition.y, _range)); + + var delta = (float2)(cameraIntPosition - _previousCameraIntPosition) / Resolution; + + // Common variables + _compute.SetFloat("Time", Time.time); + _compute.SetFloat("DeltaTime", dt); + + // Advection + _compute.SetTexture(Kernels.Advect, "U_in", VFB.V1); + _compute.SetTexture(Kernels.Advect, "W_out", VFB.V2); + _compute.Dispatch(Kernels.Advect, ThreadCount, ThreadCount, 1); + + // Diffuse setup + var dif_alpha = dx * dx / (_viscosity * dt); + _compute.SetFloat("Alpha", dif_alpha); + _compute.SetFloat("Beta", 4 + dif_alpha); + Graphics.CopyTexture(VFB.V2, VFB.V1); + _compute.SetTexture(Kernels.Jacobi2, "B2_in", VFB.V1); + + // Jacobi iteration + for (var i = 0; i < 20; i++) + { + _compute.SetTexture(Kernels.Jacobi2, "X2_in", VFB.V2); + _compute.SetTexture(Kernels.Jacobi2, "X2_out", VFB.V3); + _compute.Dispatch(Kernels.Jacobi2, ThreadCount, ThreadCount, 1); + + _compute.SetTexture(Kernels.Jacobi2, "X2_in", VFB.V3); + _compute.SetTexture(Kernels.Jacobi2, "X2_out", VFB.V2); + _compute.Dispatch(Kernels.Jacobi2, ThreadCount, ThreadCount, 1); + } + + float2 getUV(float2 pos, float2 camPos, float scale) + { + return -(pos-camPos)/scale + float2(.5f,.5f); + } + + // Add external forces + foreach(var force in _forces) + { + var uv = getUV(force.Position, cameraIntPosition, _range); + _compute.SetVector("ForceOrigin", new Vector4(uv.x,uv.y)); + _compute.SetFloat("ForceExponent", force.Exponent); + _compute.SetTexture(Kernels.Force, "W_in", VFB.V2); + _compute.SetTexture(Kernels.Force, "W_out", VFB.V3); + _compute.SetVector("ForceVector", new Vector4(force.Vector.x,force.Vector.y)); + + _compute.Dispatch(Kernels.Force, ThreadCount, ThreadCount, 1); + } + + // Projection setup + _compute.SetTexture(Kernels.PSetup, "W_in", VFB.V3); + _compute.SetTexture(Kernels.PSetup, "DivW_out", VFB.V2); + _compute.SetTexture(Kernels.PSetup, "P_out", VFB.P1); + _compute.Dispatch(Kernels.PSetup, ThreadCount, ThreadCount, 1); + + // Jacobi iteration + _compute.SetFloat("Alpha", -dx * dx); + _compute.SetFloat("Beta", 4); + _compute.SetTexture(Kernels.Jacobi1, "B1_in", VFB.V2); + + for (var i = 0; i < 20; i++) + { + _compute.SetTexture(Kernels.Jacobi1, "X1_in", VFB.P1); + _compute.SetTexture(Kernels.Jacobi1, "X1_out", VFB.P2); + _compute.Dispatch(Kernels.Jacobi1, ThreadCount, ThreadCount, 1); + + _compute.SetTexture(Kernels.Jacobi1, "X1_in", VFB.P2); + _compute.SetTexture(Kernels.Jacobi1, "X1_out", VFB.P1); + _compute.Dispatch(Kernels.Jacobi1, ThreadCount, ThreadCount, 1); + } + + // Projection finish + _compute.SetTexture(Kernels.PFinish, "W_in", VFB.V3); + _compute.SetTexture(Kernels.PFinish, "P_in", VFB.P1); + _compute.SetTexture(Kernels.PFinish, "U_out", VFB.V1); + _compute.Dispatch(Kernels.PFinish, ThreadCount, ThreadCount, 1); + + _previousCameraIntPosition = cameraIntPosition; + } + + #endregion + } +} diff --git a/Assets/Shaders/StableFluids/Fluid.cs.meta b/Assets/Shaders/StableFluids/Fluid.cs.meta new file mode 100644 index 000000000..df1d18d24 --- /dev/null +++ b/Assets/Shaders/StableFluids/Fluid.cs.meta @@ -0,0 +1,13 @@ +fileFormatVersion: 2 +guid: 68b35f6f5dea6274db0eda640e08abc2 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: + - _compute: {fileID: 7200000, guid: cd1f70f1f6b4beb45a02e1c088f0a109, type: 3} + - _shader: {fileID: 4800000, guid: 0486f5f9fa757fa49a5b9613050036fb, type: 3} + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/StableFluids/Fluid.shader b/Assets/Shaders/StableFluids/Fluid.shader new file mode 100644 index 000000000..fb5a71407 --- /dev/null +++ b/Assets/Shaders/StableFluids/Fluid.shader @@ -0,0 +1,80 @@ +// StableFluids - A GPU implementation of Jos Stam's Stable Fluids on Unity +// https://github.com/keijiro/StableFluids + +Shader "Hidden/StableFluids" +{ + Properties + { + _MainTex("", 2D) = "" + _VelocityField("", 2D) = "" + } + + CGINCLUDE + + #include "UnityCG.cginc" + + sampler2D _MainTex; + float4 _MainTex_TexelSize; + + sampler2D _VelocityField; + + float2 _ForceOrigin; + float _ForceExponent; + + half4 frag_advect(v2f_img i) : SV_Target + { + // Time parameters + float time = _Time.y; + float deltaTime = unity_DeltaTime.x; + + // Aspect ratio coefficients + float2 aspect = float2(_MainTex_TexelSize.y * _MainTex_TexelSize.z, 1); + float2 aspect_inv = float2(_MainTex_TexelSize.x * _MainTex_TexelSize.w, 1); + + // Color advection with the velocity field + float2 delta = tex2D(_VelocityField, i.uv).xy * aspect_inv * deltaTime; + float3 color = tex2D(_MainTex, i.uv - delta).xyz; + + // Dye (injection color) + float3 dye = saturate(sin(time * float3(2.72, 5.12, 4.98)) + 0.5); + + // Blend dye with the color from the buffer. + float2 pos = (i.uv - 0.5) * aspect; + float amp = exp(-_ForceExponent * distance(_ForceOrigin, pos)); + color = lerp(color, dye, saturate(amp * 100)); + + return half4(color, 1); + } + + half4 frag_render(v2f_img i) : SV_Target + { + half3 rgb = tex2D(_MainTex, i.uv).rgb; + + // Mixing channels up to get slowly changing false colors + //rgb = sin(float3(3.43, 4.43, 3.84) * rgb + + // float3(0.12, 0.23, 0.44) * _Time.y) * 0.5 + 0.5; + + return half4(GammaToLinearSpace(rgb), 1); + } + + ENDCG + + SubShader + { + Cull Off ZWrite Off ZTest Always + Pass + { + CGPROGRAM + #pragma vertex vert_img + #pragma fragment frag_advect + ENDCG + } + Pass + { + CGPROGRAM + #pragma vertex vert_img + #pragma fragment frag_render + ENDCG + } + } +} diff --git a/Assets/Shaders/StableFluids/Fluid.shader.meta b/Assets/Shaders/StableFluids/Fluid.shader.meta new file mode 100644 index 000000000..e30208044 --- /dev/null +++ b/Assets/Shaders/StableFluids/Fluid.shader.meta @@ -0,0 +1,9 @@ +fileFormatVersion: 2 +guid: 0486f5f9fa757fa49a5b9613050036fb +ShaderImporter: + externalObjects: {} + defaultTextures: [] + nonModifiableTextures: [] + userData: + assetBundleName: + assetBundleVariant: diff --git a/Assets/Shaders/Volumetric.cginc b/Assets/Shaders/Volumetric.cginc index e0c8b839e..23dbd940a 100644 --- a/Assets/Shaders/Volumetric.cginc +++ b/Assets/Shaders/Volumetric.cginc @@ -44,6 +44,7 @@ uniform sampler2D _NebulaTint; float4 _NebulaTint_TexelSize; uniform float3 _GridTransform; +uniform float3 _FluidTransform; uniform half _NebulaFillDensity, _NebulaFillDistance, @@ -64,7 +65,7 @@ uniform half _NebulaFillDensity, _FlowScale, _FlowAmplitude, _FlowScroll, - _FlowSpeed, + _FlowPeriod, _SafetyDistance, _DynamicLodRange, _DynamicLodBias, @@ -99,14 +100,14 @@ float parabola( float x, float k ) return pow( 4.0*x*(1.0-x), k ); } -float2 getUV(float3 pos) +float2 getUV(float2 pos) { - return -(pos.xz-_GridTransform.xy)/_GridTransform.z + float2(.5,.5); + return -(pos-_GridTransform.xy)/_GridTransform.z + float2(.5,.5); } float cloudDensity(float3 pos, float surfaceDisp) { - float2 uv = getUV(pos); + float2 uv = getUV(pos.xz); const float dist = pos.y + surfaceDisp - _NebulaFloorOffset; const float patch = tex2Dlod(_NebulaPatch, half4(uv, 0, 0)).r; const float patchDisp = tex2Dlod(_NebulaPatchHeight, half4(uv, 0, 0)).r; @@ -129,7 +130,7 @@ float tri2(in float x){return 1-2*abs(frac(x)-.5);} float density(float3 pos) { - float2 uv = getUV(pos); + float2 uv = getUV(pos.xz); const float surfaceDisp = tex2Dlod(_NebulaSurfaceHeight, half4(uv, 0, 0)).r; const float dist = pos.y + surfaceDisp; float d = pow(abs(dist+_NebulaFillOffset)/_NebulaFillDistance,-_NebulaFillExponent) * _NebulaFillDensity; @@ -139,15 +140,15 @@ float density(float3 pos) const float heightFade = smoothstep(_SafetyDistance,_SafetyDistance*.75,dist); float noise; const float3 fl = flow(pos, noise); - const float lerp1 = frac(_Time.x * _FlowSpeed + noise); - const float lerp2 = frac(_Time.x * _FlowSpeed + .5 + noise); - const float noise1 = pow(triNoise3d((pos+fl * lerp1) / _NebulaNoiseScale),_NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp1); - const float noise2 = pow(triNoise3d((pos+fl * lerp2) / _NebulaNoiseScale),_NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp2); + const float lerp1 = frac(_Time.y / _FlowPeriod + noise); + const float lerp2 = frac(_Time.y / _FlowPeriod + .5 + noise); + const float noise1 = pow(triNoise3d((pos+fl * lerp1 * _FlowPeriod) / _NebulaNoiseScale),_NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp1); + const float noise2 = pow(triNoise3d((pos+fl * lerp2 * _FlowPeriod) / _NebulaNoiseScale),_NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp2); pos.y += (noise1 + noise2) * heightFade; - const float lerp3 = frac(_Time.x * _FlowSpeed * 2); - const float lerp4 = frac(_Time.x * _FlowSpeed * 2 + .5); - const float noise3 = pow(triNoise3d((pos+fl * lerp3) / _NebulaNoiseScale * 8), _NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp3) / 2; - const float noise4 = pow(triNoise3d((pos+fl * lerp4) / _NebulaNoiseScale * 8), _NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp4) / 2; + const float lerp3 = frac(_Time.y / _FlowPeriod); + const float lerp4 = frac(_Time.y / _FlowPeriod + .5); + const float noise3 = pow(triNoise3d((pos+fl * lerp3 * _FlowPeriod) / _NebulaNoiseScale * 8), _NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp3) / 2; + const float noise4 = pow(triNoise3d((pos+fl * lerp4 * _FlowPeriod) / _NebulaNoiseScale * 8), _NebulaNoiseExponent) * _NebulaNoiseAmplitude * tri2(lerp4) / 2; pos.y -= (noise3 + noise4) * heightFade; d += cloudDensity(pos, surfaceDisp); } @@ -157,7 +158,7 @@ float density(float3 pos) float4 VolumeSampleColor(float3 pos) { float d = density(pos); - float2 uv = getUV(pos); + float2 uv = getUV(pos.xz); float3 tint = tex2Dlod(_NebulaTint, float4(uv.x, uv.y, 0, pow(max(.01,d), _TintLodExponent))); const float albedo = smoothstep(0,-250,pos.y) * pow(max(.1,d), _TintExponent) * _NebulaLuminance; return float4(albedo*tint, d); @@ -173,7 +174,7 @@ float2 tintGradient (float2 uv) float3 VolumeSampleColorSimple(float3 pos, float3 normal) { - float2 uv = getUV(pos); + float2 uv = getUV(pos.xz); float3 low = tex2Dlod(_NebulaTint, float4(uv.x, uv.y, 0, _DynamicLodBias)).rgb; float3 high = tex2Dlod(_NebulaTint, float4(uv.x, uv.y, 0, _DynamicLodBias + _DynamicLodRange)).rgb * _DynamicSkyBoost; float upness = dot(normal, float3(0,1,0)); diff --git a/GameData/PlayerSettings.msgpack b/GameData/PlayerSettings.msgpack index df9970d13..450573bf6 100644 --- a/GameData/PlayerSettings.msgpack +++ b/GameData/PlayerSettings.msgpack @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8dda3e784f2710e3701d28ccde3aa4eb49dd00c531dd2611f27f43217063203c -size 55896 +oid sha256:40f10d97ff974b6f1aafbdd22b769a4d0c6e6b6a111a062dfc2a5a27a6cfd6f1 +size 25577 diff --git a/UserSettings/EditorUserSettings.asset b/UserSettings/EditorUserSettings.asset index fb93f74d9..f33a7228f 100644 --- a/UserSettings/EditorUserSettings.asset +++ b/UserSettings/EditorUserSettings.asset @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:642cf805309acafa4b513535bd41e7af6a774bd8002dd6096c9a774a1c66369d +oid sha256:447234e8c4d8ad0cbbdc75de56e2d314a113b5969f4fe0e37f1012457f829739 size 1687