Files
2026-04-16 04:58:10 +09:00

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