diff --git a/Content/Shaders/ColorGrading.flax b/Content/Shaders/ColorGrading.flax index eef24db35..b89d4b22b 100644 --- a/Content/Shaders/ColorGrading.flax +++ b/Content/Shaders/ColorGrading.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e25c173644fa6e459791e1222514b77d71847bcc1d063accbf5e94e16d83527f -size 11057 +oid sha256:ae31d654f71cd691f66cd140fd066ad2cf8a31d984f62f411173972471b9022b +size 10987 diff --git a/Content/Shaders/DepthOfField.flax b/Content/Shaders/DepthOfField.flax index 5acc84c3e..a0831c322 100644 --- a/Content/Shaders/DepthOfField.flax +++ b/Content/Shaders/DepthOfField.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f4fe6201d9c205835e9a16f05fd3e95400854ea162bce8b1473037e52c7cfd69 -size 21043 +oid sha256:bea89bc4545dd7268448f3a66b7176801759744eb1f625e8c036fdd064fa4cfe +size 21025 diff --git a/Content/Shaders/Fog.flax b/Content/Shaders/Fog.flax index 126388165..f3376447a 100644 --- a/Content/Shaders/Fog.flax +++ b/Content/Shaders/Fog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:17bb8171cdafe227429d694733060a5ce7c647a48da9d6f4215f336ada03dc56 -size 2943 +oid sha256:574afd4d29bf42d17dfbc3da1b9841c1bfac8e8f4f05ff2b1ca9658eb5b8d159 +size 2878 diff --git a/Content/Shaders/ProbesFilter.flax b/Content/Shaders/ProbesFilter.flax index c95b5aac3..91fe4a08b 100644 --- a/Content/Shaders/ProbesFilter.flax +++ b/Content/Shaders/ProbesFilter.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:24e0095be6046f8cd05ed79f25c6b90b8c257420b686e050fdcedb6b210d963c -size 4999 +oid sha256:78d5d554e4dc59fdb9bcfef7c4ad0ee96cebb79b67e3e443a5a580e4e664135a +size 4980 diff --git a/Content/Shaders/VolumetricFog.flax b/Content/Shaders/VolumetricFog.flax index e62d7f4c1..4cd23cb0c 100644 --- a/Content/Shaders/VolumetricFog.flax +++ b/Content/Shaders/VolumetricFog.flax @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:daa92cd2b9f3dbc55923cf92561d6d91ab8f5d14dec183f418e03932d09f26b3 -size 15820 +oid sha256:6e278259855304359f5cf5d4860c5d473eaae4ae20cdfe73a8b311fdfc00b1aa +size 14492 diff --git a/Source/Shaders/BRDF.hlsl b/Source/Shaders/BRDF.hlsl index 4b1f0389a..47a641b16 100644 --- a/Source/Shaders/BRDF.hlsl +++ b/Source/Shaders/BRDF.hlsl @@ -25,9 +25,9 @@ float D_GGX(float roughness, float NoH) float Vis_Schlick(float roughness, float NoV, float NoL) { float k = Square(roughness) * 0.5; - float vis_SchlickV = NoV * (1 - k) + k; - float vis_SchlickL = NoL * (1 - k) + k; - return 0.25 / (vis_SchlickV * vis_SchlickL); + float visSchlickV = NoV * (1 - k) + k; + float visSchlickL = NoL * (1 - k) + k; + return 0.25 / (visSchlickV * visSchlickL); } // Smith term for GGX @@ -36,9 +36,9 @@ float Vis_Smith(float roughness, float NoV, float NoL) { float a = Square(roughness); float a2 = a * a; - float vis_SmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2); - float vis_SmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2); - return rcp(vis_SmithV * vis_SmithL); + float visSmithV = NoV + sqrt(NoV * (NoV - NoV * a2) + a2); + float visSmithL = NoL + sqrt(NoL * (NoL - NoL * a2) + a2); + return rcp(visSmithV * visSmithL); } // Appoximation of joint Smith term for GGX @@ -74,7 +74,7 @@ half SSRMipFromRoughness(half roughness) return max(1, 10 - mip1px); } -float ComputeReflectionCaptureRoughnessFromMip(float mip) +float ProbeRoughnessFromMip(float mip) { float mip1px = REFLECTION_CAPTURE_NUM_MIPS - 1 - mip; return exp2((REFLECTION_CAPTURE_ROUGHEST_MIP - mip1px) / REFLECTION_CAPTURE_ROUGHNESS_MIP_SCALE); @@ -99,8 +99,6 @@ float3 EnvBRDF(Texture2D preIntegratedGF, float3 specularColor, float roughness, return specularColor * ab.x + saturate(50.0 * specularColor.g) * ab.y; } -#define MAX_SPECULAR_POWER 10000000000.0f - float RoughnessToSpecularPower(float roughness) { return pow(2, 13 * (1 - roughness)); diff --git a/Source/Shaders/ColorGrading.shader b/Source/Shaders/ColorGrading.shader index 51015658c..9a811cb0b 100644 --- a/Source/Shaders/ColorGrading.shader +++ b/Source/Shaders/ColorGrading.shader @@ -264,37 +264,33 @@ META_VS(true, FEATURE_LEVEL_SM4) META_FLAG(VertexToGeometryShader) META_VS_IN_ELEMENT(POSITION, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) META_VS_IN_ELEMENT(TEXCOORD, 0, R32G32_FLOAT, 0, ALIGN, PER_VERTEX, 0, true) -Quad_VS2GS VS_WriteToSlice(float2 Position : POSITION0, float2 TexCoord : TEXCOORD0, uint LayerIndex : SV_InstanceID) +Quad_VS2GS VS_WriteToSlice(float2 position : POSITION0, float2 texCoord : TEXCOORD0, uint layerIndex : SV_InstanceID) { Quad_VS2GS output; - - output.Vertex.Position = float4(Position, 0, 1); - output.Vertex.TexCoord = TexCoord; - output.LayerIndex = LayerIndex; - + output.Vertex.Position = float4(position, 0, 1); + output.Vertex.TexCoord = texCoord; + output.LayerIndex = layerIndex; return output; } // Geometry shader that writes to a range of slices of a volume texture META_GS(true, FEATURE_LEVEL_SM4) [maxvertexcount(3)] -void GS_WriteToSlice(triangle Quad_VS2GS input[3], inout TriangleStream OutStream) +void GS_WriteToSlice(triangle Quad_VS2GS input[3], inout TriangleStream stream) { - Quad_GS2PS vertex0; - vertex0.Vertex = input[0].Vertex; - vertex0.LayerIndex = input[0].LayerIndex; + Quad_GS2PS vertex; - Quad_GS2PS vertex1; - vertex1.Vertex = input[1].Vertex; - vertex1.LayerIndex = input[1].LayerIndex; + vertex.Vertex = input[0].Vertex; + vertex.LayerIndex = input[0].LayerIndex; + stream.Append(vertex); - Quad_GS2PS vertex2; - vertex2.Vertex = input[2].Vertex; - vertex2.LayerIndex = input[2].LayerIndex; + vertex.Vertex = input[1].Vertex; + vertex.LayerIndex = input[1].LayerIndex; + stream.Append(vertex); - OutStream.Append(vertex0); - OutStream.Append(vertex1); - OutStream.Append(vertex2); + vertex.Vertex = input[2].Vertex; + vertex.LayerIndex = input[2].LayerIndex; + stream.Append(vertex); } META_PS(true, FEATURE_LEVEL_ES2) diff --git a/Source/Shaders/DepthOfField.shader b/Source/Shaders/DepthOfField.shader index 1855e728c..062f17c5b 100644 --- a/Source/Shaders/DepthOfField.shader +++ b/Source/Shaders/DepthOfField.shader @@ -571,9 +571,9 @@ StructuredBuffer BokehPointBuffer : register(t2); // Vertex Shader, positions and scales the bokeh point META_VS(true, FEATURE_LEVEL_SM5) META_FLAG(VertexToGeometryShader) -BokehVSOutput VS_Bokeh(in uint VertexID : SV_VertexID) +BokehVSOutput VS_Bokeh(in uint vertexID : SV_VertexID) { - BokehPoint bPoint = BokehPointBuffer[VertexID]; + BokehPoint bPoint = BokehPointBuffer[vertexID]; BokehVSOutput output; // Position the vertex in normalized device coordinate space [-1, 1] @@ -599,7 +599,7 @@ BokehVSOutput VS_Bokeh(in uint VertexID : SV_VertexID) // Geometry Shader, expands a vertex into a quad with two triangles META_GS(true, FEATURE_LEVEL_SM5) [maxvertexcount(4)] -void GS_Bokeh(point BokehVSOutput input[1], inout TriangleStream SpriteStream) +void GS_Bokeh(point BokehVSOutput input[1], inout TriangleStream stream) { BokehGSOutput output; @@ -613,9 +613,9 @@ void GS_Bokeh(point BokehVSOutput input[1], inout TriangleStream output.Color = input[0].Color; output.Depth = input[0].Depth; - SpriteStream.Append(output); + stream.Append(output); } - SpriteStream.RestartStrip(); + stream.RestartStrip(); } // Pixel Shader, applies the bokeh shape texture diff --git a/Source/Shaders/ExponentialHeightFog.hlsl b/Source/Shaders/ExponentialHeightFog.hlsl index cf085d7fe..9727380f1 100644 --- a/Source/Shaders/ExponentialHeightFog.hlsl +++ b/Source/Shaders/ExponentialHeightFog.hlsl @@ -29,29 +29,29 @@ struct ExponentialHeightFogData float StartDistance; }; -half4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 worldPosition, float3 cameraPosition, float excludeDistance) +float4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, float3 posWS, float3 camWS, float skipDistance) { - float3 cameraToReceiver = worldPosition - cameraPosition; - float cameraToReceiverLengthSqr = dot(cameraToReceiver, cameraToReceiver); - float cameraToReceiverLengthInv = rsqrt(cameraToReceiverLengthSqr); - float cameraToReceiverLength = cameraToReceiverLengthSqr * cameraToReceiverLengthInv; - half3 cameraToReceiverNormalized = cameraToReceiver * cameraToReceiverLengthInv; + float3 cameraToPos = posWS - camWS; + float cameraToPosSqr = dot(cameraToPos, cameraToPos); + float cameraToPosLenInv = rsqrt(cameraToPosSqr); + float cameraToPosLen = cameraToPosSqr * cameraToPosLenInv; + float3 cameraToReceiverNorm = cameraToPos * cameraToPosLenInv; float rayOriginTerms = exponentialHeightFog.FogAtViewPosition; - float rayLength = cameraToReceiverLength; - float rayDirectionY = cameraToReceiver.y; + float rayLength = cameraToPosLen; + float rayDirectionY = cameraToPos.y; // Apply start distance offset - excludeDistance = max(excludeDistance, exponentialHeightFog.StartDistance); - if (excludeDistance > 0) + skipDistance = max(skipDistance, exponentialHeightFog.StartDistance); + if (skipDistance > 0) { - float excludeIntersectionTime = excludeDistance * cameraToReceiverLengthInv; - float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToReceiver.y; - float exclusionIntersectionY = cameraPosition.y + cameraToExclusionIntersectionY; - float exclusionIntersectionToReceiverY = cameraToReceiver.y - cameraToExclusionIntersectionY; + float excludeIntersectionTime = skipDistance * cameraToPosLenInv; + float cameraToExclusionIntersectionY = excludeIntersectionTime * cameraToPos.y; + float exclusionIntersectionY = camWS.y + cameraToExclusionIntersectionY; + float exclusionIntersectionToReceiverY = cameraToPos.y - cameraToExclusionIntersectionY; // Calculate fog off of the ray starting from the exclusion distance, instead of starting from the camera - rayLength = (1.0f - excludeIntersectionTime) * cameraToReceiverLength; + rayLength = (1.0f - excludeIntersectionTime) * cameraToPosLen; rayDirectionY = exclusionIntersectionToReceiverY; // Move off the viewer @@ -67,22 +67,22 @@ half4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, flo float exponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * rayLength; // Calculate the amount of light that made it through the fog using the transmission equation - half expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity); + float expFogFactor = max(saturate(exp2(-exponentialHeightLineIntegral)), exponentialHeightFog.FogMinOpacity); // Calculate the directional light inscattering - half3 inscatteringColor = exponentialHeightFog.FogInscatteringColor; - half3 directionalInscattering = 0; + float3 inscatteringColor = exponentialHeightFog.FogInscatteringColor; + float3 directionalInscattering = 0; BRANCH if (exponentialHeightFog.ApplyDirectionalInscattering > 0) { // Setup a cosine lobe around the light direction to approximate inscattering from the directional light off of the ambient haze - half3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNormalized, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent); + float3 directionalLightInscattering = exponentialHeightFog.DirectionalInscatteringColor * pow(saturate(dot(cameraToReceiverNorm, exponentialHeightFog.InscatteringLightDirection)), exponentialHeightFog.DirectionalInscatteringExponent); // Calculate the line integral of the eye ray through the haze, using a special starting distance to limit the inscattering to the distance float dirExponentialHeightLineIntegral = exponentialHeightLineIntegralCalc * max(rayLength - exponentialHeightFog.DirectionalInscatteringStartDistance, 0.0f); // Calculate the amount of light that made it through the fog using the transmission equation - half directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral)); + float directionalInscatteringFogFactor = saturate(exp2(-dirExponentialHeightLineIntegral)); // Final inscattering from the light directionalInscattering = directionalLightInscattering * (1 - directionalInscatteringFogFactor); @@ -90,13 +90,13 @@ half4 GetExponentialHeightFog(ExponentialHeightFogData exponentialHeightFog, flo // Disable fog after a certain distance FLATTEN - if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToReceiverLength > exponentialHeightFog.FogCutoffDistance) + if (exponentialHeightFog.FogCutoffDistance > 0 && cameraToPosLen > exponentialHeightFog.FogCutoffDistance) { expFogFactor = 1; directionalInscattering = 0; } - return half4((inscatteringColor) * (1 - expFogFactor) + directionalInscattering, expFogFactor); + return float4(inscatteringColor * (1.0f - expFogFactor) + directionalInscattering, expFogFactor); } #endif diff --git a/Source/Shaders/Fog.shader b/Source/Shaders/Fog.shader index 7ddfe88ae..93bcac24f 100644 --- a/Source/Shaders/Fog.shader +++ b/Source/Shaders/Fog.shader @@ -33,15 +33,15 @@ float3 GetWorldPos(float2 uv) return mul(float4(viewPos, 1), gBufferData.InvViewMatrix).xyz; } -float4 CalculateCombinedFog(float3 worldPosition, float sceneDepth, float3 volumeUV) +float4 CalculateCombinedFog(float3 posWS, float sceneDepth, float3 volumeUV) { - float excludeDistance = 0; + float skipDistance = 0; #if VOLUMETRIC_FOG - excludeDistance = max(ExponentialHeightFog.VolumetricFogMaxDistance - 100, 0); + skipDistance = max(ExponentialHeightFog.VolumetricFogMaxDistance - 100, 0); #endif - float4 fog = GetExponentialHeightFog(ExponentialHeightFog, worldPosition, GBuffer.ViewPos, excludeDistance); + float4 fog = GetExponentialHeightFog(ExponentialHeightFog, posWS, GBuffer.ViewPos, skipDistance); #if VOLUMETRIC_FOG float4 volumetricFog = IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0); @@ -57,8 +57,8 @@ META_PERMUTATION_1(VOLUMETRIC_FOG=1) float4 PS_Fog(Quad_VS2PS input) : SV_Target0 { // Calculate pixel world space position - float3 worldPosition = GetWorldPos(input.TexCoord); - float3 viewVector = worldPosition - GBuffer.ViewPos; + float3 posWS = GetWorldPos(input.TexCoord); + float3 viewVector = posWS - GBuffer.ViewPos; float sceneDepth = length(viewVector); // Calculate volumetric fog coordinates @@ -67,17 +67,17 @@ float4 PS_Fog(Quad_VS2PS input) : SV_Target0 // Debug code #if VOLUMETRIC_FOG && 0 - volumeUV = worldPosition / 1000; + volumeUV = posWS / 1000; if (!all(volumeUV >= 0 && volumeUV <= 1)) return 0; return float4(IntegratedLightScattering.SampleLevel(SamplerLinearClamp, volumeUV, 0).rgb, 1); //return float4(volumeUV, 1); - //return float4(worldPosition / 100, 1); + //return float4(posWS / 100, 1); #endif // Calculate fog color - float4 fog = CalculateCombinedFog(worldPosition, sceneDepth, volumeUV); + float4 fog = CalculateCombinedFog(posWS, sceneDepth, volumeUV); return fog; } diff --git a/Source/Shaders/ProbesFilter.shader b/Source/Shaders/ProbesFilter.shader index 8ff0f8536..b8daca0db 100644 --- a/Source/Shaders/ProbesFilter.shader +++ b/Source/Shaders/ProbesFilter.shader @@ -65,7 +65,7 @@ float4 PS_FilterFace(Quad_VS2PS input) : SV_Target #define NUM_FILTER_SAMPLES 512 float3 N = normalize(cubeCoordinates); - float roughness = ComputeReflectionCaptureRoughnessFromMip(SourceMipIndex); + float roughness = ProbeRoughnessFromMip(SourceMipIndex); float4 filteredColor = 0; float weight = 0; diff --git a/Source/Shaders/VolumetricFog.shader b/Source/Shaders/VolumetricFog.shader index cc07de293..0b1fdb650 100644 --- a/Source/Shaders/VolumetricFog.shader +++ b/Source/Shaders/VolumetricFog.shader @@ -61,7 +61,7 @@ META_CB_END META_CB_BEGIN(1, PerLight) float2 Dummy1; -int MinZ; // Z index of the minimum slice in the range +int MinZ; float LocalLightScatteringIntensity; float4 ViewSpaceBoundingSphere; @@ -72,48 +72,44 @@ LightShadowData LocalLightShadow; META_CB_END -float ComputeDepthFromZSlice(float zSlice) -{ - return (zSlice / GridSize.z) * VolumetricFogMaxDistance; -} - -float3 ComputeCellWorldPosition(uint3 gridCoordinate, float3 cellOffset, out float sceneDepth) -{ - float2 volumeUV = (gridCoordinate.xy + cellOffset.xy) / GridSize.xy; - sceneDepth = ComputeDepthFromZSlice(gridCoordinate.z + cellOffset.z) / GBuffer.ViewFar; - float deviceDepth = LinearZ2DeviceDepth(GBuffer, sceneDepth); - return GetWorldPos(GBuffer, volumeUV, deviceDepth); -} - -float3 ComputeCellWorldPosition(uint3 gridCoordinate, float3 cellOffset) -{ - float unused; - return ComputeCellWorldPosition(gridCoordinate, cellOffset, unused); -} - -float ComputeNormalizedZSliceFromDepth(float sceneDepth) -{ - return sceneDepth / VolumetricFogMaxDistance; -} - -float3 ComputeVolumeUV(float3 worldPosition, float4x4 worldToClip) -{ - float4 ndcPosition = mul(float4(worldPosition, 1), worldToClip); - ndcPosition.xy /= ndcPosition.w; - return float3(ndcPosition.xy * float2(0.5f, -0.5f) + 0.5f, ComputeNormalizedZSliceFromDepth(ndcPosition.w)); -} - +// The Henyey-Greenstein phase function +// [Henyey and Greenstein 1941, https://www.astro.umd.edu/~jph/HG_note.pdf] float HenyeyGreensteinPhase(float g, float cosTheta) { return (1 - g * g) / (4 * PI * pow(1 + g * g + 2 * g * cosTheta, 1.5f)); } -// +g = forward scattering, 0=g = isotropic, -g = backward scattering -float PhaseFunction(float g, float cosTheta) +float GetPhase(float g, float cosTheta) { return HenyeyGreensteinPhase(g, cosTheta); } +float GetSliceDepth(float zSlice) +{ + return (zSlice / GridSize.z) * VolumetricFogMaxDistance; +} + +float3 GetCellPositionWS(uint3 gridCoordinate, float3 cellOffset, out float sceneDepth) +{ + float2 volumeUV = (gridCoordinate.xy + cellOffset.xy) / GridSize.xy; + sceneDepth = GetSliceDepth(gridCoordinate.z + cellOffset.z) / GBuffer.ViewFar; + float deviceDepth = LinearZ2DeviceDepth(GBuffer, sceneDepth); + return GetWorldPos(GBuffer, volumeUV, deviceDepth); +} + +float3 GetCellPositionWS(uint3 gridCoordinate, float3 cellOffset) +{ + float temp; + return GetCellPositionWS(gridCoordinate, cellOffset, temp); +} + +float3 GetVolumeUV(float3 worldPosition, float4x4 worldToClip) +{ + float4 ndcPosition = mul(float4(worldPosition, 1), worldToClip); + ndcPosition.xy /= ndcPosition.w; + return float3(ndcPosition.xy * float2(0.5f, -0.5f) + 0.5f, ndcPosition.w / VolumetricFogMaxDistance); +} + // Vertex shader that writes to a range of slices of a volume texture META_VS(true, FEATURE_LEVEL_SM5) META_FLAG(VertexToGeometryShader) @@ -123,22 +119,18 @@ Quad_VS2GS VS_WriteToSlice(float2 TexCoord : TEXCOORD0, uint LayerIndex : SV_Ins Quad_VS2GS output; uint slice = LayerIndex + MinZ; - float sliceDepth = ComputeDepthFromZSlice(slice); - float sliceDepthOffset = abs(sliceDepth - ViewSpaceBoundingSphere.z); + float depth = GetSliceDepth(slice); + float depthOffset = abs(depth - ViewSpaceBoundingSphere.z); - if (sliceDepthOffset < ViewSpaceBoundingSphere.w) + if (depthOffset < ViewSpaceBoundingSphere.w) { - // Compute the radius of the circle formed by the intersection of the bounding sphere and the current depth slice - float sliceRadius = sqrt(ViewSpaceBoundingSphere.w * ViewSpaceBoundingSphere.w - sliceDepthOffset * sliceDepthOffset); - - // Place the quad vertex to tightly bound the circle - float3 viewSpaceVertexPosition = float3(ViewSpaceBoundingSphere.xy + (TexCoord * 2 - 1) * sliceRadius, sliceDepth); - output.Vertex.Position = mul(float4(viewSpaceVertexPosition, 1), ViewToVolumeClip); + float radius = sqrt(ViewSpaceBoundingSphere.w * ViewSpaceBoundingSphere.w - depthOffset * depthOffset); + float3 positionVS = float3(ViewSpaceBoundingSphere.xy + (TexCoord * 2 - 1) * radius, depth); + output.Vertex.Position = mul(float4(positionVS, 1), ViewToVolumeClip); } else { - // Slice does not intersect bounding sphere, emit degenerate triangle - output.Vertex.Position = 0; + output.Vertex.Position = float4(0, 0, 0, 0); } output.Vertex.TexCoord = 0; @@ -150,23 +142,21 @@ Quad_VS2GS VS_WriteToSlice(float2 TexCoord : TEXCOORD0, uint LayerIndex : SV_Ins // Geometry shader that writes to a range of slices of a volume texture META_GS(true, FEATURE_LEVEL_SM5) [maxvertexcount(3)] -void GS_WriteToSlice(triangle Quad_VS2GS input[3], inout TriangleStream OutStream) +void GS_WriteToSlice(triangle Quad_VS2GS input[3], inout TriangleStream stream) { - Quad_GS2PS vertex0; - vertex0.Vertex = input[0].Vertex; - vertex0.LayerIndex = input[0].LayerIndex; + Quad_GS2PS vertex; - Quad_GS2PS vertex1; - vertex1.Vertex = input[1].Vertex; - vertex1.LayerIndex = input[1].LayerIndex; + vertex.Vertex = input[0].Vertex; + vertex.LayerIndex = input[0].LayerIndex; + stream.Append(vertex); - Quad_GS2PS vertex2; - vertex2.Vertex = input[2].Vertex; - vertex2.LayerIndex = input[2].LayerIndex; + vertex.Vertex = input[1].Vertex; + vertex.LayerIndex = input[1].LayerIndex; + stream.Append(vertex); - OutStream.Append(vertex0); - OutStream.Append(vertex1); - OutStream.Append(vertex2); + vertex.Vertex = input[2].Vertex; + vertex.LayerIndex = input[2].LayerIndex; + stream.Append(vertex); } #if USE_SHADOW @@ -208,18 +198,16 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0 return 0; #if USE_TEMPORAL_REPROJECTION - float3 historyUV = ComputeVolumeUV(ComputeCellWorldPosition(gridCoordinate, 0.5f), PrevWorldToClip); + float3 historyUV = GetVolumeUV(GetCellPositionWS(gridCoordinate, 0.5f), PrevWorldToClip); float historyAlpha = HistoryWeight; - FLATTEN if (any(historyUV < 0) || any(historyUV > 1)) { historyAlpha = 0; } - - uint numSuperSamples = historyAlpha < .001f ? HistoryMissSuperSampleCount : 1; + uint samplesCount = historyAlpha < 0.001f ? HistoryMissSuperSampleCount : 1; #else - uint numSuperSamples = 1; + uint samplesCount = 1; #endif float3 L = 0; @@ -229,22 +217,19 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0 float lightRadiusMask = 1; float spotAttenuation = 1; bool isSpotLight = LocalLight.SpotAngles.x > -2.0f; - float4 scattering = 0; - for (uint sampleIndex = 0; sampleIndex < numSuperSamples; sampleIndex++) + for (uint sampleIndex = 0; sampleIndex < samplesCount; sampleIndex++) { float3 cellOffset = FrameJitterOffsets[sampleIndex].xyz; //float cellOffset = 0.5f; - float3 worldPosition = ComputeCellWorldPosition(gridCoordinate, cellOffset); - float3 cameraVector = normalize(worldPosition - GBuffer.ViewPos); - float cellRadius = length(worldPosition - ComputeCellWorldPosition(gridCoordinate + uint3(1, 1, 1), cellOffset)); - - // Bias the inverse squared light falloff based on voxel size to prevent aliasing near the light source + float3 positionWS = GetCellPositionWS(gridCoordinate, cellOffset); + float3 cameraVector = normalize(positionWS - GBuffer.ViewPos); + float cellRadius = length(positionWS - GetCellPositionWS(gridCoordinate + uint3(1, 1, 1), cellOffset)); float distanceBias = max(cellRadius * InverseSquaredLightDistanceBiasScale, 1); - // Get the light attenuation - GetRadialLightAttenuation(LocalLight, isSpotLight, worldPosition, float3(0, 0, 1), distanceBias * distanceBias, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation); + // Calculate the light attenuation + GetRadialLightAttenuation(LocalLight, isSpotLight, positionWS, float3(0, 0, 1), distanceBias * distanceBias, toLight, L, NoL, distanceAttenuation, lightRadiusMask, spotAttenuation); float combinedAttenuation = distanceAttenuation * lightRadiusMask * spotAttenuation; // Peek the shadow @@ -252,16 +237,14 @@ float4 PS_InjectLight(Quad_GS2PS input) : SV_Target0 #if USE_SHADOW if (combinedAttenuation > 0) { - shadowFactor = ComputeVolumeShadowing(worldPosition, isSpotLight); + shadowFactor = ComputeVolumeShadowing(positionWS, isSpotLight); } #endif - scattering.rgb += LocalLight.Color * (PhaseFunction(PhaseG, dot(L, -cameraVector)) * combinedAttenuation * shadowFactor * LocalLightScatteringIntensity); + scattering.rgb += LocalLight.Color * (GetPhase(PhaseG, dot(L, -cameraVector)) * combinedAttenuation * shadowFactor * LocalLightScatteringIntensity); } - // Normalize - scattering.rgb /= (float)numSuperSamples; - + scattering.rgb /= (float)samplesCount; return scattering; } @@ -280,7 +263,7 @@ void CS_Initialize(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Dispa // Center of the voxel float voxelOffset = 0.5f; - float3 worldPosition = ComputeCellWorldPosition(gridCoordinate, voxelOffset); + float3 positionWS = GetCellPositionWS(gridCoordinate, voxelOffset); // Unpack the fog parameters (packing done in C++ ExponentialHeightFog::GetVolumetricFogOptions) float fogDensity = FogParameters.x; @@ -288,7 +271,7 @@ void CS_Initialize(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_Dispa float fogHeightFalloff = FogParameters.z; // Calculate the global fog density that matches the exponential height fog density - float globalDensity = fogDensity * exp2(-fogHeightFalloff * (worldPosition.y - fogHeight)); + float globalDensity = fogDensity * exp2(-fogHeightFalloff * (positionWS.y - fogHeight)); float matchFactor = 0.24f; float extinction = max(globalDensity * GlobalExtinctionScale * matchFactor, 0); @@ -326,7 +309,7 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_ uint numSuperSamples = 1; #if USE_TEMPORAL_REPROJECTION - float3 historyUV = ComputeVolumeUV(ComputeCellWorldPosition(gridCoordinate, 0.5f), PrevWorldToClip); + float3 historyUV = GetVolumeUV(GetCellPositionWS(gridCoordinate, 0.5f), PrevWorldToClip); float historyAlpha = HistoryWeight; // Discard history if it lays outside the current view @@ -347,8 +330,8 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_ //float3 cellOffset = 0.5f; float sceneDepth; - float3 worldPosition = ComputeCellWorldPosition(gridCoordinate, cellOffset, sceneDepth); - float3 cameraVector = worldPosition - GBuffer.ViewPos; + float3 positionWS = GetCellPositionWS(gridCoordinate, cellOffset, sceneDepth); + float3 cameraVector = positionWS - GBuffer.ViewPos; float cameraVectorLength = length(cameraVector); float3 cameraVectorNormalized = cameraVector / cameraVectorLength; @@ -360,10 +343,10 @@ void CS_LightScattering(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_ float shadow = 1; if (DirectionalLightShadow.NumCascades > 0) { - shadow = SampleShadow(DirectionalLight, DirectionalLightShadow, ShadowMapCSM, worldPosition, cameraVectorLength); + shadow = SampleShadow(DirectionalLight, DirectionalLightShadow, ShadowMapCSM, positionWS, cameraVectorLength); } - lightScattering += DirectionalLight.Color * (8 * shadow * PhaseFunction(PhaseG, dot(DirectionalLight.Direction, cameraVectorNormalized))); + lightScattering += DirectionalLight.Color * (8 * shadow * GetPhase(PhaseG, dot(DirectionalLight.Direction, cameraVectorNormalized))); } // Sky light @@ -414,38 +397,30 @@ META_CS(true, FEATURE_LEVEL_SM5) void CS_FinalIntegration(uint3 GroupId : SV_GroupID, uint3 DispatchThreadId : SV_DispatchThreadID, uint3 GroupThreadId : SV_GroupThreadID) { uint3 gridCoordinate = DispatchThreadId; - - float4 accumulatedLightingAndTransmittance = float4(0, 0, 0, 1); - float3 previousSliceWorldPosition = GBuffer.ViewPos; + float4 acc = float4(0, 0, 0, 1); + float3 prevPositionWS = GBuffer.ViewPos; for (uint layerIndex = 0; layerIndex < GridSizeInt.z; layerIndex++) { - uint3 layerCoordinate = uint3(gridCoordinate.xy, layerIndex); - float4 scatteringAndExtinction = LightScattering[layerCoordinate]; - - float3 layerWorldPosition = ComputeCellWorldPosition(layerCoordinate, 0.5f); - float stepLength = length(layerWorldPosition - previousSliceWorldPosition); - previousSliceWorldPosition = layerWorldPosition; - - float transmittance = exp(-scatteringAndExtinction.w * stepLength); + uint3 coords = uint3(gridCoordinate.xy, layerIndex); + float4 scatteringExtinction = LightScattering[coords]; + float3 positionWS = GetCellPositionWS(coords, 0.5f); // Ref: "Physically Based and Unified Volumetric Rendering in Frostbite" -#define ENERGY_CONSERVING_INTEGRATION 1 -#if ENERGY_CONSERVING_INTEGRATION - float3 scatteringIntegratedOverSlice = (scatteringAndExtinction.rgb - scatteringAndExtinction.rgb * transmittance) / max(scatteringAndExtinction.w, .00001f); - accumulatedLightingAndTransmittance.rgb += scatteringIntegratedOverSlice * accumulatedLightingAndTransmittance.a; -#else - accumulatedLightingAndTransmittance.rgb += scatteringAndExtinction.rgb * accumulatedLightingAndTransmittance.a; -#endif - accumulatedLightingAndTransmittance.a *= transmittance; - + float transmittance = exp(-scatteringExtinction.w * length(positionWS - prevPositionWS)); + float3 scatteringIntegratedOverSlice = (scatteringExtinction.rgb - scatteringExtinction.rgb * transmittance) / max(scatteringExtinction.w, 0.00001f); + acc.rgb += scatteringIntegratedOverSlice * acc.a; + acc.a *= transmittance; + #if DEBUG_VOXELS - RWIntegratedLightScattering[layerCoordinate] = float4(scatteringAndExtinction.rgb, 1.0f); + RWIntegratedLightScattering[coords] = float4(scatteringExtinction.rgb, 1.0f); #elif DEBUG_VOXEL_WS_POS - RWIntegratedLightScattering[layerCoordinate] = float4(layerWorldPosition.rgb, 1.0f); + RWIntegratedLightScattering[coords] = float4(positionWS.rgb, 1.0f); #else - RWIntegratedLightScattering[layerCoordinate] = accumulatedLightingAndTransmittance; + RWIntegratedLightScattering[coords] = acc; #endif + + prevPositionWS = positionWS; } }