Unified Volumetric Flow
Modified stardust & nebula shaders such that flow velocity is consistent: Volumetric:flow now represents global velocity in meters per second
This commit is contained in:
parent
424010584c
commit
c3329cecc4
19 changed files with 527 additions and 67 deletions
|
|
@ -86,7 +86,7 @@ Material:
|
|||
- _Mode: 0
|
||||
- _OcclusionStrength: 1
|
||||
- _Parallax: 0.02
|
||||
- _Scale: 4.5182247
|
||||
- _Scale: 0.5
|
||||
- _SmoothnessTextureChannel: 0
|
||||
- _SpecularHighlights: 1
|
||||
- _SrcBlend: 1
|
||||
|
|
|
|||
BIN
Assets/Resources/Settings.asset
(Stored with Git LFS)
BIN
Assets/Resources/Settings.asset
(Stored with Git LFS)
Binary file not shown.
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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)]
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -13,16 +13,11 @@ struct Particle
|
|||
|
||||
RWStructuredBuffer<Particle> 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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
8
Assets/Shaders/StableFluids.meta
Normal file
8
Assets/Shaders/StableFluids.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 992fcf67b7a8501419a78ce324844bce
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
156
Assets/Shaders/StableFluids/Fluid.compute
Normal file
156
Assets/Shaders/StableFluids/Fluid.compute
Normal file
|
|
@ -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<float2> U_in;
|
||||
SamplerState samplerU_in;
|
||||
RWTexture2D<float2> U_out;
|
||||
|
||||
// W (velocity field; working)
|
||||
Texture2D<float2> W_in;
|
||||
RWTexture2D<float2> W_out;
|
||||
|
||||
// Div W
|
||||
RWTexture2D<float> DivW_out;
|
||||
|
||||
// P (pressure field)
|
||||
Texture2D<float> P_in;
|
||||
RWTexture2D<float> P_out;
|
||||
|
||||
// Color map
|
||||
Texture2D<half4> C_in;
|
||||
SamplerState samplerC_in;
|
||||
RWTexture2D<half4> C_out;
|
||||
|
||||
// Jacobi method arguments
|
||||
float Alpha, Beta;
|
||||
|
||||
Texture2D<float> X1_in;
|
||||
Texture2D<float> B1_in;
|
||||
RWTexture2D<float> X1_out;
|
||||
|
||||
Texture2D<float2> X2_in;
|
||||
Texture2D<float2> B2_in;
|
||||
RWTexture2D<float2> 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;
|
||||
}
|
||||
8
Assets/Shaders/StableFluids/Fluid.compute.meta
Normal file
8
Assets/Shaders/StableFluids/Fluid.compute.meta
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
fileFormatVersion: 2
|
||||
guid: cd1f70f1f6b4beb45a02e1c088f0a109
|
||||
ComputeShaderImporter:
|
||||
externalObjects: {}
|
||||
currentAPIMask: 4
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
215
Assets/Shaders/StableFluids/Fluid.cs
Normal file
215
Assets/Shaders/StableFluids/Fluid.cs
Normal file
|
|
@ -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<Force> _forces = new List<Force>();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
13
Assets/Shaders/StableFluids/Fluid.cs.meta
Normal file
13
Assets/Shaders/StableFluids/Fluid.cs.meta
Normal file
|
|
@ -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:
|
||||
80
Assets/Shaders/StableFluids/Fluid.shader
Normal file
80
Assets/Shaders/StableFluids/Fluid.shader
Normal file
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
9
Assets/Shaders/StableFluids/Fluid.shader.meta
Normal file
9
Assets/Shaders/StableFluids/Fluid.shader.meta
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 0486f5f9fa757fa49a5b9613050036fb
|
||||
ShaderImporter:
|
||||
externalObjects: {}
|
||||
defaultTextures: []
|
||||
nonModifiableTextures: []
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
|
@ -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));
|
||||
|
|
|
|||
BIN
GameData/PlayerSettings.msgpack
(Stored with Git LFS)
BIN
GameData/PlayerSettings.msgpack
(Stored with Git LFS)
Binary file not shown.
BIN
UserSettings/EditorUserSettings.asset
(Stored with Git LFS)
BIN
UserSettings/EditorUserSettings.asset
(Stored with Git LFS)
Binary file not shown.
Loading…
Reference in a new issue