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");