diff --git a/Assets/Shaders/Compute/Stardust/Stardust.cs b/Assets/Shaders/Compute/Stardust/Stardust.cs index fd4ada9c0..03c49d663 100644 --- a/Assets/Shaders/Compute/Stardust/Stardust.cs +++ b/Assets/Shaders/Compute/Stardust/Stardust.cs @@ -59,8 +59,10 @@ public class Stardust : MonoBehaviour // The dust is drawn by a command buffer on TargetCamera: a camera replays its buffers at their event and never culls them, // so the draw needs no bounds and no extent can fall short of how deep the field is. private CommandBuffer _draw; - // Before the [ImageEffectOpaque] nebula, so its march fogs the dust; a deferred camera skips the forward-opaque events when nothing forward-opaque is drawn (B46, B49). - private const CameraEvent DrawEvent = CameraEvent.BeforeImageEffectsOpaque; + // Into the G-buffer, so the dust's depth is in the depth texture the [ImageEffectOpaque] nebula marches against and the fog + // attenuates the dust by distance like any opaque surface. Drawn later (BeforeImageEffectsOpaque) it missed that depth and + // was fogged as if it were the far background. + private const CameraEvent DrawEvent = CameraEvent.AfterGBuffer; #endregion @@ -105,7 +107,12 @@ public class Stardust : MonoBehaviour DetachDraw(); _draw?.Release(); _draw = new CommandBuffer { name = "Stardust" }; - _draw.DrawProcedural(Matrix4x4.identity, ParticleMaterial, 0, MeshTopology.Triangles, 6, Span * Span); + _draw.SetRenderTarget(new RenderTargetIdentifier[] + { + BuiltinRenderTextureType.GBuffer0, BuiltinRenderTextureType.GBuffer1, BuiltinRenderTextureType.GBuffer2, + BuiltinRenderTextureType.CameraTarget + }, BuiltinRenderTextureType.CameraTarget); + _draw.DrawProcedural(Matrix4x4.identity, ParticleMaterial, ParticleMaterial.FindPass("DEFERRED"), MeshTopology.Triangles, 6, Span * Span); AttachDraw(); } diff --git a/Assets/Shaders/Compute/Stardust/Stardust.shader b/Assets/Shaders/Compute/Stardust/Stardust.shader index 86acf8c43..4ed3e9d9f 100644 --- a/Assets/Shaders/Compute/Stardust/Stardust.shader +++ b/Assets/Shaders/Compute/Stardust/Stardust.shader @@ -83,6 +83,104 @@ float2 screenUV = i.screenPos.xy / i.screenPos.w; ditherClip(screenUV, emission * i.color.a); return i.color * emission * (exp(_EmissionGain * 5.0f)); + } + ENDCG + } + Pass { + Name "DEFERRED" + Tags { "LightMode" = "Deferred" } + + CGPROGRAM + #pragma vertex particle_vertex + #pragma fragment frag + + #pragma target 5.0 + #pragma multi_compile ___ UNITY_HDR_ON + #include "UnityCG.cginc" + #include "Assets/Shaders/Dither Functions.cginc" + + struct Particle + { + float3 position; + float3 color; + float size; + }; + + StructuredBuffer particles; + StructuredBuffer quadPoints; + + float4 _TintColor; + float _EmissionGain; + float _Power; + float _DistanceSizeExponent; + float _DistanceIntensityExponent; + + struct v2f + { + float4 pos : SV_POSITION; + float2 uv : TEXCOORD0; + float4 screenPos : TEXCOORD1; + float4 color : COLOR; + }; + + //Vertex shader with no inputs + //Uses the system values SV_VertexID and SV_InstanceID to read from compute buffers + v2f particle_vertex(uint id : SV_VertexID, uint inst : SV_InstanceID) + { + v2f o; + + //Only transform world pos by view matrix + //To Create a billboarding effect + float3 worldPosition = particles[inst].position; + float dist = length(worldPosition - _WorldSpaceCameraPos); + + float4 cameraSpacePosition = mul(UNITY_MATRIX_V, float4(worldPosition, 1.0f)); + //cameraSpacePosition.w /= dist; + + float3 quadPoint = float3(quadPoints[id].xy, 0.0f) * particles[inst].size * pow(dist, _DistanceSizeExponent) / pow(100, _DistanceSizeExponent); + o.pos = mul(UNITY_MATRIX_P, cameraSpacePosition + float4(quadPoint, 0.0f)); + + //Shift coordinates for uvs + o.uv = quadPoints[id] + 0.5f; + + //transfer color of particle and global tint to vertex + o.color = float4(particles[inst].color * _TintColor.rgb, 100 * pow(100, _DistanceIntensityExponent) / pow(dist, _DistanceIntensityExponent)); + o.screenPos = ComputeScreenPos(o.pos); + + return o; + } + + float powerPulse( float x, float power ) + { + x = saturate(abs(x)); + return pow((x + 1.0f) * (1.0f - x), power); + } + + // The dust in the G-buffer (Stardust.cs DrawEvent): black, unreflective, emissive, so deferred lighting adds nothing and + // its depth is in the depth texture the nebula march fogs by. + struct GBufferOut + { + half4 albedo : SV_Target0; + half4 specular : SV_Target1; + half4 normal : SV_Target2; + half4 emission : SV_Target3; + }; + + GBufferOut frag(v2f i) + { + float emission = powerPulse(length(i.uv-float2(.5,.5))*2,_Power); + float2 screenUV = i.screenPos.xy / i.screenPos.w; + ditherClip(screenUV, emission * i.color.a); + GBufferOut o; + o.albedo = 0; + o.specular = 0; + o.normal = half4(.5, 1, .5, 1); + float3 light = (i.color * emission * (exp(_EmissionGain * 5.0f))).rgb; + #ifndef UNITY_HDR_ON + light = exp2(-light); + #endif + o.emission = half4(light, 1); + return o; } ENDCG }