309 lines
15 KiB
C#
309 lines
15 KiB
C#
using UnityEngine;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
namespace BadDog.Rendering.AreaLight
|
|
{
|
|
public partial class AreaLightingPass
|
|
{
|
|
partial void AllocateShadowmapIfNeeded()
|
|
{
|
|
if (m_CreateEmptyShadowmap)
|
|
{
|
|
// Allocate minimal 1x1 shadowmap for shader compatibility
|
|
ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_AreaLightsShadowmapHandle, 1, 1, k_ShadowmapBufferBits, name: "_AreaLightsShadowmapTexture");
|
|
}
|
|
else
|
|
{
|
|
// Allocate actual shadowmap atlas for rendering shadows
|
|
ShadowUtils.ShadowRTReAllocateIfNeeded(ref m_AreaLightsShadowmapHandle, m_RenderTargetWidth, m_RenderTargetHeight, k_ShadowmapBufferBits, name: "_AreaLightsShadowmapTexture");
|
|
}
|
|
}
|
|
|
|
public override void Configure(CommandBuffer cmd, RenderTextureDescriptor cameraTextureDescriptor)
|
|
{
|
|
if (m_AreaLightsShadowmapHandle != null)
|
|
{
|
|
ConfigureTarget(m_AreaLightsShadowmapHandle);
|
|
|
|
if (m_EmptyShadowmapNeedsClear || !m_CreateEmptyShadowmap)
|
|
{
|
|
ConfigureClear(ClearFlag.All, Color.black);
|
|
m_EmptyShadowmapNeedsClear = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
public override void Execute(ScriptableRenderContext context, ref RenderingData renderingData)
|
|
{
|
|
CommandBuffer cmd = CommandBufferPool.Get();
|
|
|
|
using (new ProfilingScope(cmd, m_ProfilingSampler))
|
|
{
|
|
// Enable global shader keyword for BG Area Lighting
|
|
CoreUtils.SetKeyword(cmd, AreaLightingShaderIDs.ENABLE_BG_AREA_LIGHTING_KEYWORD, true);
|
|
|
|
// Build LTC data texture (uses reference counting, safe to call multiple times)
|
|
LTCAreaLight.instance.Build();
|
|
LTCAreaLight.instance.Bind(cmd);
|
|
|
|
// GGX + Disney Diffuse
|
|
PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse);
|
|
PreIntegratedFGD.instance.RenderInit(PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse, cmd);
|
|
PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse);
|
|
|
|
// Charlie + Fabric Lambert
|
|
PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert);
|
|
PreIntegratedFGD.instance.RenderInit(PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert, cmd);
|
|
PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert);
|
|
|
|
// Marschner
|
|
PreIntegratedFGD.instance.Build(PreIntegratedFGD.FGDIndex.FGD_Marschner);
|
|
PreIntegratedFGD.instance.RenderInit(PreIntegratedFGD.FGDIndex.FGD_Marschner, cmd);
|
|
PreIntegratedFGD.instance.Bind(cmd, PreIntegratedFGD.FGDIndex.FGD_Marschner);
|
|
|
|
var lightData = renderingData.lightData;
|
|
var visibleLights = lightData.visibleLights;
|
|
var areaLightDataArray = BGAreaLightManager.BuildAreaLightDataArray(visibleLights, lightData.mainLightIndex, m_LightSettings.maxAreaLights);
|
|
var areaLightBuffer = BGAreaLightManager.UpdateGraphicsBuffer(areaLightDataArray);
|
|
|
|
if (areaLightBuffer != null)
|
|
{
|
|
cmd.SetGlobalBuffer(AreaLightingShaderIDs._AreaLightDataBuffer, areaLightBuffer);
|
|
cmd.SetGlobalInt(AreaLightingShaderIDs._AreaLightDataBufferCount,
|
|
areaLightDataArray.IsCreated ? areaLightDataArray.Length : 0);
|
|
}
|
|
else
|
|
{
|
|
var emptyBuffer = BGAreaLightManager.GetEmptyBuffer();
|
|
cmd.SetGlobalBuffer(AreaLightingShaderIDs._AreaLightDataBuffer, emptyBuffer);
|
|
cmd.SetGlobalInt(AreaLightingShaderIDs._AreaLightDataBufferCount, 0);
|
|
}
|
|
}
|
|
|
|
if (m_CreateEmptyShadowmap)
|
|
{
|
|
SetShadowParamsForEmptyShadowmap(cmd);
|
|
|
|
if (m_AreaLightsShadowmapHandle != null)
|
|
{
|
|
cmd.SetGlobalTexture(AreaLightingShaderIDs._AreaLightsShadowmapTexture, m_AreaLightsShadowmapHandle.nameID);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
RenderAreaLightShadows(context, ref renderingData, cmd);
|
|
}
|
|
|
|
context.ExecuteCommandBuffer(cmd);
|
|
CommandBufferPool.Release(cmd);
|
|
}
|
|
|
|
private void InitRendererLists(ref CullingResults cullResults, ref PassData passData, ScriptableRenderContext context)
|
|
{
|
|
if (m_CreateEmptyShadowmap)
|
|
{
|
|
return;
|
|
}
|
|
|
|
for (int shadowSliceIndex = 0; shadowSliceIndex < m_ValidShadowCastingLightsCount; ++shadowSliceIndex)
|
|
{
|
|
int areaLightIndex = m_ShadowSliceToAreaLightIndex[shadowSliceIndex];
|
|
int visibleLightIndex = m_AreaLightIndexToVisibleLightIndex[areaLightIndex];
|
|
if (visibleLightIndex < 0 || visibleLightIndex >= passData.visibleLights.Length)
|
|
{
|
|
passData.shadowRendererLists[shadowSliceIndex] = default;
|
|
continue;
|
|
}
|
|
|
|
Light shadowLight = passData.visibleLights[visibleLightIndex].light;
|
|
int layerMask = shadowLight != null ? shadowLight.cullingMask : ~0;
|
|
bool useRenderingLayers = UniversalRenderPipeline.asset != null && UniversalRenderPipeline.asset.useRenderingLayers;
|
|
uint renderingLayerMask = useRenderingLayers && shadowLight != null
|
|
? (uint)shadowLight.renderingLayerMask
|
|
: uint.MaxValue;
|
|
var sorting = new SortingSettings(passData.camera) { criteria = SortingCriteria.None };
|
|
var ds = new DrawingSettings(new ShaderTagId("ShadowCaster"), sorting);
|
|
var fs = new FilteringSettings(RenderQueueRange.all, layerMask, renderingLayerMask);
|
|
|
|
RendererListParams param = new RendererListParams(cullResults, ds, fs);
|
|
passData.shadowRendererLists[shadowSliceIndex] = context.CreateRendererList(ref param);
|
|
}
|
|
}
|
|
|
|
private void RenderAreaLightShadowmapAtlas(CommandBuffer cmd, ref PassData data)
|
|
{
|
|
using (new ProfilingScope(cmd, m_ShadowProfilingSampler))
|
|
{
|
|
SetBGAreaLightShadowQualityKeywords(cmd);
|
|
|
|
bool anyShadowSliceRendered = false;
|
|
int shadowSlicesCount = m_ShadowSliceToAreaLightIndex.Count;
|
|
|
|
Vector4 lastShadowBias = new Vector4(-10f, -10f, -10f, -10f);
|
|
|
|
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < shadowSlicesCount; ++globalShadowSliceIndex)
|
|
{
|
|
int areaLightIndex = m_ShadowSliceToAreaLightIndex[globalShadowSliceIndex];
|
|
if (areaLightIndex < 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
Vector4 shadowParams = m_AreaLightIndexToShadowParams[areaLightIndex];
|
|
// Check if shadow is valid: w != -1 and shadowStrength > 0
|
|
if (Mathf.Approximately(shadowParams.x, 0.0f) || Mathf.Approximately(shadowParams.w, -1.0f))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
int visibleLightIndex = m_AreaLightIndexToVisibleLightIndex[areaLightIndex];
|
|
if (visibleLightIndex < 0 || visibleLightIndex >= data.visibleLights.Length)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
VisibleLight shadowLight = data.visibleLights[visibleLightIndex];
|
|
ShadowSliceData shadowSliceData = m_AreaLightsShadowSlices[globalShadowSliceIndex];
|
|
|
|
// Get nearPlane for this light (matching the logic in TryComputeAreaLightShadowSlice)
|
|
Light unityLight = shadowLight.light;
|
|
float nearPlane;
|
|
if (unityLight != null &&
|
|
BGAreaLightManager.TryGetRegisteredAreaLight(unityLight, out var areaLightComp))
|
|
{
|
|
nearPlane = areaLightComp.GetShadowNearPlane();
|
|
}
|
|
else
|
|
{
|
|
nearPlane = unityLight != null ? unityLight.shadowNearPlane : 0.1f;
|
|
}
|
|
|
|
Vector4 shadowBias = AreaShadowUtils.GetRectangleLightShadowBias(ref shadowLight,
|
|
ref m_ShadowDataCache, shadowSliceData.resolution, nearPlane, m_ShadowSettings.shadowFilter);
|
|
if (globalShadowSliceIndex == 0 || !AreaShadowUtils.FastApproximately(shadowBias, lastShadowBias))
|
|
{
|
|
SetShadowBias(cmd, shadowBias);
|
|
lastShadowBias = shadowBias;
|
|
}
|
|
|
|
Vector3 lightPosition = shadowLight.localToWorldMatrix.GetColumn(3);
|
|
SetLightPosition(cmd, lightPosition);
|
|
|
|
RendererList shadowRendererList = data.shadowRendererLists[globalShadowSliceIndex];
|
|
RenderShadowSlice(cmd, ref shadowSliceData, ref shadowRendererList,
|
|
shadowSliceData.projectionMatrix, shadowSliceData.viewMatrix);
|
|
anyShadowSliceRendered = true;
|
|
}
|
|
|
|
if (anyShadowSliceRendered)
|
|
{
|
|
SetupAreaLightsShadowReceiverConstants(cmd, data.allocatedShadowAtlasSize);
|
|
cmd.SetViewProjectionMatrices(data.viewMatrix, data.projectionMatrix);
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitPassData(ref PassData passData, in CameraData cameraData, in LightData lightData)
|
|
{
|
|
passData.pass = this;
|
|
passData.emptyShadowmap = m_CreateEmptyShadowmap;
|
|
passData.visibleLights = lightData.visibleLights;
|
|
passData.viewMatrix = cameraData.GetViewMatrix();
|
|
passData.projectionMatrix = cameraData.GetProjectionMatrix();
|
|
passData.camera = cameraData.camera;
|
|
passData.shadowRendererLists = m_ShadowRendererLists;
|
|
}
|
|
|
|
private void RenderAreaLightShadows(ScriptableRenderContext context, ref RenderingData renderingData, CommandBuffer cmd)
|
|
{
|
|
var cameraData = renderingData.cameraData;
|
|
var lightData = renderingData.lightData;
|
|
|
|
InitPassData(ref m_PassData, cameraData, lightData);
|
|
InitRendererLists(ref renderingData.cullResults, ref m_PassData, context);
|
|
m_PassData.allocatedShadowAtlasSize = new Vector2Int(m_RenderTargetWidth, m_RenderTargetHeight);
|
|
|
|
RenderAreaLightShadowmapAtlas(cmd, ref m_PassData);
|
|
|
|
if (m_AreaLightsShadowmapHandle != null)
|
|
{
|
|
cmd.SetGlobalTexture(AreaLightingShaderIDs._AreaLightsShadowmapTexture, m_AreaLightsShadowmapHandle.nameID);
|
|
}
|
|
}
|
|
|
|
private void SetShadowBias(CommandBuffer cmd, Vector4 shadowBias)
|
|
{
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._ShadowBias, shadowBias);
|
|
}
|
|
|
|
private void SetLightPosition(CommandBuffer cmd, Vector3 lightPosition)
|
|
{
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._LightPosition,
|
|
new Vector4(lightPosition.x, lightPosition.y, lightPosition.z, 1.0f));
|
|
}
|
|
|
|
private void RenderShadowSlice(CommandBuffer cmd, ref ShadowSliceData shadowSliceData, ref RendererList shadowRendererList,
|
|
Matrix4x4 proj, Matrix4x4 view)
|
|
{
|
|
cmd.SetGlobalDepthBias(1.0f, 2.5f);
|
|
cmd.SetViewport(new Rect(shadowSliceData.offsetX, shadowSliceData.offsetY, shadowSliceData.resolution,
|
|
shadowSliceData.resolution));
|
|
cmd.SetViewProjectionMatrices(view, proj);
|
|
|
|
cmd.DrawRendererList(shadowRendererList);
|
|
|
|
cmd.DisableScissorRect();
|
|
cmd.SetGlobalDepthBias(0.0f, 0.0f);
|
|
}
|
|
|
|
private void SetupAreaLightsShadowReceiverConstants(CommandBuffer cmd, Vector2Int atlasSize)
|
|
{
|
|
cmd.SetGlobalMatrixArray(AreaLightingShaderIDs._AreaLightsWorldToShadow, m_AreaLightShadowSliceIndexTo_WorldShadowMatrix);
|
|
cmd.SetGlobalVectorArray(AreaLightingShaderIDs._AreaLightShadowParams, m_AreaLightIndexToShadowParams);
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._AreaLightShadowmapSize, new Vector4(1f / atlasSize.x, 1f / atlasSize.y, atlasSize.x, atlasSize.y));
|
|
float pcssShadowSoftness = Mathf.Max(0f, m_ShadowSettings.pcssShadowSoftness);
|
|
int pcssBlockerSampleCount = Mathf.Clamp(m_ShadowSettings.pcssBlockerSampleCount, 1, 64);
|
|
int pcssFilterSampleCount = Mathf.Clamp(m_ShadowSettings.pcssFilterSampleCount, 1, 64);
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._PCSSAdditionalLightParams, new Vector4(pcssShadowSoftness, pcssBlockerSampleCount, pcssFilterSampleCount, 1.0f));
|
|
}
|
|
|
|
private static void SetShadowParamsForEmptyShadowmap(CommandBuffer cmd)
|
|
{
|
|
cmd.SetGlobalMatrixArray(AreaLightingShaderIDs._AreaLightsWorldToShadow, s_EmptyShadowMatrices);
|
|
cmd.SetGlobalVectorArray(AreaLightingShaderIDs._AreaLightShadowParams, s_EmptyShadowParams);
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._AreaLightShadowmapSize, new Vector4(0f, 0f, 1f, 1f));
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._PCSSAdditionalLightParams, Vector4.zero);
|
|
}
|
|
|
|
private void SetBGAreaLightShadowQualityKeywords(CommandBuffer cmd)
|
|
{
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCF2x2, false);
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent5x5, false);
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent7x7, false);
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCSS, false);
|
|
|
|
switch (m_ShadowSettings.shadowFilter)
|
|
{
|
|
case AreaLightShadowSettings.BGAreaLightShadowMode.PCF2x2:
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCF2x2, true);
|
|
break;
|
|
case AreaLightShadowSettings.BGAreaLightShadowMode.Tent5x5:
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent5x5, true);
|
|
break;
|
|
case AreaLightShadowSettings.BGAreaLightShadowMode.Tent7x7:
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsTent7x7, true);
|
|
break;
|
|
case AreaLightShadowSettings.BGAreaLightShadowMode.PCSS:
|
|
cmd.SetKeyword(ShaderGlobalKeywords.BGAreaLightShadowsPCSS, true);
|
|
break;
|
|
case AreaLightShadowSettings.BGAreaLightShadowMode.Off:
|
|
default:
|
|
// All keywords disabled (handled above)
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|