├── .gitignore ├── Assets ├── Scenes.meta ├── Scenes │ ├── Fluid Test.unity │ └── Fluid Test.unity.meta ├── Scripts.meta ├── Scripts │ ├── Fluid.cs │ ├── Fluid.cs.meta │ ├── Raymarching.cs │ ├── Raymarching.cs.meta │ ├── Voxelize.cs │ └── Voxelize.cs.meta ├── Shaders.meta └── Shaders │ ├── Fluid.compute │ ├── Fluid.compute.meta │ ├── RaymarchingShader.shader │ ├── RaymarchingShader.shader.meta │ ├── VoxelizeShader.shader │ └── VoxelizeShader.shader.meta ├── LICENSE ├── ProjectSettings ├── AudioManager.asset ├── DynamicsManager.asset ├── EditorBuildSettings.asset ├── EditorSettings.asset ├── GraphicsSettings.asset ├── InputManager.asset ├── NavMeshLayers.asset ├── NetworkManager.asset ├── Physics2DSettings.asset ├── ProjectSettings.asset ├── QualitySettings.asset ├── TagManager.asset └── TimeManager.asset ├── README.md └── obj └── Debug └── DesignTimeResolveAssemblyReferencesInput.cache /.gitignore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/.gitignore -------------------------------------------------------------------------------- /Assets/Scenes.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 1c691dbe62593704e994fc39645d88f4 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Scenes/Fluid Test.unity: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/Assets/Scenes/Fluid Test.unity -------------------------------------------------------------------------------- /Assets/Scenes/Fluid Test.unity.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 35d3b46519dfe5e478a58b1586627bcd 3 | DefaultImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Scripts.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 887a4a3e8a2be9243a00c69ef76e4b2c 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Scripts/Fluid.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using UnityEngine; 3 | using System.Collections; 4 | 5 | public class Fluid : MonoBehaviour 6 | { 7 | public ComputeShader fluidCompute; 8 | public const int size = 128; 9 | 10 | private RenderTexture velocity, nextVelocity, pressure, nextPressure, divergenceTexture, density, nextDensity; 11 | public static RenderTexture media; 12 | public float diffusionConstant = 1.76e-5f; 13 | public bool calculateDiffusion = true; 14 | 15 | 16 | public float distanceStep, timeStep; 17 | public Vector3 sourceVelocity, sourceDensity; 18 | public Vector3 textureSize; 19 | private int advect, jacobi, project, divergence, jacobiPressure, jacobiVector; 20 | public int jacobiCount; 21 | 22 | 23 | public float ambientTemperature, buoyancyConstant; 24 | 25 | [SerializeField] 26 | private viewTexture view; 27 | 28 | enum viewTexture { pressure, nextPressure, velocity, nextVelocity, divergence, media, density, nextDensity} 29 | 30 | // Use this for initialization 31 | void Start () 32 | { 33 | advect = fluidCompute.FindKernel("Advect"); 34 | jacobi = fluidCompute.FindKernel("Jacobi"); 35 | project = fluidCompute.FindKernel("Project"); 36 | divergence = fluidCompute.FindKernel("Divergence"); 37 | jacobiPressure = fluidCompute.FindKernel("JacobiPressure"); 38 | jacobiVector = fluidCompute.FindKernel("JacobiVector"); 39 | 40 | 41 | setupTexture(ref density, RenderTextureFormat.RGHalf); 42 | setupTexture(ref nextDensity, RenderTextureFormat.RGHalf); 43 | setupTexture(ref media, RenderTextureFormat.RHalf); 44 | setupTexture(ref velocity, RenderTextureFormat.ARGBHalf); 45 | setupTexture(ref nextVelocity, RenderTextureFormat.ARGBHalf); 46 | setupTexture(ref pressure, RenderTextureFormat.RHalf); 47 | setupTexture(ref nextPressure, RenderTextureFormat.RHalf); 48 | setupTexture(ref divergenceTexture, RenderTextureFormat.RHalf); 49 | 50 | 51 | fluidCompute.SetVector("textureSize", textureSize); 52 | 53 | //maybe 54 | 55 | 56 | 57 | } 58 | 59 | 60 | void JacobiIteration(RenderTexture x, RenderTexture nextX, float diffusionConstant, float timeStep, float distanceStep) 61 | { 62 | float alpha = (diffusionConstant * timeStep) / (Mathf.Pow(distanceStep, 3)); 63 | float beta = 1 + (6 * alpha); 64 | 65 | 66 | fluidCompute.SetFloat("alpha", alpha); 67 | fluidCompute.SetFloat("rBeta", 1.0f/beta); 68 | 69 | fluidCompute.SetTexture(jacobi, "media", media); 70 | fluidCompute.SetTexture(jacobi, "x", x); 71 | fluidCompute.SetTexture(jacobi, "nextX", nextX); 72 | fluidCompute.Dispatch(jacobi, 16, 16, 16); 73 | 74 | 75 | 76 | } 77 | void JacobiIterationVector(RenderTexture x, RenderTexture nextX, float diffusionConstant, float timeStep, float distanceStep) 78 | { 79 | float alpha = (diffusionConstant * timeStep) / (Mathf.Pow(distanceStep, 3)); 80 | float beta = 1 + (6 * alpha); 81 | 82 | 83 | fluidCompute.SetFloat("alpha", alpha); 84 | fluidCompute.SetFloat("rBeta", 1.0f / beta); 85 | 86 | fluidCompute.SetTexture(jacobiVector, "media", media); 87 | fluidCompute.SetTexture(jacobiVector, "x3D", x); 88 | fluidCompute.SetTexture(jacobiVector, "nextX3D", nextX); 89 | fluidCompute.Dispatch(jacobiVector, 16, 16, 16); 90 | 91 | } 92 | 93 | void JacobiPressure(int iterations) 94 | { 95 | 96 | fluidCompute.SetFloat("alpha", 1); 97 | fluidCompute.SetFloat("rBeta", 1.0f / 6.0f); 98 | fluidCompute.SetTexture(jacobiPressure, "b", divergenceTexture); 99 | fluidCompute.SetTexture(jacobiPressure, "media", media); 100 | 101 | for (int i = 0; i < iterations; i++) 102 | { 103 | fluidCompute.SetTexture(jacobiPressure, "x", pressure); 104 | fluidCompute.SetTexture(jacobiPressure, "nextX", nextPressure); 105 | 106 | fluidCompute.Dispatch(jacobiPressure, 16, 16, 16); 107 | 108 | fluidCompute.SetTexture(jacobiPressure, "x", nextPressure); 109 | fluidCompute.SetTexture(jacobiPressure, "nextX", pressure); 110 | 111 | fluidCompute.Dispatch(jacobiPressure, 16, 16, 16); 112 | } 113 | 114 | } 115 | 116 | 117 | 118 | // Update is called once per frame 119 | void FixedUpdate () { 120 | 121 | //set compute constants 122 | fluidCompute.SetFloat("distanceStep", distanceStep); 123 | fluidCompute.SetFloat("timeStep", timeStep); 124 | fluidCompute.SetVector("sourceVelocity", sourceVelocity); 125 | fluidCompute.SetVector("sourceDensity", sourceDensity); 126 | fluidCompute.SetFloat("buoyancyConstant", buoyancyConstant); 127 | fluidCompute.SetFloat("ambientTemperature", ambientTemperature); 128 | 129 | 130 | 131 | fluidCompute.SetTexture(advect, "density", density); 132 | fluidCompute.SetTexture(advect, "nextDensity", nextDensity); 133 | fluidCompute.SetTexture(advect, "velocity", velocity); 134 | fluidCompute.SetTexture(advect, "nextVelocity", nextVelocity); 135 | fluidCompute.SetTexture(advect, "media", media); 136 | 137 | fluidCompute.Dispatch(advect, 16, 16, 16); 138 | 139 | if (calculateDiffusion) 140 | { 141 | for (int i = 0; i < jacobiCount; i++) 142 | { 143 | JacobiIterationVector(nextDensity, density, diffusionConstant, timeStep, distanceStep); 144 | JacobiIterationVector(density, nextDensity, diffusionConstant, timeStep, distanceStep); 145 | } 146 | } 147 | 148 | fluidCompute.SetTexture(divergence, "velocity", nextVelocity); 149 | fluidCompute.SetTexture(divergence, "divergenceTexture", divergenceTexture); 150 | fluidCompute.SetTexture(divergence, "media", media); 151 | fluidCompute.Dispatch(divergence, 16, 16, 16); 152 | 153 | JacobiPressure(jacobiCount); 154 | 155 | fluidCompute.SetTexture(project, "media", media); 156 | fluidCompute.SetTexture(project, "pressure", pressure); 157 | fluidCompute.SetTexture(project, "velocity", nextVelocity); 158 | fluidCompute.SetTexture(project, "nextVelocity", velocity); 159 | fluidCompute.Dispatch(project,16,16,16); 160 | 161 | switch (view) 162 | { 163 | case viewTexture.pressure: 164 | Shader.SetGlobalTexture("Current", pressure); 165 | break; 166 | case viewTexture.nextPressure: 167 | Shader.SetGlobalTexture("Current", nextPressure); 168 | break; 169 | case viewTexture.velocity: 170 | Shader.SetGlobalTexture("Current", velocity); 171 | break; 172 | case viewTexture.nextVelocity: 173 | Shader.SetGlobalTexture("Current", nextVelocity); 174 | break; 175 | case viewTexture.divergence: 176 | Shader.SetGlobalTexture("Current", divergenceTexture); 177 | break; 178 | case viewTexture.media: 179 | Shader.SetGlobalTexture("Current", media); 180 | break; 181 | case viewTexture.density: 182 | Shader.SetGlobalTexture("Current", density); 183 | break; 184 | case viewTexture.nextDensity: 185 | Shader.SetGlobalTexture("Current", nextDensity); 186 | break; 187 | default: 188 | throw new ArgumentOutOfRangeException(); 189 | } 190 | 191 | 192 | RenderTexture temp = density; 193 | density = nextDensity; 194 | nextDensity = temp; 195 | 196 | } 197 | 198 | 199 | void setupTexture(ref RenderTexture tex, RenderTextureFormat format) 200 | { 201 | //check if those buffers exist 202 | DestroyImmediate(tex); 203 | 204 | //make those buffers 205 | tex = new RenderTexture(size, size, 0, format); 206 | tex.isVolume = true; 207 | tex.volumeDepth = size; 208 | tex.enableRandomWrite = true; 209 | tex.filterMode = FilterMode.Trilinear; 210 | tex.Create(); 211 | } 212 | } 213 | -------------------------------------------------------------------------------- /Assets/Scripts/Fluid.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 809b9b8c0752dce4389f1e25f2a350e2 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Scripts/Raymarching.cs: -------------------------------------------------------------------------------- 1 | using UnityEditor; 2 | using UnityEngine; 3 | using System.Collections; 4 | 5 | public class Raymarching : MonoBehaviour 6 | { 7 | // Fields 8 | private Vector3 LL; 9 | private Vector3 LR; 10 | private Vector3 corner; 11 | private Camera mainCamera; 12 | private Material rayMat; 13 | private Vector2 size; 14 | public float stepSize; 15 | public int stepCount; 16 | private Vector3 UL; 17 | public Vector4 intensityMask; 18 | public Shader raymarchingShader; 19 | 20 | // Methods 21 | private void Start() 22 | { 23 | GetComponent().enabled = true; 24 | rayMat = new Material(raymarchingShader); 25 | renderer.material = rayMat; 26 | 27 | 28 | } 29 | 30 | 31 | void Update() 32 | { 33 | if (Camera.current != null) 34 | { 35 | Vector3 up = Camera.current.transform.up; 36 | Vector3 right = Camera.current.transform.right; 37 | 38 | this.LL = Camera.current.ViewportToWorldPoint(new Vector3(0f, 0f, Camera.current.nearClipPlane)); 39 | this.UL = Camera.current.ViewportToWorldPoint(new Vector3(0f, 1f, Camera.current.nearClipPlane)); 40 | this.LR = Camera.current.ViewportToWorldPoint(new Vector3(1f, 0f, Camera.current.nearClipPlane)); 41 | Vector3 vector = this.LL - this.LR; 42 | Vector3 vector2 = this.LL - this.UL; 43 | this.size = new Vector2(vector.magnitude, vector2.magnitude); 44 | rayMat.SetVector("cameraWorldSize", new Vector4(this.size.x, this.size.y)); 45 | 46 | 47 | rayMat.SetVector("cameraWorldPosition", Camera.current.transform.position); 48 | rayMat.SetVector("cameraUp", new Vector4(up.x, up.y, up.z)); 49 | rayMat.SetVector("cameraRight", new Vector4(right.x, right.y, right.z)); 50 | rayMat.SetInt("stepCount", stepCount); 51 | rayMat.SetFloat("StepSize", this.stepSize); 52 | rayMat.SetVector("intensityMask", intensityMask); 53 | 54 | rayMat.SetFloat("worldSize", transform.localScale.x); 55 | 56 | corner = Camera.current.ViewportToWorldPoint(new Vector3(0f, 0f, Camera.current.nearClipPlane)); 57 | rayMat.SetVector("screenCorner", new Vector4(corner.x, corner.y, corner.z)); 58 | } 59 | } 60 | 61 | } 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /Assets/Scripts/Raymarching.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: db9e26713ec6e9e4091b4916aee52a89 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Scripts/Voxelize.cs: -------------------------------------------------------------------------------- 1 | using UnityEngine; 2 | using System.Collections; 3 | 4 | 5 | public class Voxelize : MonoBehaviour { 6 | 7 | public Shader voxelizeShader; 8 | 9 | 10 | 11 | // Use this for initialization 12 | void Start () { 13 | 14 | RenderTexture sizeTex = new RenderTexture(128, 128, 0); 15 | camera.targetTexture = sizeTex; 16 | camera.aspect = 1; 17 | 18 | 19 | Matrix4x4 P = GL.GetGPUProjectionMatrix(camera.projectionMatrix, false); 20 | Matrix4x4 V = camera.worldToCameraMatrix; 21 | Matrix4x4 MVP = P * V; 22 | Shader.SetGlobalMatrix("zMVP", MVP); 23 | 24 | 25 | 26 | } 27 | 28 | 29 | void FixedUpdate() 30 | { 31 | Graphics.SetRenderTarget(Fluid.media); 32 | GL.Clear(true, true, new Color(0, 0, 0, 0)); 33 | 34 | Graphics.SetRandomWriteTarget(1, Fluid.media); 35 | camera.RenderWithShader(voxelizeShader, ""); 36 | Graphics.ClearRandomWriteTargets(); 37 | 38 | } 39 | 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Assets/Scripts/Voxelize.cs.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2bb791045a125dd499f766c4b82dbd6f 3 | MonoImporter: 4 | serializedVersion: 2 5 | defaultReferences: [] 6 | executionOrder: 0 7 | icon: {instanceID: 0} 8 | userData: 9 | -------------------------------------------------------------------------------- /Assets/Shaders.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 2c2f2222c0f3959428ec01af92cce1c6 3 | folderAsset: yes 4 | DefaultImporter: 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Shaders/Fluid.compute: -------------------------------------------------------------------------------- 1 | // Each #kernel tells which function to compile; you can have many kernels 2 | #pragma kernel Advect 3 | #pragma kernel Jacobi 4 | #pragma kernel Project 5 | #pragma kernel Divergence 6 | #pragma kernel JacobiPressure 7 | #pragma kernel JacobiVector 8 | 9 | SamplerState LinearClampSampler; 10 | 11 | RWTexture3D nextVelocity; 12 | RWTexture3D nextPressure; 13 | RWTexture3D divergenceTexture; 14 | Texture3D velocity; 15 | Texture3D pressure; 16 | Texture3D media; 17 | Texture3D density; 18 | RWTexture3D nextDensity; 19 | const uint modBro; 20 | const float ambientTemperature; 21 | const float buoyancyConstant; 22 | 23 | const float4 textureSize; 24 | const float distanceStep, timeStep; 25 | const float4 sourceVelocity; 26 | const float4 sourceDensity; 27 | 28 | bool isObstacle(uint3 pos) 29 | { 30 | return any(pos == 1) || any(pos == 127 || any(media[pos] != 0)); 31 | 32 | } 33 | 34 | [numthreads(8, 8, 8)] 35 | void Divergence(uint3 id : SV_DispatchThreadID) 36 | { 37 | // Get velocity values from neighboring cells. 38 | float fieldL = velocity[id - int3(1, 0, 0)].x; 39 | float fieldR = velocity[id + int3(1, 0, 0)].x; 40 | float fieldB = velocity[id - int3(0, 1, 0)].y; 41 | float fieldT = velocity[id + int3(0, 1, 0)].y; 42 | float fieldD = velocity[id - int3(0, 0, 1)].z; 43 | float fieldU = velocity[id + int3(0, 0, 1)].z; 44 | 45 | 46 | if (isObstacle(id - int3(1, 0, 0))) fieldL = 0; 47 | if (isObstacle(id + int3(1, 0, 0))) fieldR = 0; 48 | if (isObstacle(id - int3(0, 1, 0))) fieldB = 0; 49 | if (isObstacle(id + int3(0, 1, 0))) fieldT = 0; 50 | if (isObstacle(id - int3(0, 0, 1))) fieldD = 0; 51 | if (isObstacle(id + int3(0, 0, 1))) fieldU = 0; 52 | 53 | // Compute the velocity's divergence using central differences. 54 | float divergence = 0.5 * ((fieldR - fieldL) + 55 | (fieldT - fieldB) + 56 | (fieldU - fieldD)); 57 | 58 | divergenceTexture[id] = -divergence; 59 | } 60 | 61 | 62 | [numthreads(8, 8, 8)] 63 | void Advect(uint3 id : SV_DispatchThreadID) 64 | { 65 | //advect 66 | if (isObstacle(id)) 67 | { 68 | nextVelocity[id] = 0; 69 | nextDensity[id] = 0; 70 | return; 71 | } 72 | 73 | 74 | // project backwards to find a location from which to pull vel, dens 75 | float3 sam = id - timeStep * velocity[id]; 76 | sam = (sam + 0.5) / 128; 77 | 78 | // sample at those positions 79 | float3 v = velocity.SampleLevel(LinearClampSampler, sam, 0); 80 | // density is (density, temperature) 81 | float2 d = density.SampleLevel(LinearClampSampler, sam, 0); 82 | 83 | nextVelocity[id] = v 84 | // vas ist dis? surely not buoyancy 85 | + (d.x * float3(0, -9.81, 0) * timeStep) 86 | // buoyancy term 87 | + float3(0, 1, 0) * (buoyancyConstant * (d.y - ambientTemperature) ) * timeStep; 88 | nextDensity[id] = d; 89 | 90 | //add sources 91 | if (all(id == 64) ) 92 | { 93 | nextVelocity[id] = v + (sourceVelocity.xyz * timeStep) + (d.x * float3(0, -9.81, 0) * timeStep) + float3(0, 1, 0) * (buoyancyConstant * (d.y - ambientTemperature)) * timeStep; 94 | nextDensity[id] = d + (sourceDensity.xy * timeStep); 95 | 96 | } 97 | else if ( all(id == uint3(64, 64, 63)) || all(id == uint3(64, 64, 63)) || all(id == uint3(64, 63, 64)) || all(id == uint3(63, 64, 64)) || all(id == uint3(64, 64, 65)) 98 | || all(id == uint3(64, 65, 64)) || all(id == uint3(65, 64, 64))) 99 | { 100 | nextVelocity[id] = v + ((sourceVelocity.xyz * timeStep) + (d.x * float3(0, -9.81, 0) * timeStep) + float3(0, 1, 0) * (buoyancyConstant * (d.y - ambientTemperature)) * timeStep)/2; 101 | nextDensity[id] = d + (sourceDensity.xy * timeStep)/2; 102 | } 103 | } 104 | 105 | 106 | [numthreads(8, 8, 8)] 107 | void Advect2ndOrder(uint3 id : SV_DispatchThreadID) 108 | { 109 | //advect 110 | if (isObstacle(id)) 111 | { 112 | nextVelocity[id] = 0; 113 | nextDensity[id] = 0; 114 | return; 115 | } 116 | 117 | 118 | // project backwards to find a location from which to pull vel, dens 119 | float3 sam = id - timeStep * velocity[id]; 120 | sam = (sam + 0.5) / 128; 121 | 122 | // sample velocity only 123 | float3 v = velocity.SampleLevel(LinearClampSampler, sam, 0); 124 | 125 | // project backwards using the average of both velocities 126 | sam = id - 0.5 * timeStep * (velocity[id] + v); 127 | sam = (sam + 0.5) / 128; 128 | 129 | // now sample both using this new location 130 | v = velocity.SampleLevel(LinearClampSampler, sam, 0); 131 | // density is (density, temperature) 132 | float2 d = density.SampleLevel(LinearClampSampler, sam, 0); 133 | 134 | nextVelocity[id] = v 135 | // vas ist dis? surely not buoyancy 136 | + (d.x * float3(0, -9.81, 0) * timeStep) 137 | // buoyancy term 138 | + float3(0, 1, 0) * (buoyancyConstant * (d.y - ambientTemperature) ) * timeStep; 139 | nextDensity[id] = d; 140 | 141 | //add sources 142 | if (all(id == 64) ) 143 | { 144 | nextVelocity[id] = v + (sourceVelocity.xyz * timeStep) + (d.x * float3(0, -9.81, 0) * timeStep) + float3(0, 1, 0) * (buoyancyConstant * (d.y - ambientTemperature)) * timeStep; 145 | nextDensity[id] = d + (sourceDensity.xy * timeStep); 146 | 147 | } 148 | else if ( all(id == uint3(64, 64, 63)) || all(id == uint3(64, 64, 63)) || all(id == uint3(64, 63, 64)) || all(id == uint3(63, 64, 64)) || all(id == uint3(64, 64, 65)) 149 | || all(id == uint3(64, 65, 64)) || all(id == uint3(65, 64, 64))) 150 | { 151 | nextVelocity[id] = v + ((sourceVelocity.xyz * timeStep) + (d.x * float3(0, -9.81, 0) * timeStep) + float3(0, 1, 0) * (buoyancyConstant * (d.y - ambientTemperature)) * timeStep)/2; 152 | nextDensity[id] = d + (sourceDensity.xy * timeStep)/2; 153 | } 154 | } 155 | 156 | 157 | Texture3D x; 158 | Texture3D b; 159 | const int jacobiIterations; 160 | RWTexture3D nextX; 161 | const float alpha, rBeta; 162 | 163 | /* 164 | groupshared float sharedValue[8][8][8]; 165 | [numthreads(8, 8, 8)] 166 | void JacobiGeneric(uint3 id : SV_DispatchThreadID, uint3 gid : SV_GroupThreadID) 167 | { 168 | float oldValue = b[id]; 169 | sharedValue[gid.x][gid.y][gid.z] = x[id]; 170 | GroupMemoryBarrierWithGroupSync(); 171 | 172 | for(int i =0; i < jacobiIterations; i++) 173 | { 174 | 175 | float L = sharedValue[gid.x-1][gid.y][gid.z]; 176 | float R = sharedValue[gid.x+1][gid.y][gid.z]; 177 | float B = sharedValue[gid.x][gid.y-1][gid.z]; 178 | float T = sharedValue[gid.x][gid.y+1][gid.z]; 179 | float D = sharedValue[gid.x][gid.y][gid.z-1]; 180 | float U = sharedValue[gid.x][gid.y][gid.z+1]; 181 | 182 | GroupMemoryBarrierWithGroupSync(); 183 | 184 | sharedValue[gid.x][gid.y][gid.z] = (alpha * (L + R + B + T + D + U) + oldValue) * rBeta; 185 | 186 | GroupMemoryBarrierWithGroupSync(); 187 | } 188 | 189 | x[id] = sharedValue[gid.x][gid.y][gid.z]; 190 | 191 | } 192 | */ 193 | 194 | 195 | 196 | [numthreads(8, 8, 8)] 197 | void JacobiPressure(uint3 id : SV_DispatchThreadID) 198 | { 199 | //float alpha = (diffusionConstant * timeStep) / (Mathf.Pow(distanceStep, 3)); 200 | //float beta = 1 + (6 * alpha); 201 | 202 | float C = x[id]; 203 | 204 | 205 | float L = x[id - int3(1, 0, 0)]; 206 | float R = x[id + int3(1, 0, 0)]; 207 | float B = x[id - int3(0, 1, 0)]; 208 | float T = x[id + int3(0, 1, 0)]; 209 | float D = x[id - int3(0, 0, 1)]; 210 | float U = x[id + int3(0, 0, 1)]; 211 | 212 | 213 | if (isObstacle(id - int3(1, 0, 0))) L = C; 214 | if (isObstacle(id + int3(1, 0, 0))) R = C; 215 | if (isObstacle(id - int3(0, 1, 0))) B = C; 216 | if (isObstacle(id + int3(0, 1, 0))) T = C; 217 | if (isObstacle(id - int3(0, 0, 1))) D = C; 218 | if (isObstacle(id + int3(0, 0, 1))) U = C; 219 | 220 | 221 | nextX[id] = ( (L + R + B + T + D + U) + b[id]) / 6; 222 | } 223 | 224 | 225 | [numthreads(8, 8, 8)] 226 | void Jacobi(uint3 id : SV_DispatchThreadID) 227 | { 228 | //float alpha = (diffusionConstant * timeStep) / (Mathf.Pow(distanceStep, 3)); 229 | //float beta = 1 + (6 * alpha); 230 | if (any(id == 1) || any(id == 127) || any(media[id] != 0)) 231 | { 232 | return; 233 | } 234 | 235 | float L = x[id - int3(1, 0, 0)]; 236 | float R = x[id + int3(1, 0, 0)]; 237 | float B = x[id - int3(0, 1, 0)]; 238 | float T = x[id + int3(0, 1, 0)]; 239 | float D = x[id - int3(0, 0, 1)]; 240 | float U = x[id + int3(0, 0, 1)]; 241 | 242 | 243 | nextX[id] = ( alpha* (L + R + B + T + D + U) + x[id]) * rBeta; 244 | } 245 | 246 | texture3D x3D; 247 | RWTexture3D nextX3D; 248 | [numthreads(8, 8, 8)] 249 | void JacobiVector(uint3 id : SV_DispatchThreadID) 250 | { 251 | //float alpha = (diffusionConstant * timeStep) / (Mathf.Pow(distanceStep, 3)); 252 | //float beta = 1 + (6 * alpha); 253 | 254 | float3 C = x3D[id]; 255 | float3 L = x3D[id - int3(1, 0, 0)]; 256 | float3 R = x3D[id + int3(1, 0, 0)]; 257 | float3 B = x3D[id - int3(0, 1, 0)]; 258 | float3 T = x3D[id + int3(0, 1, 0)]; 259 | float3 D = x3D[id - int3(0, 0, 1)]; 260 | float3 U = x3D[id + int3(0, 0, 1)]; 261 | 262 | if (isObstacle(id - int3(1, 0, 0))) L = C; 263 | if (isObstacle(id + int3(1, 0, 0))) R = C; 264 | if (isObstacle(id - int3(0, 1, 0))) B = C; 265 | if (isObstacle(id + int3(0, 1, 0))) T = C; 266 | if (isObstacle(id - int3(0, 0, 1))) D = C; 267 | if (isObstacle(id + int3(0, 0, 1))) U = C; 268 | 269 | 270 | nextX3D[id] = ( alpha* (L + R + B + T + D + U) + x3D[id]) * rBeta; 271 | } 272 | 273 | 274 | [numthreads(8, 8, 8)] 275 | void Project(uint3 id : SV_DispatchThreadID) 276 | { 277 | if (isObstacle(id)) 278 | { 279 | nextVelocity[id] = 0; 280 | return; 281 | } 282 | 283 | float C = pressure[id]; 284 | float L = pressure[id - int3(1, 0, 0)]; 285 | float R = pressure[id + int3(1, 0, 0)]; 286 | float B = pressure[id - int3(0, 1, 0)]; 287 | float T = pressure[id + int3(0, 1, 0)]; 288 | float D = pressure[id - int3(0, 0, 1)]; 289 | float U = pressure[id + int3(0, 0, 1)]; 290 | 291 | float3 vMask = float3(1, 1, 1); 292 | // If an adjacent cell is solid, ignore its pressure 293 | // and use its velocity. 294 | 295 | if (isObstacle(id - int3(1, 0, 0))) { 296 | L = C; vMask.x = 0; 297 | } 298 | if (isObstacle(id + int3(1, 1, 0))) { 299 | R = C; vMask.x = 0; 300 | } 301 | if (isObstacle(id - int3(0, 1, 0))) { 302 | B = C; vMask.y = 0; 303 | } 304 | if (isObstacle(id + int3(0, 0, 0))) { 305 | T = C; vMask.y = 0; 306 | } 307 | if (isObstacle(id - int3(0, 0, 1))) { 308 | D = C; vMask.z = 0; 309 | } 310 | if (isObstacle(id + int3(0, 0, 1))) { 311 | U = C; vMask.z = 0; 312 | } 313 | 314 | 315 | //uses advected velocity; 316 | float3 gradient = 0.5*float3(R - L, T - B, U - D); 317 | 318 | nextVelocity[id] = (velocity[id] - gradient) * vMask; 319 | } 320 | -------------------------------------------------------------------------------- /Assets/Shaders/Fluid.compute.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 982b40a4a609e464b9335f396e450d73 3 | ComputeShaderImporter: 4 | userData: 5 | -------------------------------------------------------------------------------- /Assets/Shaders/RaymarchingShader.shader: -------------------------------------------------------------------------------- 1 | Shader "Raymarching" { 2 | Properties { 3 | 4 | } 5 | SubShader { 6 | Tags { "RenderType"="Transparent" } 7 | LOD 200 8 | 9 | Pass { 10 | cull front ZTest always zwrite off 11 | Blend SrcAlpha OneMinusSrcAlpha 12 | CGPROGRAM 13 | 14 | #pragma target 5.0 15 | #pragma vertex vert 16 | #pragma fragment frag 17 | #include "UnityCG.cginc" 18 | 19 | struct v2f { 20 | float4 pos : SV_POSITION; 21 | float3 worldPos : texcoord0; 22 | float4 screenPos : texcoord1; 23 | }; 24 | 25 | 26 | v2f vert(appdata_base v) { 27 | v2f o; 28 | o.pos = mul(UNITY_MATRIX_MVP, v.vertex); 29 | o.worldPos = mul(_Object2World, v.vertex); 30 | o.screenPos = ComputeScreenPos(o.pos); 31 | return o; 32 | } 33 | 34 | 35 | 36 | 37 | float4 screenCorner; 38 | float4 cameraUp; 39 | float4 cameraRight; 40 | float StepSize; 41 | float4 cameraWorldSize; 42 | float worldSize; 43 | float3 simulationTextureSize; 44 | float4 cameraWorldPosition; 45 | float4 intensityMask; 46 | int stepCount; 47 | 48 | sampler3D Current; 49 | sampler2D _CameraDepthTexture; 50 | 51 | 52 | float4 frag(v2f IN) : COLOR { 53 | 54 | float2 uv = IN.screenPos.xy/IN.screenPos.w; 55 | float depth = LinearEyeDepth(tex2D(_CameraDepthTexture, uv)); 56 | 57 | float3 front = screenCorner.xyz + (uv.x * cameraWorldSize.x * cameraRight.xyz) + (uv.y * cameraWorldSize.y * cameraUp.xyz); 58 | float3 back = IN.worldPos.xyz; 59 | 60 | float3 dir = normalize(front - cameraWorldPosition); 61 | float4 pos = float4(front, 0); 62 | 63 | float4 dst = float4(0, 0, 0, 0); 64 | float4 src = 0; 65 | 66 | float3 value = float3(0,0,0); 67 | 68 | float3 Step = dir * StepSize; 69 | 70 | 71 | for(int i = 0; i < stepCount; i++) 72 | { 73 | 74 | 75 | 76 | value = abs(tex3Dlod(Current, float4(pos.xyz/128,0) ).rgb); 77 | 78 | 79 | src = float4(value,length(value) ) * intensityMask; 80 | 81 | //Front to back blending 82 | // dst.rgb = dst.rgb + (1 - dst.a) * src.a * src.rgb 83 | // dst.a = dst.a + (1 - dst.a) * src.a 84 | src.rgb *= src.a; 85 | dst = (1.0f - dst.a)*src + dst; 86 | 87 | //break from the loop when alpha gets high enough 88 | if(dst.a >= .95) 89 | break; 90 | 91 | 92 | //advance the current position 93 | pos.xyz += Step; 94 | 95 | if(length(pos - front) > depth) 96 | break; 97 | 98 | 99 | 100 | } 101 | 102 | return dst; 103 | } 104 | 105 | 106 | ENDCG 107 | } 108 | } 109 | } -------------------------------------------------------------------------------- /Assets/Shaders/RaymarchingShader.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 8999f769659079243b30b1ac490e468c 3 | ShaderImporter: 4 | defaultTextures: [] 5 | userData: 6 | -------------------------------------------------------------------------------- /Assets/Shaders/VoxelizeShader.shader: -------------------------------------------------------------------------------- 1 | Shader "Voxelize" 2 | { 3 | Properties 4 | { 5 | _Emit ("Sound Emission", Vector) = (0,0,0,0) 6 | } 7 | 8 | SubShader 9 | { 10 | Pass 11 | { 12 | zwrite off ztest always cull off 13 | 14 | Tags { "RenderType"="Opaque" } 15 | LOD 200 16 | 17 | CGPROGRAM 18 | #pragma target 5.0 19 | #pragma vertex VS_Main 20 | #pragma fragment FS_Main 21 | #pragma geometry GS_Main 22 | #include "UnityCG.cginc" 23 | 24 | // ************************************************************** 25 | // Data structures * 26 | // ************************************************************** 27 | struct GS_INPUT 28 | { 29 | float4 pos : POSITION; 30 | 31 | }; 32 | 33 | struct FS_INPUT 34 | { 35 | float4 pos : POSITION; 36 | 37 | float4 worldPos : texcoord0; 38 | 39 | }; 40 | 41 | 42 | // ************************************************************** 43 | // Vars * 44 | // ************************************************************** 45 | 46 | float4x4 zMVP; 47 | RWTexture3D Media : register(u1); 48 | float4 _Emit; 49 | 50 | // ************************************************************** 51 | // Shader Programs * 52 | // ************************************************************** 53 | 54 | // Vertex Shader ------------------------------------------------ 55 | GS_INPUT VS_Main(appdata_base v) 56 | { 57 | GS_INPUT output = (GS_INPUT)0; 58 | 59 | output.pos = mul(_Object2World,v.vertex); 60 | 61 | return output; 62 | } 63 | 64 | 65 | 66 | // Geometry Shader ----------------------------------------------------- 67 | [maxvertexcount(3)] 68 | void GS_Main(triangle GS_INPUT p[3], inout TriangleStream triStream) 69 | { 70 | 71 | //calculate normal 72 | float3 normal = cross( (p[1].pos.xyz - p[0].pos.xyz ), (p[2].pos.xyz - p[0].pos.xyz) ); 73 | 74 | float xDot = abs( dot(normal,float3(1,0,0))); 75 | float yDot = abs( dot(normal,float3(0,1,0))); 76 | float zDot = abs( dot(normal,float3(0,0,1))); 77 | 78 | FS_INPUT pIn; 79 | float4 p0world = p[0].pos; 80 | float4 p1world = p[1].pos; 81 | float4 p2world = p[2].pos; 82 | 83 | 84 | if(xDot > yDot && xDot > zDot){ 85 | 86 | p[0].pos = p[0].pos.yzxw; 87 | p[1].pos = p[1].pos.yzxw; 88 | p[2].pos = p[2].pos.yzxw; 89 | } 90 | else if(yDot > xDot && yDot > zDot){ 91 | p[0].pos = p[0].pos.zxyw; 92 | p[1].pos = p[1].pos.zxyw; 93 | p[2].pos = p[2].pos.zxyw; 94 | 95 | } 96 | 97 | pIn.pos = mul(zMVP, p[0].pos); 98 | pIn.worldPos = p0world; 99 | triStream.Append(pIn); 100 | 101 | pIn.pos = mul(zMVP, p[1].pos); 102 | pIn.worldPos = p1world; 103 | triStream.Append(pIn); 104 | 105 | pIn.pos = mul(zMVP, p[2].pos); 106 | pIn.worldPos = p2world; 107 | triStream.Append(pIn); 108 | 109 | } 110 | 111 | float4 FS_Main(FS_INPUT input) : COLOR 112 | { 113 | //128 128 128 -> 128 128 128 114 | // 144, 144, 144 -> 256, 256, 256 115 | // 128 + ( (world pos - 128 ) * 128/16 ) 116 | // 112-> 0 117 | //float3 relativePos = 128 + ((input.worldPos.xyz - 128) * 128 / 16); 118 | 119 | Media[uint3(input.worldPos.xyz)] = 10; 120 | 121 | discard; 122 | return float4(0,0,0,0); 123 | } 124 | 125 | ENDCG 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Assets/Shaders/VoxelizeShader.shader.meta: -------------------------------------------------------------------------------- 1 | fileFormatVersion: 2 2 | guid: 3f6ecfca53794574090c504a4e2aedb4 3 | ShaderImporter: 4 | defaultTextures: [] 5 | userData: 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Kelly MacNeill 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of 6 | this software and associated documentation files (the "Software"), to deal in 7 | the Software without restriction, including without limitation the rights to 8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software is furnished to do so, 10 | subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS 17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR 18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER 19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /ProjectSettings/AudioManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/AudioManager.asset -------------------------------------------------------------------------------- /ProjectSettings/DynamicsManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/DynamicsManager.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorBuildSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/EditorBuildSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/EditorSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/EditorSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/GraphicsSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/GraphicsSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/InputManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/InputManager.asset -------------------------------------------------------------------------------- /ProjectSettings/NavMeshLayers.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/NavMeshLayers.asset -------------------------------------------------------------------------------- /ProjectSettings/NetworkManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/NetworkManager.asset -------------------------------------------------------------------------------- /ProjectSettings/Physics2DSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/Physics2DSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/ProjectSettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/ProjectSettings.asset -------------------------------------------------------------------------------- /ProjectSettings/QualitySettings.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/QualitySettings.asset -------------------------------------------------------------------------------- /ProjectSettings/TagManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/TagManager.asset -------------------------------------------------------------------------------- /ProjectSettings/TimeManager.asset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/ProjectSettings/TimeManager.asset -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Pyrofluid 2 | ========= 3 | 4 | An interactive Unity gpgpu fluid simulation 5 | 6 | Super quick getting started instructions: 7 | 8 | 1) open up the fluid test scene 9 | 2) press the play button 10 | 3) the game view is boring, so probably switch to the scene view and pan around. You can move obstacles and the fluid should respond appropriately. 11 | 4) you can change fluid simulation properties on the fluid manager gameobject 12 | 5) you can change visualization properties on the Ray Marching Box gameobject 13 | -------------------------------------------------------------------------------- /obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Pyromuffin/Pyrofluid/209fcec090ccbad312323f8ac12eadc8e400f914/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache --------------------------------------------------------------------------------