UnityGame/Library/PackageCache/com.unity.render-pipelines.universal/Runtime/Passes/MainLightShadowCasterPass.cs

510 lines
26 KiB
C#
Raw Normal View History

2024-10-27 10:53:47 +03:00
using System;
using UnityEngine.Experimental.Rendering;
using UnityEngine.Rendering.RenderGraphModule;
using System.Collections.Generic;
namespace UnityEngine.Rendering.Universal.Internal
{
/// <summary>
/// Renders a shadow map for the main Light.
/// </summary>
public class MainLightShadowCasterPass : ScriptableRenderPass
{
private static class MainLightShadowConstantBuffer
{
public static int _WorldToShadow;
public static int _ShadowParams;
public static int _CascadeShadowSplitSpheres0;
public static int _CascadeShadowSplitSpheres1;
public static int _CascadeShadowSplitSpheres2;
public static int _CascadeShadowSplitSpheres3;
public static int _CascadeShadowSplitSphereRadii;
public static int _ShadowOffset0;
public static int _ShadowOffset1;
public static int _ShadowmapSize;
}
const int k_MaxCascades = 4;
const int k_ShadowmapBufferBits = 16;
float m_CascadeBorder;
float m_MaxShadowDistanceSq;
int m_ShadowCasterCascadesCount;
int m_MainLightShadowmapID;
internal RTHandle m_MainLightShadowmapTexture;
private RTHandle m_EmptyMainLightShadowmapTexture;
private const int k_EmptyShadowMapDimensions = 1;
private const string k_MainLightShadowMapTextureName = "_MainLightShadowmapTexture";
private const string k_EmptyMainLightShadowMapTextureName = "_EmptyMainLightShadowmapTexture";
private static readonly Vector4 s_EmptyShadowParams = new Vector4(1, 0, 1, 0);
private static readonly Vector4 s_EmptyShadowmapSize = s_EmptyShadowmapSize = new Vector4(k_EmptyShadowMapDimensions, 1f / k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions);
Matrix4x4[] m_MainLightShadowMatrices;
ShadowSliceData[] m_CascadeSlices;
Vector4[] m_CascadeSplitDistances;
private RenderTextureDescriptor m_MainLightShadowDescriptor;
bool m_CreateEmptyShadowmap;
bool m_EmptyShadowmapNeedsClear = false;
int renderTargetWidth;
int renderTargetHeight;
ProfilingSampler m_ProfilingSetupSampler = new ProfilingSampler("Setup Main Shadowmap");
private PassData m_PassData;
/// <summary>
/// Creates a new <c>MainLightShadowCasterPass</c> instance.
/// </summary>
/// <param name="evt">The <c>RenderPassEvent</c> to use.</param>
/// <seealso cref="RenderPassEvent"/>
public MainLightShadowCasterPass(RenderPassEvent evt)
{
profilingSampler = new ProfilingSampler("Draw Main Light Shadowmap");
renderPassEvent = evt;
m_PassData = new PassData();
m_MainLightShadowMatrices = new Matrix4x4[k_MaxCascades + 1];
m_CascadeSlices = new ShadowSliceData[k_MaxCascades];
m_CascadeSplitDistances = new Vector4[k_MaxCascades];
MainLightShadowConstantBuffer._WorldToShadow = Shader.PropertyToID("_MainLightWorldToShadow");
MainLightShadowConstantBuffer._ShadowParams = Shader.PropertyToID("_MainLightShadowParams");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0 = Shader.PropertyToID("_CascadeShadowSplitSpheres0");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1 = Shader.PropertyToID("_CascadeShadowSplitSpheres1");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2 = Shader.PropertyToID("_CascadeShadowSplitSpheres2");
MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3 = Shader.PropertyToID("_CascadeShadowSplitSpheres3");
MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii = Shader.PropertyToID("_CascadeShadowSplitSphereRadii");
MainLightShadowConstantBuffer._ShadowOffset0 = Shader.PropertyToID("_MainLightShadowOffset0");
MainLightShadowConstantBuffer._ShadowOffset1 = Shader.PropertyToID("_MainLightShadowOffset1");
MainLightShadowConstantBuffer._ShadowmapSize = Shader.PropertyToID("_MainLightShadowmapSize");
m_MainLightShadowmapID = Shader.PropertyToID(k_MainLightShadowMapTextureName);
m_EmptyShadowmapNeedsClear = true;
}
/// <summary>
/// Cleans up resources used by the pass.
/// </summary>
public void Dispose()
{
m_MainLightShadowmapTexture?.Release();
m_EmptyMainLightShadowmapTexture?.Release();
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="renderingData"></param>
/// <returns>True if the pass should be enqueued, otherwise false.</returns>
/// <seealso cref="RenderingData"/>
public bool Setup(ref RenderingData renderingData)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
return Setup(universalRenderingData, cameraData, lightData, shadowData);
}
/// <summary>
/// Sets up the pass.
/// </summary>
/// <param name="renderingData">Data containing rendering settings.</param>
/// <param name="cameraData">Data containing camera settings.</param>
/// <param name="lightData">Data containing light settings.</param>
/// <param name="shadowData">Data containing shadow settings.</param>
/// <returns>True if the pass should be enqueued, otherwise false.</returns>
/// <seealso cref="RenderingData"/>
public bool Setup(UniversalRenderingData renderingData, UniversalCameraData cameraData, UniversalLightData lightData, UniversalShadowData shadowData)
{
if (!shadowData.mainLightShadowsEnabled)
return false;
#if UNITY_EDITOR
if (CoreUtils.IsSceneLightingDisabled(cameraData.camera))
return false;
#endif
using var profScope = new ProfilingScope(m_ProfilingSetupSampler);
if (!shadowData.supportsMainLightShadows)
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants);
Clear();
int shadowLightIndex = lightData.mainLightIndex;
if (shadowLightIndex == -1)
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants);
VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
Light light = shadowLight.light;
if (light.shadows == LightShadows.None)
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants);
if (shadowLight.lightType != LightType.Directional)
{
Debug.LogWarning("Only directional lights are supported as main light.");
}
if (!renderingData.cullResults.GetShadowCasterBounds(shadowLightIndex, out Bounds _))
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants);
m_ShadowCasterCascadesCount = shadowData.mainLightShadowCascadesCount;
renderTargetWidth = shadowData.mainLightRenderTargetWidth;
renderTargetHeight = shadowData.mainLightRenderTargetHeight;
ref readonly URPLightShadowCullingInfos shadowCullingInfos = ref shadowData.visibleLightsShadowCullingInfos.UnsafeElementAt(shadowLightIndex);
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
ref readonly ShadowSliceData sliceData = ref shadowCullingInfos.slices.UnsafeElementAt(cascadeIndex);
m_CascadeSplitDistances[cascadeIndex] = sliceData.splitData.cullingSphere;
m_CascadeSlices[cascadeIndex] = sliceData;
if (!shadowCullingInfos.IsSliceValid(cascadeIndex))
return SetupForEmptyRendering(cameraData.renderer.stripShadowsOffVariants);
}
UpdateTextureDescriptorIfNeeded();
m_MaxShadowDistanceSq = cameraData.maxShadowDistance * cameraData.maxShadowDistance;
m_CascadeBorder = shadowData.mainLightShadowCascadeBorder;
m_CreateEmptyShadowmap = false;
useNativeRenderPass = true;
return true;
}
private void UpdateTextureDescriptorIfNeeded()
{
if ( m_MainLightShadowDescriptor.width != renderTargetWidth
|| m_MainLightShadowDescriptor.height != renderTargetHeight
|| m_MainLightShadowDescriptor.depthBufferBits != k_ShadowmapBufferBits
|| m_MainLightShadowDescriptor.colorFormat != RenderTextureFormat.Shadowmap)
{
m_MainLightShadowDescriptor = new RenderTextureDescriptor(renderTargetWidth, renderTargetHeight, RenderTextureFormat.Shadowmap, k_ShadowmapBufferBits);
}
}
bool SetupForEmptyRendering(bool stripShadowsOffVariants)
{
if (!stripShadowsOffVariants)
return false;
m_CreateEmptyShadowmap = true;
useNativeRenderPass = false;
return true;
}
/// <inheritdoc />
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
{
// Disable obsolete warning for internal usage
#pragma warning disable CS0618
if (m_CreateEmptyShadowmap)
{
// Required for scene view camera(URP renderer not initialized)
if (ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_EmptyMainLightShadowmapTexture, k_EmptyShadowMapDimensions, k_EmptyShadowMapDimensions, k_ShadowmapBufferBits, name: k_EmptyMainLightShadowMapTextureName))
m_EmptyShadowmapNeedsClear = true;
if (!m_EmptyShadowmapNeedsClear)
{
// UUM-63146 - glClientWaitSync: Expected application to have kicked everything until job: 96089 (possibly by calling glFlush)" are thrown in the Android Player on some devices with PowerVR Rogue GE8320
// Resetting of target would clean up the color attachment buffers and depth attachment buffers, which inturn is preventing the leak in the said platform. This is likely a symptomatic fix, but is solving the problem for now.
if (Application.platform == RuntimePlatform.Android && PlatformAutoDetect.isRunningOnPowerVRGPU)
ResetTarget();
return;
}
ConfigureTarget(m_EmptyMainLightShadowmapTexture);
m_EmptyShadowmapNeedsClear = false;
}
else
{
ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_MainLightShadowmapTexture, renderTargetWidth, renderTargetHeight, k_ShadowmapBufferBits, name: k_MainLightShadowMapTextureName);
ConfigureTarget(m_MainLightShadowmapTexture);
}
ConfigureClear(ClearFlag.All, Color.black);
#pragma warning restore CS0618
}
/// <inheritdoc/>
[Obsolete(DeprecationMessage.CompatibilityScriptingAPIObsolete, false)]
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
{
ContextContainer frameData = renderingData.frameData;
UniversalRenderingData universalRenderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
if (m_CreateEmptyShadowmap)
{
SetEmptyMainLightCascadeShadowmap(CommandBufferHelpers.GetRasterCommandBuffer(universalRenderingData.commandBuffer));
universalRenderingData.commandBuffer.SetGlobalTexture(m_MainLightShadowmapID, m_EmptyMainLightShadowmapTexture.nameID);
return;
}
InitPassData(ref m_PassData, universalRenderingData, cameraData, lightData, shadowData);
InitRendererLists(ref m_PassData, context, default(RenderGraph), false);
RenderMainLightCascadeShadowmap(CommandBufferHelpers.GetRasterCommandBuffer(universalRenderingData.commandBuffer), ref m_PassData, false);
universalRenderingData.commandBuffer.SetGlobalTexture(m_MainLightShadowmapID, m_MainLightShadowmapTexture.nameID);
}
void Clear()
{
for (int i = 0; i < m_MainLightShadowMatrices.Length; ++i)
m_MainLightShadowMatrices[i] = Matrix4x4.identity;
for (int i = 0; i < m_CascadeSplitDistances.Length; ++i)
m_CascadeSplitDistances[i] = new Vector4(0.0f, 0.0f, 0.0f, 0.0f);
for (int i = 0; i < m_CascadeSlices.Length; ++i)
m_CascadeSlices[i].Clear();
}
void SetEmptyMainLightCascadeShadowmap(RasterCommandBuffer cmd)
{
cmd.EnableKeyword(ShaderGlobalKeywords.MainLightShadows);
SetEmptyMainLightShadowParams(cmd);
}
internal static void SetEmptyMainLightShadowParams(RasterCommandBuffer cmd)
{
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams, s_EmptyShadowParams);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, s_EmptyShadowmapSize);
}
void RenderMainLightCascadeShadowmap(RasterCommandBuffer cmd, ref PassData data, bool isRenderGraph)
{
var lightData = data.lightData;
int shadowLightIndex = lightData.mainLightIndex;
if (shadowLightIndex == -1)
return;
VisibleLight shadowLight = lightData.visibleLights[shadowLightIndex];
using (new ProfilingScope(cmd, ProfilingSampler.Get(URPProfileId.MainLightShadow)))
{
// Need to start by setting the Camera position and worldToCamera Matrix as that is not set for passes executed before normal rendering
ShadowUtils.SetCameraPosition(cmd, data.cameraData.worldSpaceCameraPos);
// For non-RG, need set the worldToCamera Matrix as that is not set for passes executed before normal rendering,
// otherwise shadows will behave incorrectly when Scene and Game windows are open at the same time (UUM-63267).
if (!isRenderGraph)
ShadowUtils.SetWorldToCameraMatrix(cmd, data.cameraData.GetViewMatrix());
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
Vector4 shadowBias = ShadowUtils.GetShadowBias(ref shadowLight, shadowLightIndex, data.shadowData, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].resolution);
ShadowUtils.SetupShadowCasterConstantBuffer(cmd, ref shadowLight, shadowBias);
cmd.SetKeyword(ShaderGlobalKeywords.CastingPunctualLightShadow, false);
RendererList shadowRendererList = isRenderGraph? data.shadowRendererListsHandle[cascadeIndex] : data.shadowRendererLists[cascadeIndex];
ShadowUtils.RenderShadowSlice(cmd, ref m_CascadeSlices[cascadeIndex], ref shadowRendererList, m_CascadeSlices[cascadeIndex].projectionMatrix, m_CascadeSlices[cascadeIndex].viewMatrix);
}
data.shadowData.isKeywordSoftShadowsEnabled = shadowLight.light.shadows == LightShadows.Soft && data.shadowData.supportsSoftShadows;
cmd.SetKeyword(ShaderGlobalKeywords.MainLightShadows, data.shadowData.mainLightShadowCascadesCount == 1);
cmd.SetKeyword(ShaderGlobalKeywords.MainLightShadowCascades, data.shadowData.mainLightShadowCascadesCount > 1);
ShadowUtils.SetSoftShadowQualityShaderKeywords(cmd, data.shadowData);
SetupMainLightShadowReceiverConstants(cmd, ref shadowLight, data.shadowData);
}
}
void SetupMainLightShadowReceiverConstants(RasterCommandBuffer cmd, ref VisibleLight shadowLight, UniversalShadowData shadowData)
{
Light light = shadowLight.light;
bool softShadows = shadowLight.light.shadows == LightShadows.Soft && shadowData.supportsSoftShadows;
int cascadeCount = m_ShadowCasterCascadesCount;
for (int i = 0; i < cascadeCount; ++i)
m_MainLightShadowMatrices[i] = m_CascadeSlices[i].shadowTransform;
// We setup and additional a no-op WorldToShadow matrix in the last index
// because the ComputeCascadeIndex function in Shadows.hlsl can return an index
// out of bounds. (position not inside any cascade) and we want to avoid branching
Matrix4x4 noOpShadowMatrix = Matrix4x4.zero;
noOpShadowMatrix.m22 = (SystemInfo.usesReversedZBuffer) ? 1.0f : 0.0f;
for (int i = cascadeCount; i <= k_MaxCascades; ++i)
m_MainLightShadowMatrices[i] = noOpShadowMatrix;
float invShadowAtlasWidth = 1.0f / renderTargetWidth;
float invShadowAtlasHeight = 1.0f / renderTargetHeight;
float invHalfShadowAtlasWidth = 0.5f * invShadowAtlasWidth;
float invHalfShadowAtlasHeight = 0.5f * invShadowAtlasHeight;
float softShadowsProp = ShadowUtils.SoftShadowQualityToShaderProperty(light, softShadows);
ShadowUtils.GetScaleAndBiasForLinearDistanceFade(m_MaxShadowDistanceSq, m_CascadeBorder, out float shadowFadeScale, out float shadowFadeBias);
cmd.SetGlobalMatrixArray(MainLightShadowConstantBuffer._WorldToShadow, m_MainLightShadowMatrices);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowParams,
new Vector4(light.shadowStrength, softShadowsProp, shadowFadeScale, shadowFadeBias));
if (m_ShadowCasterCascadesCount > 1)
{
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres0,
m_CascadeSplitDistances[0]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres1,
m_CascadeSplitDistances[1]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres2,
m_CascadeSplitDistances[2]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSpheres3,
m_CascadeSplitDistances[3]);
cmd.SetGlobalVector(MainLightShadowConstantBuffer._CascadeShadowSplitSphereRadii, new Vector4(
m_CascadeSplitDistances[0].w * m_CascadeSplitDistances[0].w,
m_CascadeSplitDistances[1].w * m_CascadeSplitDistances[1].w,
m_CascadeSplitDistances[2].w * m_CascadeSplitDistances[2].w,
m_CascadeSplitDistances[3].w * m_CascadeSplitDistances[3].w));
}
// Inside shader soft shadows are controlled through global keyword.
// If any additional light has soft shadows it will force soft shadows on main light too.
// As it is not trivial finding out which additional light has soft shadows, we will pass main light properties if soft shadows are supported.
// This workaround will be removed once we will support soft shadows per light.
if (shadowData.supportsSoftShadows)
{
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset0,
new Vector4(-invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight,
invHalfShadowAtlasWidth, -invHalfShadowAtlasHeight));
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowOffset1,
new Vector4(-invHalfShadowAtlasWidth, invHalfShadowAtlasHeight,
invHalfShadowAtlasWidth, invHalfShadowAtlasHeight));
cmd.SetGlobalVector(MainLightShadowConstantBuffer._ShadowmapSize, new Vector4(invShadowAtlasWidth,
invShadowAtlasHeight,
renderTargetWidth, renderTargetHeight));
}
}
private class PassData
{
internal UniversalRenderingData renderingData;
internal UniversalCameraData cameraData;
internal UniversalLightData lightData;
internal UniversalShadowData shadowData;
internal MainLightShadowCasterPass pass;
internal TextureHandle shadowmapTexture;
internal int shadowmapID;
internal bool emptyShadowmap;
internal RendererListHandle[] shadowRendererListsHandle = new RendererListHandle[k_MaxCascades];
internal RendererList[] shadowRendererLists = new RendererList[k_MaxCascades];
}
private void InitPassData(
ref PassData passData,
UniversalRenderingData renderingData,
UniversalCameraData cameraData,
UniversalLightData lightData,
UniversalShadowData shadowData)
{
passData.pass = this;
passData.emptyShadowmap = m_CreateEmptyShadowmap;
passData.shadowmapID = m_MainLightShadowmapID;
passData.renderingData = renderingData;
passData.cameraData = cameraData;
passData.lightData = lightData;
passData.shadowData = shadowData;
}
void InitEmptyPassData(
ref PassData passData,
UniversalRenderingData renderingData,
UniversalCameraData cameraData,
UniversalLightData lightData,
UniversalShadowData shadowData)
{
passData.pass = this;
passData.emptyShadowmap = m_CreateEmptyShadowmap;
passData.shadowmapID = m_MainLightShadowmapID;
passData.renderingData = renderingData;
passData.cameraData = cameraData;
passData.lightData = lightData;
passData.shadowData = shadowData;
}
private void InitRendererLists(ref PassData passData, ScriptableRenderContext context, RenderGraph renderGraph, bool useRenderGraph)
{
int shadowLightIndex = passData.lightData.mainLightIndex;
if (!m_CreateEmptyShadowmap && shadowLightIndex != -1)
{
var settings = new ShadowDrawingSettings(passData.renderingData.cullResults, shadowLightIndex);
settings.useRenderingLayerMaskTest = UniversalRenderPipeline.asset.useRenderingLayers;
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
if (useRenderGraph)
passData.shadowRendererListsHandle[cascadeIndex] = renderGraph.CreateShadowRendererList(ref settings);
else
passData.shadowRendererLists[cascadeIndex] = context.CreateShadowRendererList(ref settings);
}
}
}
internal TextureHandle Render(RenderGraph graph, ContextContainer frameData)
{
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
UniversalLightData lightData = frameData.Get<UniversalLightData>();
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
TextureHandle shadowTexture;
using (var builder = graph.AddRasterRenderPass<PassData>(passName, out var passData, profilingSampler))
{
InitPassData(ref passData, renderingData, cameraData, lightData, shadowData);
InitRendererLists(ref passData, default(ScriptableRenderContext), graph, true);
if (!m_CreateEmptyShadowmap)
{
for (int cascadeIndex = 0; cascadeIndex < m_ShadowCasterCascadesCount; ++cascadeIndex)
{
builder.UseRendererList(passData.shadowRendererListsHandle[cascadeIndex]);
}
shadowTexture = UniversalRenderer.CreateRenderGraphTexture(graph, m_MainLightShadowDescriptor, k_MainLightShadowMapTextureName, true, ShadowUtils.m_ForceShadowPointSampling ? FilterMode.Point : FilterMode.Bilinear);
builder.SetRenderAttachmentDepth(shadowTexture, AccessFlags.Write);
}
else
{
shadowTexture = graph.defaultResources.defaultShadowTexture;
}
// Need this as shadowmap is only used as Global Texture and not a buffer, so would get culled by RG
builder.AllowPassCulling(false);
builder.AllowGlobalStateModification(true);
if (shadowTexture.IsValid())
builder.SetGlobalTextureAfterPass(shadowTexture, m_MainLightShadowmapID);
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
{
if (!data.emptyShadowmap)
{
data.pass.RenderMainLightCascadeShadowmap(context.cmd, ref data, true);
}
else
{
data.pass.SetEmptyMainLightCascadeShadowmap(context.cmd);
}
});
}
return shadowTexture;
}
};
}