diff --git a/Assets/Swarm/CrawlerSwarm.cs b/Assets/Swarm/CrawlerSwarm.cs index 8d97a75..bd5e3ae 100644 --- a/Assets/Swarm/CrawlerSwarm.cs +++ b/Assets/Swarm/CrawlerSwarm.cs @@ -30,6 +30,7 @@ public sealed class CrawlerSwarm : MonoBehaviour ComputeBuffer _drawArgsBuffer; ComputeBuffer _positionBuffer; MaterialPropertyBlock _props; + int _frameCount; #endregion @@ -38,6 +39,7 @@ public sealed class CrawlerSwarm : MonoBehaviour const int kThreadCount = 64; int ThreadGroupCount { get { return _instanceCount / kThreadCount; } } int InstanceCount { get { return kThreadCount * ThreadGroupCount; } } + int HistoryLength { get { return _template.segments + 1; } } #endregion @@ -56,16 +58,25 @@ void Start() }); // Initialize the position buffer. - _positionBuffer = new ComputeBuffer( - (_template.segments + 1) * InstanceCount, 16 - ); + _positionBuffer = new ComputeBuffer(HistoryLength * InstanceCount, 16); var kernel = _compute.FindKernel("CrawlerInit"); - _compute.SetInt("ArraySize", _template.segments + 1); _compute.SetInt("InstanceCount", InstanceCount); + _compute.SetInt("HistoryLength", HistoryLength); _compute.SetBuffer(kernel, "PositionBuffer", _positionBuffer); + _compute.SetTexture(kernel, "DFVolume", _volume.texture); _compute.Dispatch(kernel, ThreadGroupCount, 1, 1); + // Initialize the update kernel. + kernel = _compute.FindKernel("CrawlerUpdate"); + _compute.SetBuffer(kernel, "PositionBuffer", _positionBuffer); + _compute.SetTexture(kernel, "DFVolume", _volume.texture); + + // Initialize the mateiral. + _material.SetInt("_InstanceCount", InstanceCount); + _material.SetInt("_HistoryLength", HistoryLength); + _material.SetBuffer("_PositionBuffer", _positionBuffer); + // This property block is used only for avoiding an instancing bug. _props = new MaterialPropertyBlock(); _props.SetFloat("_UniqueID", Random.value); @@ -79,21 +90,27 @@ void OnDestroy() void Update() { + // Index offset on the position buffer. + var offset0 = InstanceCount * (_frameCount % HistoryLength); + var offset1 = InstanceCount * ((_frameCount + 1) % HistoryLength); + // Update the position buffer. var kernel = _compute.FindKernel("CrawlerUpdate"); + _compute.SetInt("IndexOffset0", offset0); + _compute.SetInt("IndexOffset1", offset1); _compute.SetFloat("Time", Time.time); - _compute.SetBuffer(kernel, "PositionBuffer", _positionBuffer); - _compute.SetTexture(kernel, "DFVolume", _volume.texture); _compute.Dispatch(kernel, ThreadGroupCount, 1, 1); + _frameCount++; + // Draw the meshes with instancing. - _material.SetInt("_InstanceCount", InstanceCount); - _material.SetInt("_ArraySize", _template.segments + 1); - _material.SetBuffer("_PositionBuffer", _positionBuffer); + _material.SetInt("_IndexOffset", _frameCount % HistoryLength); _material.SetVector("_GradientA", _gradient.coeffsA); _material.SetVector("_GradientB", _gradient.coeffsB); _material.SetVector("_GradientC", _gradient.coeffsC2); _material.SetVector("_GradientD", _gradient.coeffsD2); + _material.SetMatrix("_LocalToWorld", transform.localToWorldMatrix); + _material.SetMatrix("_WorldToLocal", transform.worldToLocalMatrix); Graphics.DrawMeshInstancedIndirect( _template.mesh, 0, _material, diff --git a/Assets/Swarm/Shader/Crawler.compute b/Assets/Swarm/Shader/Crawler.compute index 621a3f6..ee23ae4 100644 --- a/Assets/Swarm/Shader/Crawler.compute +++ b/Assets/Swarm/Shader/Crawler.compute @@ -12,8 +12,10 @@ Texture3D DFVolume; SamplerState samplerDFVolume; CBUFFER_START(Params) - uint ArraySize; uint InstanceCount; + uint HistoryLength; + uint IndexOffset0; + uint IndexOffset1; float Time; CBUFFER_END @@ -31,33 +33,53 @@ float4 SampleVolume(float3 p) [numthreads(64, 1, 1)] void CrawlerInit(uint id : SV_DispatchThreadID) { + // Get a random point. float x = UVRandom(id * 0.01334, 0.3728); float y = UVRandom(0.8372, id * 0.01197); float z = UVRandom(4.438, id * 0.01938 - 4.378); float4 p = float4(x, y, z, 0) * 0.8 - 0.4; + float d = SampleVolume(p); - for (uint i = 0; i < ArraySize; i++) + // Swizzle and compre, choose one with closer distance. + { + float4 p2 = p.yzxw; + float d2 = SampleVolume(p2); + p = d2 < d ? p2 : p; + d = min(d, d2); + } + + // Swizzle again. + { + float4 p2 = p.zxyw; + float d2 = SampleVolume(p2); + p = d2 < d ? p2 : p; + d = min(d, d2); + } + + // Fill the position buffer. + for (uint i = 0; i < HistoryLength; i++) PositionBuffer[id + i * InstanceCount] = p; } [numthreads(64, 1, 1)] void CrawlerUpdate(uint id : SV_DispatchThreadID) { - uint offs = id; - for (uint i = 0; i < ArraySize - 1; i++) - { - PositionBuffer[offs] = PositionBuffer[offs + InstanceCount]; - offs += InstanceCount; - } + // Retrieve the previous position. + float3 p = PositionBuffer[IndexOffset0 + id].xyz; - float3 p = PositionBuffer[offs].xyz; + // Distance field (gradient.x, y, z, distance). float4 df = SampleVolume(p) * 6; + // Two independent noise fields (not completely independent though). float4 sn1 = snoise(10.33 + p * 2 + Time * 0.1); float4 sn2 = snoise(32.38 - p * 2 + Time * 0.1); + // Apply the boundary condition to the first noise field. sn1.xyz = -sn1.xyz * df.w + df.xyz * sn1.w; + + // Apply the divergence free noise field to the position. p += cross(sn1.xyz, sn2.xyz) * 0.005; - PositionBuffer[offs].xyz = p; + // Update the buffer. + PositionBuffer[IndexOffset1 + id].xyz = p; } diff --git a/Assets/Swarm/Shader/Swarm.shader b/Assets/Swarm/Shader/Swarm.shader deleted file mode 100644 index cbd1709..0000000 --- a/Assets/Swarm/Shader/Swarm.shader +++ /dev/null @@ -1,99 +0,0 @@ -Shader "Cloner/Swarm" -{ - Properties - { - _MainTex("Albedo", 2D) = "white" {} - _NormalMap("Normal Map", 2D) = "bump" {} - _NormalScale("Normal Scale", Range(0, 1)) = 1 - _Smoothness("Smoothness", Range(0, 1)) = 0 - _Metallic("Metallic", Range(0, 1)) = 0 - } - SubShader - { - Tags { "RenderType"="Opaque" } - - CGPROGRAM - - #pragma surface surf Standard vertex:vert addshadow - #pragma instancing_options procedural:setup - - struct Input - { - float2 uv_MainTex; - }; - - sampler2D _MainTex; - sampler2D _NormalMap; - half _NormalScale; - half _Smoothness; - half _Metallic; - - half3 _GradientA; - half3 _GradientB; - half3 _GradientC; - half3 _GradientD; - - #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED - - StructuredBuffer _PositionBuffer; - uint _ArraySize; - uint _InstanceCount; - - #endif - - void vert(inout appdata_full v, out Input data) - { - UNITY_INITIALIZE_OUTPUT(Input, data); - - #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED - - const float radius = 0.01; - - float phi = v.vertex.x; - int seg = (int)v.vertex.z; - float cp = v.vertex.z / _ArraySize + (float)unity_InstanceID / _InstanceCount; - - seg = clamp(seg, 2, _ArraySize - 1) - 2; - seg = unity_InstanceID + seg * _InstanceCount; - - float3 p0 = _PositionBuffer[seg].xyz; seg += _InstanceCount; - float3 p1 = _PositionBuffer[seg].xyz; seg += _InstanceCount; - float3 p2 = _PositionBuffer[seg].xyz; seg += _InstanceCount; - float3 p3 = _PositionBuffer[seg].xyz; - - float3 vt0 = normalize(p2 - p0); - float3 vt1 = normalize(p3 - p1); - float3 vn = normalize(vt1 - vt0); - float3 vb = cross(vt1, vn); - vn = cross(vb, vt1); - - float2 xy = float2(cos(phi), sin(phi)); - - v.vertex.xyz = p1 + (vn * xy.x + vb * xy.y) * radius; - v.normal.xyz = vn * xy.x + vb * xy.y; - v.texcoord = cp; - - #endif - } - - void setup() - { - #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED - - #endif - } - - void surf (Input IN, inout SurfaceOutputStandard o) - { - float2 uv = IN.uv_MainTex; - half3 gradient = saturate(_GradientA + _GradientB * cos(_GradientC * uv.y + _GradientD)); - o.Albedo = tex2D(_MainTex, uv).rgb * GammaToLinearSpace(gradient); - o.Metallic = _Metallic; - o.Smoothness = _Smoothness; - o.Normal = UnpackScaleNormal(tex2D(_NormalMap, uv), _NormalScale); - } - - ENDCG - } - FallBack "Diffuse" -} diff --git a/Assets/Swarm/Shader/Tube.shader b/Assets/Swarm/Shader/Tube.shader new file mode 100644 index 0000000..26dc962 --- /dev/null +++ b/Assets/Swarm/Shader/Tube.shader @@ -0,0 +1,107 @@ +Shader "Swarm/Tube" +{ + Properties + { + _Smoothness("Smoothness", Range(0, 1)) = 0 + _Metallic("Metallic", Range(0, 1)) = 0 + } + SubShader + { + Tags { "RenderType"="Opaque" } + + CGPROGRAM + + #pragma surface surf Standard vertex:vert addshadow + #pragma instancing_options procedural:setup + + struct Input + { + half param : COLOR; + }; + + half _Smoothness; + half _Metallic; + + half3 _GradientA; + half3 _GradientB; + half3 _GradientC; + half3 _GradientD; + + float4x4 _LocalToWorld; + float4x4 _WorldToLocal; + + #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED + + StructuredBuffer _PositionBuffer; + uint _InstanceCount; + uint _HistoryLength; + uint _IndexOffset; + + #endif + + half3 CosineGradient(half param) + { + half3 c = _GradientB * cos(_GradientC * param + _GradientD); + return GammaToLinearSpace(saturate(c + _GradientA)); + } + + void vert(inout appdata_full v, out Input data) + { + UNITY_INITIALIZE_OUTPUT(Input, data); + + #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED + + const float radius = 0.01; + + float phi = v.vertex.x; + float seg = v.vertex.z; + + uint id = unity_InstanceID; + uint idx = clamp(seg, 1, _HistoryLength - 2); + uint idx0 = (idx - 1 + _IndexOffset + _HistoryLength) % _HistoryLength; + uint idx1 = (idx + _IndexOffset) % _HistoryLength; + uint idx2 = (idx + 1 + _IndexOffset) % _HistoryLength; + uint idx3 = (idx + 2 + _IndexOffset) % _HistoryLength; + + float3 p0 = _PositionBuffer[id + idx0 * _InstanceCount].xyz; + float3 p1 = _PositionBuffer[id + idx1 * _InstanceCount].xyz; + float3 p2 = _PositionBuffer[id + idx2 * _InstanceCount].xyz; + float3 p3 = _PositionBuffer[id + idx3 * _InstanceCount].xyz; + + float3 vt0 = normalize(p2 - p0); + float3 vt1 = normalize(p3 - p1); + float3 vn = normalize(vt1 - vt0); + float3 vb = cross(vt1, vn); + vn = cross(vb, vt1); + + float2 xy = float2(cos(phi), sin(phi)); + + float param = seg / _HistoryLength; + param += (float)unity_InstanceID / _InstanceCount; + + v.vertex.xyz = p1 + (vn * xy.x + vb * xy.y) * radius; + v.normal.xyz = vn * xy.x + vb * xy.y; + v.color = param; + + #endif + } + + void setup() + { + #ifdef UNITY_PROCEDURAL_INSTANCING_ENABLED + unity_ObjectToWorld = _LocalToWorld; + unity_WorldToObject = _WorldToLocal; + #endif + } + + void surf (Input IN, inout SurfaceOutputStandard o) + { + o.Albedo = CosineGradient(IN.param); + o.Metallic = _Metallic; + o.Smoothness = _Smoothness; + } + + ENDCG + } + FallBack "Diffuse" +} diff --git a/Assets/Swarm/Shader/Swarm.shader.meta b/Assets/Swarm/Shader/Tube.shader.meta similarity index 100% rename from Assets/Swarm/Shader/Swarm.shader.meta rename to Assets/Swarm/Shader/Tube.shader.meta diff --git a/Assets/Test/PostFx.asset b/Assets/Test/PostFx.asset index dac9fc3..cb055f0 100644 --- a/Assets/Test/PostFx.asset +++ b/Assets/Test/PostFx.asset @@ -120,9 +120,9 @@ MonoBehaviour: neutralWhiteLevel: 5.3 neutralWhiteClip: 10 basic: - postExposure: 1.75 + postExposure: 1.63 temperature: 1 - tint: -8 + tint: -6 hueShift: 0 saturation: 1.09 contrast: 1 @@ -138,7 +138,7 @@ MonoBehaviour: power: {r: 1, g: 1, b: 1, a: 0} offset: {r: 1, g: 1, b: 1, a: 0} linear: - lift: {r: 0.78238696, g: 1, b: 0.89026314, a: 0} + lift: {r: 0.73218346, g: 1, b: 0.90338755, a: 0} gamma: {r: 1, g: 1, b: 1, a: 0} gain: {r: 1, g: 0.843413, b: 0.767135, a: 0} curves: diff --git a/Assets/Test/Test.unity b/Assets/Test/Test.unity index d2ac46d..e64b17e 100644 --- a/Assets/Test/Test.unity +++ b/Assets/Test/Test.unity @@ -353,7 +353,7 @@ MonoBehaviour: m_Script: {fileID: 11500000, guid: b96d22c8c7818ec49969122b320a267a, type: 3} m_Name: m_EditorClassIdentifier: - _instanceCount: 5000 + _instanceCount: 4000 _template: {fileID: 11400000, guid: 14bd2a077b97cb042ac835ef28e4add9, type: 2} _material: {fileID: 2100000, guid: b3e3f432bd29ac1499686dbad7ae39ef, type: 2} _gradient: {fileID: 11400000, guid: 202a0875a5bc2554a932e60d8ed7e7ec, type: 2} @@ -429,7 +429,7 @@ Prefab: objectReference: {fileID: 0} - target: {fileID: 100000, guid: 1392dc8849c4ad448879f0e14954e36e, type: 3} propertyPath: m_IsActive - value: 1 + value: 0 objectReference: {fileID: 0} m_RemovedComponents: [] m_ParentPrefab: {fileID: 100100000, guid: 1392dc8849c4ad448879f0e14954e36e, type: 3}