From e18e49a9f7c799aaa93ebc63f567ab9a3bff708b Mon Sep 17 00:00:00 2001 From: Anis Benyoub Date: Wed, 8 Sep 2021 19:13:12 +0200 Subject: [PATCH] Added an option for the ultra 1024x1024 mode for the volumetric cloud shadows and a filtering pass to reduce the aliasing artifacts of the clouds shadow. Changed the number of steps to evaluate the shadow from 9 to 16. --- .../CHANGELOG.md | 4 ++ ...DRenderPipeline.VolumetricCloudsShadows.cs | 37 ++++++++-- .../VolumetricClouds.compute | 72 ++++++++++++++++++- .../VolumetricLighting/VolumetricClouds.cs | 4 +- .../RenderPipeline/HDStringConstants.cs | 1 + 5 files changed, 109 insertions(+), 9 deletions(-) diff --git a/com.unity.render-pipelines.high-definition/CHANGELOG.md b/com.unity.render-pipelines.high-definition/CHANGELOG.md index 97d43ebb61e..f31a74d13e0 100644 --- a/com.unity.render-pipelines.high-definition/CHANGELOG.md +++ b/com.unity.render-pipelines.high-definition/CHANGELOG.md @@ -17,6 +17,10 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ### changed - Visual Environment ambient mode is now Dynamic by default. +- Changed the number of steps to evaluate the shadow from 9 to 16. + +### Added +- Added an option for the ultra 1024x1024 mode for the volumetric cloud shadows and a filtering pass to reduce the aliasing artifacts of the clouds shadow. ## [12.0.0] - 2021-01-11 diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricCloudsShadows.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricCloudsShadows.cs index ecd67027d88..521dbf64dc7 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricCloudsShadows.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/HDRenderPipeline.VolumetricCloudsShadows.cs @@ -7,23 +7,25 @@ public partial class HDRenderPipeline { // Cloud preset maps RTHandle[] m_VolumetricCloudsShadowTexture = new RTHandle[VolumetricClouds.CloudShadowResolutionCount]; + RTHandle m_VolumetricCloudsIntermediateShadowTexture; // The set of kernels that are required int m_ComputeShadowCloudsKernel; + int m_FilterShadowCloudsKernel; void InitializeVolumetricCloudsShadows() { // Grab the kernels we need ComputeShader volumetricCloudsCS = m_Asset.renderPipelineResources.shaders.volumetricCloudsCS; m_ComputeShadowCloudsKernel = volumetricCloudsCS.FindKernel("ComputeVolumetricCloudsShadow"); + m_FilterShadowCloudsKernel = volumetricCloudsCS.FindKernel("FilterVolumetricCloudsShadow"); } void ReleaseVolumetricCloudsShadows() { for (int i = 0; i < VolumetricClouds.CloudShadowResolutionCount; ++i) - { RTHandles.Release(m_VolumetricCloudsShadowTexture[i]); - } + RTHandles.Release(m_VolumetricCloudsIntermediateShadowTexture); } bool HasVolumetricCloudsShadows(HDCamera hdCamera, in VolumetricClouds settings) @@ -55,6 +57,7 @@ struct VolumetricCloudsShadowsParameters // Data common to all volumetric cloud passes public VolumetricCloudCommonData commonData; public int shadowsKernel; + public int filterShadowsKernel; } VolumetricCloudsShadowsParameters PrepareVolumetricCloudsShadowsParameters(HDCamera hdCamera, VolumetricClouds settings) @@ -68,6 +71,7 @@ VolumetricCloudsShadowsParameters PrepareVolumetricCloudsShadowsParameters(HDCam FillVolumetricCloudsCommonData(false, settings, TVolumetricCloudsCameraType.Default, in cloudModelData, ref parameters.commonData); parameters.shadowsKernel = m_ComputeShadowCloudsKernel; + parameters.filterShadowsKernel = m_FilterShadowCloudsKernel; // Update the constant buffer VolumetricCloudsCameraData cameraData; @@ -87,7 +91,7 @@ VolumetricCloudsShadowsParameters PrepareVolumetricCloudsShadowsParameters(HDCam return parameters; } - static void TraceVolumetricCloudShadow(CommandBuffer cmd, VolumetricCloudsShadowsParameters parameters, RTHandle shadowTexture) + static void TraceVolumetricCloudShadow(CommandBuffer cmd, VolumetricCloudsShadowsParameters parameters, RTHandle intermediateTexture, RTHandle shadowTexture) { using (new ProfilingScope(cmd, ProfilingSampler.Get(HDProfileId.VolumetricCloudsShadow))) { @@ -111,6 +115,17 @@ static void TraceVolumetricCloudShadow(CommandBuffer cmd, VolumetricCloudsShadow // Evaluate the shadow cmd.DispatchCompute(parameters.commonData.volumetricCloudsCS, parameters.shadowsKernel, shadowTX, shadowTY, 1); + // Given the low number of steps available and the absence of noise in the integration, we try to reduce the artifacts by doing two consecutive 3x3 blur passes. + // Filter the shadow + cmd.SetComputeTextureParam(parameters.commonData.volumetricCloudsCS, parameters.filterShadowsKernel, HDShaderIDs._VolumetricCloudsShadow, shadowTexture); + cmd.SetComputeTextureParam(parameters.commonData.volumetricCloudsCS, parameters.filterShadowsKernel, HDShaderIDs._VolumetricCloudsShadowRW, intermediateTexture); + cmd.DispatchCompute(parameters.commonData.volumetricCloudsCS, parameters.filterShadowsKernel, shadowTX, shadowTY, 1); + + // Filter the shadow + cmd.SetComputeTextureParam(parameters.commonData.volumetricCloudsCS, parameters.filterShadowsKernel, HDShaderIDs._VolumetricCloudsShadow, intermediateTexture); + cmd.SetComputeTextureParam(parameters.commonData.volumetricCloudsCS, parameters.filterShadowsKernel, HDShaderIDs._VolumetricCloudsShadowRW, shadowTexture); + cmd.DispatchCompute(parameters.commonData.volumetricCloudsCS, parameters.filterShadowsKernel, shadowTX, shadowTY, 1); + // Bump the texture version shadowTexture.rt.IncrementUpdateCount(); } @@ -146,12 +161,15 @@ RTHandle RequestVolumetricCloudsShadowTexture(in VolumetricClouds settings) case VolumetricClouds.CloudShadowResolution.High512: shadowResIndex = 3; break; + case VolumetricClouds.CloudShadowResolution.Ultra1024: + shadowResIndex = 4; + break; } if (m_VolumetricCloudsShadowTexture[shadowResIndex] == null) { m_VolumetricCloudsShadowTexture[shadowResIndex] = RTHandles.Alloc(shadowResolution, shadowResolution, 1, colorFormat: GraphicsFormat.B10G11R11_UFloatPack32, - enableRandomWrite: true, useDynamicScale: false, useMipMap: false, wrapMode: TextureWrapMode.Clamp, name: "Volumetric Clouds Shadow Texture"); + enableRandomWrite: true, useDynamicScale: false, useMipMap: false, filterMode: FilterMode.Bilinear, wrapMode: TextureWrapMode.Clamp, name: "Volumetric Clouds Shadow Texture"); } return m_VolumetricCloudsShadowTexture[shadowResIndex]; @@ -169,9 +187,18 @@ CookieParameters RenderVolumetricCloudsShadows(CommandBuffer cmd, HDCamera hdCam // TODO: Right now we can end up with a bunch of textures allocated which should be solved by an other PR. RTHandle currentHandle = RequestVolumetricCloudsShadowTexture(settings); + // Check if the intermediate texture that we need for the filtering has already been allocated + if (m_VolumetricCloudsIntermediateShadowTexture == null) + { + m_VolumetricCloudsIntermediateShadowTexture = RTHandles.Alloc((int)VolumetricClouds.CloudShadowResolution.Ultra1024, (int)VolumetricClouds.CloudShadowResolution.Ultra1024, + 1, colorFormat: GraphicsFormat.B10G11R11_UFloatPack32, + enableRandomWrite: true, useDynamicScale: false, useMipMap: false, + wrapMode: TextureWrapMode.Clamp, name: "Intermediate Volumetric Clouds Shadow Texture"); + } + // Evaluate and return the shadow var parameters = PrepareVolumetricCloudsShadowsParameters(hdCamera, settings); - TraceVolumetricCloudShadow(cmd, parameters, currentHandle); + TraceVolumetricCloudShadow(cmd, parameters, m_VolumetricCloudsIntermediateShadowTexture, currentHandle); // Grab the current sun light Light sunLight = GetMainLight(); diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.compute b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.compute index a61d4eeb013..cad84b01da9 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.compute +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.compute @@ -23,6 +23,7 @@ // Shadows #pragma kernel ComputeVolumetricCloudsShadow +#pragma kernel FilterVolumetricCloudsShadow #pragma multi_compile _ PHYSICALLY_BASED_SUN #pragma multi_compile _ LOCAL_VOLUMETRIC_CLOUDS @@ -1547,9 +1548,9 @@ void ComputeVolumetricCloudsShadow(uint3 currentCoords : SV_DispatchThreadID, ui float startDistance = intersectionO.x; float totalDistance = intersectionI.x - intersectionO.x; - float stepSize = totalDistance / 10; + float stepSize = totalDistance / 16; - for (int i = 1; i <= 8; ++i) + for (int i = 0; i < 16; ++i) { // Compute the sphere intersection position float3 positionWS = rayOriginWS + rayDirection * (intersectionO.x + stepSize * i); @@ -1564,7 +1565,7 @@ void ComputeVolumetricCloudsShadow(uint3 currentCoords : SV_DispatchThreadID, ui if (cloudProperties.density > CLOUD_DENSITY_TRESHOLD) { // Apply the extinction - const float3 currentStepExtinction = exp(- _ScatteringTint.xyz * cloudProperties.density * cloudProperties.sigmaT * stepSize); + const float3 currentStepExtinction = exp(-_ScatteringTint.xyz * cloudProperties.density * cloudProperties.sigmaT * stepSize); transmittance *= Luminance(currentStepExtinction); } } @@ -1572,3 +1573,68 @@ void ComputeVolumetricCloudsShadow(uint3 currentCoords : SV_DispatchThreadID, ui _VolumetricCloudsShadowRW[currentCoords.xy] = lerp(1.0 - _ShadowIntensity, 1.0, transmittance); } + +// LDS used to pre-fetch the neighborhood data a 8x8 region with a one pixel border (10x10) +groupshared uint gs_cacheShadow[100]; + +TEXTURE2D(_VolumetricCloudsShadow); + +void FillShadowLDSData(uint elementIndex, uint2 groupOrigin) +{ + // Define which value we will be acessing with this worker thread + int acessCoordX = elementIndex % 10; + int acessCoordY = elementIndex / 10; + + // The initial position of the access + int2 originXY = (int2)groupOrigin - int2(1, 1) + int2(acessCoordX, acessCoordY); + + // Compute the sample position + int2 tapCoord = int2(clamp(originXY.x, 0, _ShadowCookieResolution - 1), clamp(originXY.y, 0, _ShadowCookieResolution - 1)); + + // Read the value from the texture + float3 shadowValue = LOAD_TEXTURE2D(_VolumetricCloudsShadow, tapCoord.xy).xyz; + + // Pack it and store it into the LDS + gs_cacheShadow[elementIndex] = PackToR11G11B10f(shadowValue); +} + +uint ShadowOffsetToLDSAdress(uint2 groupThreadId, int2 offset) +{ + // Compute the tap coordinate in the 10x10 grid + uint2 tapAddress = (uint2)((int2)(groupThreadId + 1) + offset); + return clamp((uint)(tapAddress.x) % 10 + tapAddress.y * 10, 0, 99); +} + +[numthreads(8, 8, 1)] +void FilterVolumetricCloudsShadow(uint3 currentCoords : SV_DispatchThreadID, int groupIndex : SV_GroupIndex, uint2 groupThreadId : SV_GroupThreadID, uint2 groupId : SV_GroupID) +{ + UNITY_XR_ASSIGN_VIEW_INDEX(currentCoords.z); + + // Fill the LDS with the shadow data + if (groupIndex < 50) + { + FillShadowLDSData(groupIndex * 2, groupId * 8); + FillShadowLDSData(groupIndex * 2 + 1, groupId * 8); + } + GroupMemoryBarrierWithGroupSync(); + + // Grab the center pixel + float3 centerShadow = UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(0, 0))]); + + // Average the 3x3 rgion + float3 filteredShadow = UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(-1, -1))]); + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(0, -1))]); + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(1, -1))]); + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(-1, 0))]); + filteredShadow += centerShadow; + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(1, 0))]); + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(-1, 1))]); + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(0, 1))]); + filteredShadow += UnpackFromR11G11B10f(gs_cacheShadow[ShadowOffsetToLDSAdress(groupThreadId, int2(1, 1))]); + + // We have a different behavior if this is a border pixel + float borderPixel = currentCoords.x == 0 || currentCoords.y == 0 || ((int)currentCoords.x) == (_ShadowCookieResolution - 1) || ((int)currentCoords.y) == (_ShadowCookieResolution - 1) ? 1.0 : 0.0; + + // Normalize and return the result + _VolumetricCloudsShadowRW[currentCoords.xy] = lerp(filteredShadow * 0.1111111, centerShadow, borderPixel); +} diff --git a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.cs b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.cs index 57d1a1acbdd..60cabeb6d89 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/Lighting/VolumetricLighting/VolumetricClouds.cs @@ -80,10 +80,12 @@ public enum CloudShadowResolution Medium256 = 256, /// The volumetric clouds shadow will be 512x512. High512 = 512, + /// The volumetric clouds shadow will be 1024x1024. + Ultra1024 = 1024, } /// - public const int CloudShadowResolutionCount = 4; + public const int CloudShadowResolutionCount = 5; /// /// A that holds a value. diff --git a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs index e06e5d423c9..30890c2390c 100644 --- a/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs +++ b/com.unity.render-pipelines.high-definition/Runtime/RenderPipeline/HDStringConstants.cs @@ -481,6 +481,7 @@ static class HDShaderIDs public static readonly int _CloudsAdditionalTextureRW = Shader.PropertyToID("_CloudsAdditionalTextureRW"); public static readonly int _VolumetricCloudsTexture = Shader.PropertyToID("_VolumetricCloudsTexture"); public static readonly int _VolumetricCloudsTextureRW = Shader.PropertyToID("_VolumetricCloudsTextureRW"); + public static readonly int _VolumetricCloudsShadow = Shader.PropertyToID("_VolumetricCloudsShadow"); public static readonly int _VolumetricCloudsShadowRW = Shader.PropertyToID("_VolumetricCloudsShadowRW"); public static readonly int _VolumetricCloudsUpscaleTextureRW = Shader.PropertyToID("_VolumetricCloudsUpscaleTextureRW"); public static readonly int _HistoryVolumetricClouds0Texture = Shader.PropertyToID("_HistoryVolumetricClouds0Texture");