356 lines
18 KiB
C#
356 lines
18 KiB
C#
using System;
|
|
using Unity.Collections;
|
|
using UnityEngine;
|
|
using UnityEngine.Experimental.Rendering;
|
|
using UnityEngine.Rendering;
|
|
using UnityEngine.Rendering.Universal;
|
|
|
|
#if UNITY_6000_0_OR_NEWER || UNITY_2023_2_OR_NEWER
|
|
using UnityEngine.Rendering.RenderGraphModule;
|
|
|
|
namespace BadDog.Rendering.AreaLight
|
|
{
|
|
public partial class AreaLightingPass
|
|
{
|
|
private RendererListHandle[] m_ShadowRendererListsHandles;
|
|
|
|
private partial class PassData
|
|
{
|
|
internal RendererListHandle[] shadowRendererListsHdl;
|
|
internal UniversalLightData lightData;
|
|
internal int maxAreaLights;
|
|
}
|
|
|
|
partial void ConfigureShadowSettingsRenderGraph(int maxShadowCastingLights)
|
|
{
|
|
if (m_ShadowRendererListsHandles == null || m_ShadowRendererListsHandles.Length != maxShadowCastingLights)
|
|
{
|
|
m_ShadowRendererListsHandles = new RendererListHandle[maxShadowCastingLights];
|
|
}
|
|
if (m_PassData != null)
|
|
{
|
|
m_PassData.shadowRendererListsHdl = m_ShadowRendererListsHandles;
|
|
}
|
|
}
|
|
|
|
partial void EnsureMappingArraysRenderGraph()
|
|
{
|
|
if (m_ShadowRendererListsHandles == null || m_ShadowRendererListsHandles.Length != m_ShadowSettings.maxShadowCastingLights)
|
|
{
|
|
m_ShadowRendererListsHandles = new RendererListHandle[m_ShadowSettings.maxShadowCastingLights];
|
|
}
|
|
}
|
|
|
|
partial void ResetMappingArraysRenderGraph()
|
|
{
|
|
if (m_ShadowRendererListsHandles != null)
|
|
{
|
|
Array.Clear(m_ShadowRendererListsHandles, 0, m_ShadowRendererListsHandles.Length);
|
|
}
|
|
}
|
|
|
|
public override void RecordRenderGraph(RenderGraph renderGraph, ContextContainer frameData)
|
|
{
|
|
UniversalLightData lightData = frameData.Get<UniversalLightData>();
|
|
UniversalCameraData cameraData = frameData.Get<UniversalCameraData>();
|
|
UniversalRenderingData renderingData = frameData.Get<UniversalRenderingData>();
|
|
UniversalShadowData shadowData = frameData.Get<UniversalShadowData>();
|
|
|
|
using (var builder = renderGraph.AddUnsafePass<PassData>("AreaLighting Setup", out var passData, m_ProfilingSampler))
|
|
{
|
|
// Disable pass culling first - this pass must always execute to set up global textures and buffers
|
|
builder.AllowPassCulling(false);
|
|
builder.AllowGlobalStateModification(true);
|
|
|
|
passData.lightData = lightData;
|
|
passData.maxAreaLights = m_LightSettings.maxAreaLights;
|
|
|
|
builder.SetRenderFunc((PassData data, UnsafeGraphContext context) =>
|
|
{
|
|
// Convert UnsafeCommandBuffer to CommandBuffer for methods that require CommandBuffer
|
|
CommandBuffer cmd = CommandBufferHelpers.GetNativeCommandBuffer(context.cmd);
|
|
|
|
// 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 visibleLights = data.lightData.visibleLights;
|
|
var areaLightDataArray = BGAreaLightManager.BuildAreaLightDataArray(visibleLights, data.lightData.mainLightIndex, data.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);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Shadow pass - render area light shadows if any
|
|
if (m_ValidShadowCastingLightsCount > 0 || m_CreateEmptyShadowmap)
|
|
{
|
|
using (var builder = renderGraph.AddRasterRenderPass<PassData>("AreaLight Shadows", out var passData, m_ShadowProfilingSampler))
|
|
{
|
|
InitPassData(ref passData, cameraData, lightData, shadowData);
|
|
InitRendererLists(ref renderingData.cullResults, ref passData, renderGraph);
|
|
|
|
TextureHandle shadowTexture;
|
|
if (!m_CreateEmptyShadowmap)
|
|
{
|
|
for (int globalShadowSliceIndex = 0; globalShadowSliceIndex < m_ShadowSliceToAreaLightIndex.Count; ++globalShadowSliceIndex)
|
|
{
|
|
builder.UseRendererList(passData.shadowRendererListsHdl[globalShadowSliceIndex]);
|
|
}
|
|
|
|
shadowTexture = UniversalRenderer.CreateRenderGraphTexture(renderGraph, m_AreaLightShadowDescriptor, "_AreaLightsShadowmapTexture", true, FilterMode.Bilinear);
|
|
builder.SetRenderAttachmentDepth(shadowTexture, AccessFlags.Write);
|
|
}
|
|
else
|
|
{
|
|
shadowTexture = renderGraph.defaultResources.defaultShadowTexture;
|
|
}
|
|
|
|
TextureDesc descriptor = shadowTexture.GetDescriptor(renderGraph);
|
|
passData.allocatedShadowAtlasSize = new Vector2Int(descriptor.width, descriptor.height);
|
|
|
|
builder.AllowGlobalStateModification(true);
|
|
|
|
if (shadowTexture.IsValid())
|
|
{
|
|
builder.SetGlobalTextureAfterPass(shadowTexture, AreaLightingShaderIDs._AreaLightsShadowmapTexture);
|
|
}
|
|
|
|
builder.SetRenderFunc((PassData data, RasterGraphContext context) =>
|
|
{
|
|
RasterCommandBuffer rasterCommandBuffer = context.cmd;
|
|
if (!data.emptyShadowmap)
|
|
{
|
|
data.pass.RenderAreaLightShadowmapAtlas(rasterCommandBuffer, ref data);
|
|
}
|
|
else
|
|
{
|
|
SetShadowParamsForEmptyShadowmap(rasterCommandBuffer);
|
|
}
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
private void InitRendererLists(ref CullingResults cullResults, ref PassData passData, RenderGraph renderGraph)
|
|
{
|
|
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.shadowRendererListsHdl[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.shadowRendererListsHdl[shadowSliceIndex] = renderGraph.CreateRendererList(param);
|
|
}
|
|
}
|
|
|
|
private void InitPassData(ref PassData passData, UniversalCameraData cameraData, UniversalLightData lightData, UniversalShadowData shadowData)
|
|
{
|
|
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;
|
|
passData.shadowRendererListsHdl = m_ShadowRendererListsHandles;
|
|
}
|
|
|
|
private void SetupAreaLightsShadowReceiverConstants(RasterCommandBuffer 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(RasterCommandBuffer 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(RasterCommandBuffer 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;
|
|
}
|
|
}
|
|
|
|
private void RenderAreaLightShadowmapAtlas(RasterCommandBuffer 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.shadowRendererListsHdl[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 SetShadowBias(RasterCommandBuffer cmd, Vector4 shadowBias)
|
|
{
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._ShadowBias, shadowBias);
|
|
}
|
|
|
|
private void SetLightPosition(RasterCommandBuffer cmd, Vector3 lightPosition)
|
|
{
|
|
cmd.SetGlobalVector(AreaLightingShaderIDs._LightPosition,
|
|
new Vector4(lightPosition.x, lightPosition.y, lightPosition.z, 1.0f));
|
|
}
|
|
|
|
private void RenderShadowSlice(RasterCommandBuffer 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);
|
|
|
|
if (shadowRendererList.isValid)
|
|
{
|
|
cmd.DrawRendererList(shadowRendererList);
|
|
}
|
|
|
|
cmd.DisableScissorRect();
|
|
cmd.SetGlobalDepthBias(0.0f, 0.0f);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|