2026-04-16 오브젝트 그림자
This commit is contained in:
@@ -0,0 +1,308 @@
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1c5279b325c6fc74bbc057ef781801be
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Passes/AreaLightingPass.Legacy.cs
|
||||
uploadId: 884030
|
||||
@@ -0,0 +1,355 @@
|
||||
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
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ccba3711a0907224992b4d3ff8af9a13
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Passes/AreaLightingPass.RenderGraph.cs
|
||||
uploadId: 884030
|
||||
@@ -0,0 +1,445 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace BadDog.Rendering.AreaLight
|
||||
{
|
||||
public partial class AreaLightingPass : ScriptableRenderPass
|
||||
{
|
||||
private static readonly ProfilingSampler m_ProfilingSampler = new ProfilingSampler("AreaLighting Setup");
|
||||
private static readonly ProfilingSampler m_ShadowProfilingSampler = new ProfilingSampler("AreaLight Shadows");
|
||||
|
||||
private const int k_ShadowmapBufferBits = 16;
|
||||
private const int k_MaxAreaLightShadowCount = 8;
|
||||
|
||||
private static readonly Matrix4x4[] s_EmptyShadowMatrices = new Matrix4x4[k_MaxAreaLightShadowCount]
|
||||
{
|
||||
Matrix4x4.identity, Matrix4x4.identity, Matrix4x4.identity, Matrix4x4.identity,
|
||||
Matrix4x4.identity, Matrix4x4.identity, Matrix4x4.identity, Matrix4x4.identity
|
||||
};
|
||||
|
||||
private static readonly Vector4[] s_EmptyShadowParams = new Vector4[k_MaxAreaLightShadowCount]
|
||||
{
|
||||
Vector4.zero, Vector4.zero, Vector4.zero, Vector4.zero,
|
||||
Vector4.zero, Vector4.zero, Vector4.zero, Vector4.zero
|
||||
};
|
||||
private AreaLightSettings m_LightSettings;
|
||||
private AreaLightShadowSettings m_ShadowSettings;
|
||||
|
||||
private RTHandle m_AreaLightsShadowmapHandle;
|
||||
private Matrix4x4[] m_AreaLightShadowSliceIndexTo_WorldShadowMatrix; // Indexed by areaLightIndex
|
||||
private Vector4[] m_AreaLightIndexToShadowParams; // Indexed by areaLightIndex
|
||||
private ShadowSliceData[] m_AreaLightsShadowSlices; // Indexed by shadowSliceIndex
|
||||
private int m_ValidShadowCastingLightsCount = 0;
|
||||
private short[] m_VisibleLightIndexToAreaLightIndex;
|
||||
private short[] m_AreaLightIndexToVisibleLightIndex;
|
||||
private bool[] m_VisibleLightIndexToIsCastingShadows;
|
||||
private readonly List<short> m_ShadowSliceToAreaLightIndex = new(); // shadowSliceIndex -> areaLightIndex
|
||||
private RendererList[] m_ShadowRendererLists; // Indexed by shadowSliceIndex
|
||||
private bool m_CreateEmptyShadowmap;
|
||||
private bool m_EmptyShadowmapNeedsClear = true;
|
||||
private int m_RenderTargetWidth;
|
||||
private int m_RenderTargetHeight;
|
||||
private ShadowData m_ShadowDataCache;
|
||||
private RenderTextureDescriptor m_AreaLightShadowDescriptor;
|
||||
private static readonly List<int> s_AreaLightRequestedResolutions = new();
|
||||
|
||||
private partial class PassData
|
||||
{
|
||||
internal AreaLightingPass pass;
|
||||
internal bool emptyShadowmap;
|
||||
internal Vector2Int allocatedShadowAtlasSize;
|
||||
internal RendererList[] shadowRendererLists;
|
||||
internal NativeArray<VisibleLight> visibleLights;
|
||||
internal Matrix4x4 viewMatrix;
|
||||
internal Matrix4x4 projectionMatrix;
|
||||
internal Camera camera;
|
||||
}
|
||||
|
||||
private PassData m_PassData;
|
||||
|
||||
partial void ConfigureShadowSettingsRenderGraph(int maxShadowCastingLights);
|
||||
|
||||
public AreaLightingPass()
|
||||
{
|
||||
renderPassEvent = RenderPassEvent.AfterRenderingShadows;
|
||||
|
||||
m_AreaLightShadowSliceIndexTo_WorldShadowMatrix = new Matrix4x4[k_MaxAreaLightShadowCount];
|
||||
m_AreaLightIndexToShadowParams = new Vector4[k_MaxAreaLightShadowCount];
|
||||
m_AreaLightsShadowSlices = new ShadowSliceData[k_MaxAreaLightShadowCount];
|
||||
m_ShadowRendererLists = new RendererList[k_MaxAreaLightShadowCount];
|
||||
|
||||
m_PassData = new PassData
|
||||
{
|
||||
shadowRendererLists = m_ShadowRendererLists
|
||||
};
|
||||
}
|
||||
|
||||
private void ConfigureAreaLightingSettings(AreaLightSettings lightSettings,
|
||||
AreaLightShadowSettings shadowSettings)
|
||||
{
|
||||
m_LightSettings = lightSettings;
|
||||
m_ShadowSettings = shadowSettings;
|
||||
|
||||
PreIntegratedFGD.instance.SetShader(PreIntegratedFGD.FGDIndex.FGD_GGXAndDisneyDiffuse, lightSettings.preIntegratedFGD_GGXDisneyDiffuse);
|
||||
PreIntegratedFGD.instance.SetShader(PreIntegratedFGD.FGDIndex.FGD_CharlieAndFabricLambert, lightSettings.preIntegratedFGD_CharlieFabricLambert);
|
||||
PreIntegratedFGD.instance.SetShader(PreIntegratedFGD.FGDIndex.FGD_Marschner, lightSettings.preIntegratedFGD_Marschner);
|
||||
|
||||
ConfigureShadowSettingsRenderGraph(shadowSettings.maxShadowCastingLights);
|
||||
}
|
||||
|
||||
public void Setup(AreaLightSettings lightSettings, AreaLightShadowSettings shadowSettings, RenderingData renderingData)
|
||||
{
|
||||
ConfigureAreaLightingSettings(lightSettings, shadowSettings);
|
||||
PrepareAreaLightShadows(renderingData);
|
||||
}
|
||||
|
||||
public override void OnCameraCleanup(CommandBuffer cmd)
|
||||
{
|
||||
CoreUtils.SetKeyword(cmd, AreaLightingShaderIDs.ENABLE_BG_AREA_LIGHTING_KEYWORD, false);
|
||||
}
|
||||
|
||||
public void Dispose()
|
||||
{
|
||||
m_AreaLightsShadowmapHandle?.Release();
|
||||
}
|
||||
|
||||
|
||||
private void PrepareAreaLightShadows(RenderingData renderingData)
|
||||
{
|
||||
m_CreateEmptyShadowmap = false;
|
||||
m_ValidShadowCastingLightsCount = 0;
|
||||
m_RenderTargetWidth = m_ShadowSettings.shadowAtlasResolution;
|
||||
m_RenderTargetHeight = m_ShadowSettings.shadowAtlasResolution;
|
||||
|
||||
var cameraData = renderingData.cameraData;
|
||||
var lightData = renderingData.lightData;
|
||||
var shadowData = renderingData.shadowData;
|
||||
m_ShadowDataCache = shadowData;
|
||||
NativeArray<VisibleLight> visibleLights = lightData.visibleLights;
|
||||
if (!visibleLights.IsCreated || visibleLights.Length == 0)
|
||||
{
|
||||
m_CreateEmptyShadowmap = true;
|
||||
m_EmptyShadowmapNeedsClear = true;
|
||||
AllocateShadowmapIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
EnsureMappingArrays(visibleLights.Length);
|
||||
ResetMappingArrays(visibleLights.Length);
|
||||
|
||||
PrepareAreaLightRequestedResolutions(visibleLights, lightData.mainLightIndex);
|
||||
|
||||
var atlasLayout = new AreaLightsShadowAtlasLayout(lightData, shadowData, cameraData, s_AreaLightRequestedResolutions, m_ShadowSettings.maxShadowCastingLights, m_ShadowSettings.shadowAtlasResolution);
|
||||
|
||||
int targetShadowCount = Math.Min(atlasLayout.GetTotalShadowSlicesCount(), m_ShadowSettings.maxShadowCastingLights);
|
||||
if (targetShadowCount <= 0)
|
||||
{
|
||||
m_CreateEmptyShadowmap = true;
|
||||
m_EmptyShadowmapNeedsClear = true;
|
||||
AllocateShadowmapIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
// Trim shadow atlas dimensions if possible (to avoid allocating texture space that will not be used)
|
||||
int atlasMaxX = 0;
|
||||
int atlasMaxY = 0;
|
||||
for (int i = 0; i < targetShadowCount; ++i)
|
||||
{
|
||||
var request = atlasLayout.GetSortedShadowResolutionRequest(i);
|
||||
int maxX = request.offsetX + request.allocatedResolution;
|
||||
int maxY = request.offsetY + request.allocatedResolution;
|
||||
if (maxX > atlasMaxX) atlasMaxX = maxX;
|
||||
if (maxY > atlasMaxY) atlasMaxY = maxY;
|
||||
}
|
||||
|
||||
// Use power-of-two dimensions (might perform better on some hardware)
|
||||
m_RenderTargetWidth = Mathf.NextPowerOfTwo(atlasMaxX);
|
||||
m_RenderTargetHeight = Mathf.NextPowerOfTwo(atlasMaxY);
|
||||
|
||||
int areaLightIndex = 0;
|
||||
int shadowSliceIndex = 0;
|
||||
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length && areaLightIndex < m_LightSettings.maxAreaLights; ++visibleLightIndex)
|
||||
{
|
||||
if (visibleLightIndex == lightData.mainLightIndex)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ref readonly VisibleLight visibleLight = ref visibleLights.UnsafeElementAt(visibleLightIndex);
|
||||
Light light = visibleLight.light;
|
||||
|
||||
// Only registered BGAreaLight rectangle lights participate in the area light system.
|
||||
if (visibleLight.lightType != LightType.Rectangle || light == null
|
||||
|| !BGAreaLightManager.TryGetRegisteredAreaLight(light, out _))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Default shadow params: no shadow (w = -1 means no shadow)
|
||||
// z = 0 means Spot-like light type identifier (for shader compatibility)
|
||||
m_AreaLightIndexToShadowParams[areaLightIndex] = new Vector4(0f, 0f, 0f, -1f);
|
||||
m_AreaLightShadowSliceIndexTo_WorldShadowMatrix[areaLightIndex] = Matrix4x4.identity;
|
||||
m_AreaLightIndexToVisibleLightIndex[areaLightIndex] = (short)visibleLightIndex;
|
||||
m_VisibleLightIndexToAreaLightIndex[visibleLightIndex] = (short)areaLightIndex;
|
||||
|
||||
// Check if this light casts shadows (light type and null check already done above)
|
||||
bool canCastShadows = AreaShadowUtils.CanLightCastShadows(light);
|
||||
// Ensure we don't exceed array bounds: shadowSliceIndex < maxShadowCastingLights
|
||||
if (m_ShadowSettings.shadowFilter != AreaLightShadowSettings.BGAreaLightShadowMode.Off
|
||||
&& canCastShadows
|
||||
&& atlasLayout.HasSpaceForLight(visibleLightIndex)
|
||||
&& shadowSliceIndex < targetShadowCount
|
||||
&& shadowSliceIndex < m_ShadowSettings.maxShadowCastingLights)
|
||||
{
|
||||
var resolutionRequest = atlasLayout.GetSliceShadowResolutionRequest(visibleLightIndex, 0);
|
||||
int resolution = Mathf.Clamp(resolutionRequest.allocatedResolution, AreaShadowUtils.MinimalAreaLightShadowResolution(), m_ShadowSettings.shadowAtlasResolution);
|
||||
|
||||
if (TryComputeAreaLightShadowSlice(visibleLight, visibleLightIndex, resolution, out var viewMatrix, out var projMatrix, out var splitData, out var shadowTransform))
|
||||
{
|
||||
// Store shadow slice data using shadowSliceIndex for rendering
|
||||
ref ShadowSliceData slice = ref m_AreaLightsShadowSlices[shadowSliceIndex];
|
||||
slice.viewMatrix = viewMatrix;
|
||||
slice.projectionMatrix = projMatrix;
|
||||
slice.splitData = splitData;
|
||||
slice.offsetX = resolutionRequest.offsetX;
|
||||
slice.offsetY = resolutionRequest.offsetY;
|
||||
slice.resolution = resolution;
|
||||
slice.shadowTransform = shadowTransform;
|
||||
|
||||
float shadowStrength = light.shadowStrength;
|
||||
|
||||
// Store shadow parameters using areaLightIndex (matches BGAreaLightManager index)
|
||||
// x: shadowStrength, y: softShadows (1.0), z: lightType (0.0 = Spot-like), w: areaLightIndex
|
||||
m_AreaLightIndexToShadowParams[areaLightIndex] = new Vector4(shadowStrength, 1f, 0f, (float)areaLightIndex);
|
||||
m_AreaLightShadowSliceIndexTo_WorldShadowMatrix[areaLightIndex] = shadowTransform;
|
||||
|
||||
// Map shadowSliceIndex to areaLightIndex for rendering loop
|
||||
m_ShadowSliceToAreaLightIndex.Add((short)areaLightIndex);
|
||||
m_VisibleLightIndexToIsCastingShadows[visibleLightIndex] = true;
|
||||
|
||||
shadowSliceIndex++;
|
||||
}
|
||||
}
|
||||
|
||||
areaLightIndex++;
|
||||
}
|
||||
|
||||
m_ValidShadowCastingLightsCount = shadowSliceIndex;
|
||||
|
||||
// Clear remaining entries
|
||||
for (int i = areaLightIndex; i < m_AreaLightShadowSliceIndexTo_WorldShadowMatrix.Length; ++i)
|
||||
{
|
||||
m_AreaLightShadowSliceIndexTo_WorldShadowMatrix[i] = Matrix4x4.identity;
|
||||
m_AreaLightIndexToShadowParams[i] = Vector4.zero;
|
||||
}
|
||||
|
||||
if (m_ValidShadowCastingLightsCount <= 0)
|
||||
{
|
||||
m_CreateEmptyShadowmap = true;
|
||||
m_ShadowSliceToAreaLightIndex.Clear();
|
||||
m_EmptyShadowmapNeedsClear = true;
|
||||
AllocateShadowmapIfNeeded();
|
||||
return;
|
||||
}
|
||||
|
||||
m_CreateEmptyShadowmap = false;
|
||||
m_EmptyShadowmapNeedsClear = true;
|
||||
|
||||
float oneOverAtlasWidth = 1f / m_RenderTargetWidth;
|
||||
float oneOverAtlasHeight = 1f / m_RenderTargetHeight;
|
||||
|
||||
// Apply atlas transform to shadow matrices
|
||||
for (int i = 0; i < shadowSliceIndex; ++i)
|
||||
{
|
||||
int areaLightIdx = m_ShadowSliceToAreaLightIndex[i];
|
||||
ref ShadowSliceData slice = ref m_AreaLightsShadowSlices[i];
|
||||
|
||||
Matrix4x4 sliceTransform = Matrix4x4.identity;
|
||||
sliceTransform.m00 = slice.resolution * oneOverAtlasWidth;
|
||||
sliceTransform.m11 = slice.resolution * oneOverAtlasHeight;
|
||||
sliceTransform.m03 = slice.offsetX * oneOverAtlasWidth;
|
||||
sliceTransform.m13 = slice.offsetY * oneOverAtlasHeight;
|
||||
|
||||
Matrix4x4 worldToShadow = sliceTransform * slice.shadowTransform;
|
||||
m_AreaLightShadowSliceIndexTo_WorldShadowMatrix[areaLightIdx] = worldToShadow;
|
||||
slice.shadowTransform = worldToShadow;
|
||||
}
|
||||
|
||||
UpdateTextureDescriptorIfNeeded();
|
||||
AllocateShadowmapIfNeeded();
|
||||
}
|
||||
|
||||
partial void AllocateShadowmapIfNeeded();
|
||||
|
||||
private void UpdateTextureDescriptorIfNeeded()
|
||||
{
|
||||
if ( m_AreaLightShadowDescriptor.width != m_RenderTargetWidth
|
||||
|| m_AreaLightShadowDescriptor.height != m_RenderTargetHeight
|
||||
|| m_AreaLightShadowDescriptor.depthBufferBits != k_ShadowmapBufferBits
|
||||
|| m_AreaLightShadowDescriptor.colorFormat != RenderTextureFormat.Shadowmap)
|
||||
{
|
||||
m_AreaLightShadowDescriptor = new RenderTextureDescriptor(m_RenderTargetWidth, m_RenderTargetHeight, RenderTextureFormat.Shadowmap, k_ShadowmapBufferBits);
|
||||
}
|
||||
}
|
||||
|
||||
void PrepareAreaLightRequestedResolutions(NativeArray<VisibleLight> visibleLights, int mainLightIndex)
|
||||
{
|
||||
int requiredLength = visibleLights.Length;
|
||||
if (s_AreaLightRequestedResolutions.Capacity < requiredLength)
|
||||
{
|
||||
s_AreaLightRequestedResolutions.Capacity = requiredLength;
|
||||
}
|
||||
|
||||
s_AreaLightRequestedResolutions.Clear();
|
||||
|
||||
int defaultResolution = Mathf.Max(m_ShadowSettings.shadowAtlasResolution, AreaShadowUtils.MinimalAreaLightShadowResolution());
|
||||
|
||||
for (int i = 0; i < visibleLights.Length; ++i)
|
||||
{
|
||||
ref readonly VisibleLight visibleLight = ref visibleLights.UnsafeElementAt(i);
|
||||
bool isValidAreaLight = AreaShadowUtils.IsValidShadowCastingLight(visibleLight, mainLightIndex, i);
|
||||
|
||||
int requestedResolution = 0;
|
||||
|
||||
if (isValidAreaLight)
|
||||
{
|
||||
requestedResolution = defaultResolution;
|
||||
|
||||
Light unityLight = visibleLight.light;
|
||||
if (unityLight != null && BGAreaLightManager.TryGetRegisteredAreaLight(unityLight, out var areaLight))
|
||||
{
|
||||
int custom = areaLight.GetCustomShadowResolution();
|
||||
if (custom > 0)
|
||||
{
|
||||
int minRes = AreaShadowUtils.MinimalAreaLightShadowResolution();
|
||||
requestedResolution = Mathf.Max(minRes, Mathf.NextPowerOfTwo(custom));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
s_AreaLightRequestedResolutions.Add(requestedResolution);
|
||||
}
|
||||
}
|
||||
|
||||
void EnsureMappingArrays(int visibleLightCount)
|
||||
{
|
||||
if (m_AreaLightIndexToVisibleLightIndex == null || m_AreaLightIndexToVisibleLightIndex.Length != m_LightSettings.maxAreaLights)
|
||||
{
|
||||
m_AreaLightIndexToVisibleLightIndex = new short[m_LightSettings.maxAreaLights];
|
||||
}
|
||||
|
||||
if (m_VisibleLightIndexToAreaLightIndex == null || m_VisibleLightIndexToAreaLightIndex.Length != visibleLightCount)
|
||||
{
|
||||
m_VisibleLightIndexToAreaLightIndex = new short[visibleLightCount];
|
||||
}
|
||||
|
||||
if (m_VisibleLightIndexToIsCastingShadows == null || m_VisibleLightIndexToIsCastingShadows.Length != visibleLightCount)
|
||||
{
|
||||
m_VisibleLightIndexToIsCastingShadows = new bool[visibleLightCount];
|
||||
}
|
||||
|
||||
if (m_ShadowRendererLists == null || m_ShadowRendererLists.Length != m_ShadowSettings.maxShadowCastingLights)
|
||||
{
|
||||
m_ShadowRendererLists = new RendererList[m_ShadowSettings.maxShadowCastingLights];
|
||||
}
|
||||
|
||||
EnsureMappingArraysRenderGraph();
|
||||
}
|
||||
|
||||
partial void EnsureMappingArraysRenderGraph();
|
||||
|
||||
void ResetMappingArrays(int visibleLightCount)
|
||||
{
|
||||
if (m_VisibleLightIndexToAreaLightIndex != null)
|
||||
{
|
||||
for (int i = 0; i < m_VisibleLightIndexToAreaLightIndex.Length; ++i)
|
||||
{
|
||||
m_VisibleLightIndexToAreaLightIndex[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_VisibleLightIndexToIsCastingShadows != null)
|
||||
{
|
||||
Array.Clear(m_VisibleLightIndexToIsCastingShadows, 0, m_VisibleLightIndexToIsCastingShadows.Length);
|
||||
}
|
||||
|
||||
if (m_AreaLightIndexToVisibleLightIndex != null)
|
||||
{
|
||||
for (int i = 0; i < m_AreaLightIndexToVisibleLightIndex.Length; ++i)
|
||||
{
|
||||
m_AreaLightIndexToVisibleLightIndex[i] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_ShadowRendererLists != null)
|
||||
{
|
||||
Array.Clear(m_ShadowRendererLists, 0, m_ShadowRendererLists.Length);
|
||||
}
|
||||
|
||||
ResetMappingArraysRenderGraph();
|
||||
|
||||
m_ShadowSliceToAreaLightIndex.Clear();
|
||||
}
|
||||
|
||||
partial void ResetMappingArraysRenderGraph();
|
||||
|
||||
bool TryComputeAreaLightShadowSlice(in VisibleLight visibleLight, int visibleLightIndex, int resolution,
|
||||
out Matrix4x4 viewMatrix, out Matrix4x4 projMatrix, out ShadowSplitData splitData, out Matrix4x4 shadowTransform)
|
||||
{
|
||||
viewMatrix = Matrix4x4.identity;
|
||||
projMatrix = Matrix4x4.identity;
|
||||
splitData = new ShadowSplitData();
|
||||
shadowTransform = Matrix4x4.identity;
|
||||
|
||||
Light unityLight = visibleLight.light;
|
||||
|
||||
if (unityLight == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!AreaShadowUtils.TryGetAreaLightParameters(unityLight, out float areaLightShadowCone,
|
||||
out Vector2 areaSize))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
float forwardOffset = AreaShadowUtils.GetAreaLightOffsetForShadows(areaSize, areaLightShadowCone);
|
||||
|
||||
float normalBias;
|
||||
float nearPlane;
|
||||
if (BGAreaLightManager.TryGetRegisteredAreaLight(unityLight, out var areaLightComp))
|
||||
{
|
||||
normalBias = areaLightComp.GetShadowNormalBias();
|
||||
nearPlane = areaLightComp.GetShadowNearPlane();
|
||||
}
|
||||
else
|
||||
{
|
||||
normalBias = unityLight.shadowNormalBias;
|
||||
nearPlane = unityLight.shadowNearPlane;
|
||||
}
|
||||
|
||||
AreaShadowUtils.ExtractRectangleAreaLightMatrix(
|
||||
visibleLight,
|
||||
forwardOffset,
|
||||
areaLightShadowCone,
|
||||
nearPlane,
|
||||
areaSize,
|
||||
resolution,
|
||||
normalBias,
|
||||
SystemInfo.usesReversedZBuffer,
|
||||
out shadowTransform,
|
||||
out viewMatrix,
|
||||
out projMatrix,
|
||||
out splitData);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 3f059a5dc5b3b584b82d9a7b518cb9c0
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Passes/AreaLightingPass.cs
|
||||
uploadId: 884030
|
||||
@@ -0,0 +1,383 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace BadDog.Rendering.AreaLight
|
||||
{
|
||||
internal struct AreaLightsShadowAtlasLayout
|
||||
{
|
||||
internal struct ShadowResolutionRequest
|
||||
{
|
||||
public ushort visibleLightIndex;
|
||||
public ushort requestedResolution;
|
||||
public ushort offsetX; // x coordinate of the square area allocated in the atlas for this shadow map
|
||||
public ushort offsetY; // y coordinate of the square area allocated in the atlas for this shadow map
|
||||
public ushort allocatedResolution; // width of the square area allocated in the atlas for this shadow map
|
||||
}
|
||||
|
||||
// Static fields used to avoid GC allocs of intermediate computations
|
||||
static List<RectInt> s_UnusedAtlasSquareAreas; // This list tracks space available in the atlas
|
||||
static List<ShadowResolutionRequest> s_ShadowResolutionRequests; // intermediate array used to compute the final resolution of each shadow slice rendered in the frame
|
||||
static float[] s_VisibleLightIndexToCameraSquareDistance; // stores for each shadowed additional light its (squared) distance to camera ; used to sub-sort shadow requests according to how close their casting light is
|
||||
static Func<ShadowResolutionRequest, ShadowResolutionRequest, int> s_CompareShadowResolutionRequest;
|
||||
static ShadowResolutionRequest[] s_SortedShadowResolutionRequests;
|
||||
|
||||
NativeArray<ShadowResolutionRequest> m_SortedShadowResolutionRequests;
|
||||
NativeArray<int> m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex; // for each visible light, store the index of its first shadow slice in m_SortedShadowResolutionRequests (for quicker access)
|
||||
int m_TotalShadowSlicesCount;
|
||||
int m_TotalShadowResolutionRequestCount;
|
||||
bool m_TooManyShadowMaps;
|
||||
int m_ShadowSlicesScaleFactor;
|
||||
int m_AtlasSize;
|
||||
|
||||
public AreaLightsShadowAtlasLayout(LightData lightData, ShadowData shadowData, CameraData cameraData, int maxShadowCastingLights)
|
||||
: this(lightData, shadowData, cameraData, null, maxShadowCastingLights, 0)
|
||||
{
|
||||
}
|
||||
|
||||
public AreaLightsShadowAtlasLayout(LightData lightData, ShadowData shadowData, CameraData cameraData, List<int> overrideRequestedResolutions, int maxShadowCastingLights, int customAtlasSize = 0)
|
||||
{
|
||||
NativeArray<VisibleLight> visibleLights = lightData.visibleLights;
|
||||
int numberOfVisibleLights = visibleLights.Length;
|
||||
|
||||
if (s_UnusedAtlasSquareAreas == null)
|
||||
{
|
||||
s_UnusedAtlasSquareAreas = new List<RectInt>();
|
||||
}
|
||||
|
||||
if (s_ShadowResolutionRequests == null)
|
||||
{
|
||||
s_ShadowResolutionRequests = new List<ShadowResolutionRequest>();
|
||||
}
|
||||
|
||||
if (s_VisibleLightIndexToCameraSquareDistance == null || s_VisibleLightIndexToCameraSquareDistance.Length < numberOfVisibleLights)
|
||||
{
|
||||
s_VisibleLightIndexToCameraSquareDistance = new float[numberOfVisibleLights];
|
||||
}
|
||||
|
||||
if (s_CompareShadowResolutionRequest == null)
|
||||
{
|
||||
s_CompareShadowResolutionRequest = CreateCompareShadowResolutionRequesPredicate();
|
||||
}
|
||||
|
||||
int newCapacity = maxShadowCastingLights;
|
||||
|
||||
if (s_UnusedAtlasSquareAreas.Capacity < newCapacity)
|
||||
{
|
||||
s_UnusedAtlasSquareAreas.Capacity = newCapacity;
|
||||
}
|
||||
|
||||
if (s_ShadowResolutionRequests.Count < numberOfVisibleLights)
|
||||
{
|
||||
s_ShadowResolutionRequests.Capacity = numberOfVisibleLights;
|
||||
int diff = numberOfVisibleLights - s_ShadowResolutionRequests.Count + 1;
|
||||
for (int i = 0; i < diff; i++)
|
||||
{
|
||||
s_ShadowResolutionRequests.Add(new ShadowResolutionRequest());
|
||||
}
|
||||
}
|
||||
|
||||
s_UnusedAtlasSquareAreas.Clear();
|
||||
|
||||
ushort totalShadowResolutionRequestsCount = 0; // Number of shadow slices that we would need for all shadowed area lights in the scene. We might have to ignore some of those requests if they do not fit in the shadow atlas.
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < visibleLights.Length; ++visibleLightIndex)
|
||||
{
|
||||
// Skip main directional light as it is not packed into the shadow atlas
|
||||
if (visibleLightIndex == lightData.mainLightIndex)
|
||||
{
|
||||
s_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = float.MaxValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
ref readonly VisibleLight vl = ref visibleLights.UnsafeElementAt(visibleLightIndex);
|
||||
Light light = vl.light;
|
||||
if (!AreaShadowUtils.IsValidShadowCastingLight(vl, lightData.mainLightIndex, visibleLightIndex))
|
||||
{
|
||||
s_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = float.MaxValue;
|
||||
continue;
|
||||
}
|
||||
|
||||
ushort visibleLightIndexUshort = (ushort)visibleLightIndex;
|
||||
|
||||
ushort requestedResolution = 0;
|
||||
|
||||
if (overrideRequestedResolutions != null && overrideRequestedResolutions.Count == numberOfVisibleLights)
|
||||
{
|
||||
requestedResolution = (ushort)overrideRequestedResolutions[visibleLightIndex];
|
||||
}
|
||||
else if (shadowData.resolution != null && shadowData.resolution.Count == numberOfVisibleLights)
|
||||
{
|
||||
requestedResolution = (ushort)shadowData.resolution[visibleLightIndex];
|
||||
}
|
||||
else
|
||||
{
|
||||
requestedResolution = 0;
|
||||
}
|
||||
|
||||
if (totalShadowResolutionRequestsCount >= s_ShadowResolutionRequests.Count)
|
||||
{
|
||||
s_ShadowResolutionRequests.Add(new ShadowResolutionRequest());
|
||||
}
|
||||
|
||||
ShadowResolutionRequest request = s_ShadowResolutionRequests[totalShadowResolutionRequestsCount];
|
||||
request.visibleLightIndex = visibleLightIndexUshort;
|
||||
request.requestedResolution = requestedResolution;
|
||||
s_ShadowResolutionRequests[totalShadowResolutionRequestsCount] = request;
|
||||
totalShadowResolutionRequestsCount++;
|
||||
|
||||
// mark this light as casting shadows
|
||||
s_VisibleLightIndexToCameraSquareDistance[visibleLightIndex] = (cameraData.worldSpaceCameraPos - light.transform.position).sqrMagnitude;
|
||||
}
|
||||
|
||||
if (s_SortedShadowResolutionRequests == null || s_SortedShadowResolutionRequests.Length < totalShadowResolutionRequestsCount)
|
||||
{
|
||||
s_SortedShadowResolutionRequests = new ShadowResolutionRequest[totalShadowResolutionRequestsCount];
|
||||
}
|
||||
|
||||
for (int i = 0; i < totalShadowResolutionRequestsCount; ++i)
|
||||
{
|
||||
s_SortedShadowResolutionRequests[i] = s_ShadowResolutionRequests[i];
|
||||
}
|
||||
|
||||
// Sort shadow requests by priority (resolution, soft shadow, distance to camera, etc.)
|
||||
InsertionSort(s_SortedShadowResolutionRequests, 0, totalShadowResolutionRequestsCount);
|
||||
|
||||
m_SortedShadowResolutionRequests = new NativeArray<ShadowResolutionRequest>(s_SortedShadowResolutionRequests, Allocator.Temp);
|
||||
|
||||
// To avoid visual artifacts when there is not enough place in the atlas, we remove shadow slices that would be allocated a too small resolution.
|
||||
// When using uniform buffers (non-SSBO path), m_AreaLightShadowSliceIndexTo_WorldShadowMatrix.Length maps to _BGAreaLightWorldToShadow in Shadows.hlsl
|
||||
// We have to limit its size because uniform buffers cannot be higher than 64kb for some platforms.
|
||||
int totalShadowSlicesCount = Math.Min(totalShadowResolutionRequestsCount, maxShadowCastingLights); // Number of shadow slices that we will actually be able to fit in the shadow atlas without causing visual artifacts.
|
||||
int atlasSize = customAtlasSize > 0 ? customAtlasSize : shadowData.additionalLightsShadowmapWidth;
|
||||
|
||||
// Find biggest end index in m_SortedShadowResolutionRequests array, under which all shadow requests can be allocated a big enough shadow atlas slot, to not cause rendering artifacts
|
||||
bool allShadowsAfterStartIndexHaveEnoughResolution = false;
|
||||
int estimatedScaleFactor = 1;
|
||||
while (!allShadowsAfterStartIndexHaveEnoughResolution && totalShadowSlicesCount > 0)
|
||||
{
|
||||
ShadowResolutionRequest request = m_SortedShadowResolutionRequests[totalShadowSlicesCount - 1];
|
||||
estimatedScaleFactor = EstimateScaleFactorNeededToFitAllShadowsInAtlas(m_SortedShadowResolutionRequests, totalShadowSlicesCount, atlasSize);
|
||||
|
||||
// check if resolution of the least priority shadow slice request would be acceptable
|
||||
if (request.requestedResolution >= estimatedScaleFactor * AreaShadowUtils.MinimalAreaLightShadowResolution())
|
||||
{
|
||||
allShadowsAfterStartIndexHaveEnoughResolution = true;
|
||||
}
|
||||
else // Skip shadow requests for this light ; their resolution is too small to look any good
|
||||
{
|
||||
totalShadowSlicesCount--;
|
||||
}
|
||||
}
|
||||
|
||||
for (int sortedArrayIndex = totalShadowSlicesCount; sortedArrayIndex < m_SortedShadowResolutionRequests.Length; ++sortedArrayIndex)
|
||||
{
|
||||
m_SortedShadowResolutionRequests[sortedArrayIndex] = default; // Reset entries that we cannot fit in the atlas
|
||||
}
|
||||
|
||||
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex = new NativeArray<int>(visibleLights.Length, Allocator.Temp);
|
||||
|
||||
// Reset the reverse lookup array
|
||||
for (int visibleLightIndex = 0; visibleLightIndex < m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex.Length; ++visibleLightIndex)
|
||||
{
|
||||
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] = -1;
|
||||
}
|
||||
|
||||
// Update the reverse lookup array (starting from the end of the array, in order to use index of slice#0 in case a same visibleLight has several shadowSlices)
|
||||
for (int sortedArrayIndex = totalShadowSlicesCount - 1; sortedArrayIndex >= 0; --sortedArrayIndex)
|
||||
{
|
||||
int visibleLightIndex = s_SortedShadowResolutionRequests[sortedArrayIndex].visibleLightIndex;
|
||||
m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[visibleLightIndex] = sortedArrayIndex;
|
||||
}
|
||||
|
||||
// Assigns to each of the first totalShadowSlicesCount items in m_SortedShadowResolutionRequests a location in the shadow atlas based on requested resolutions.
|
||||
// If necessary, scales down shadow maps active in the frame, to make all of them fit in the atlas.
|
||||
bool allShadowSlicesFitInAtlas = false;
|
||||
bool tooManyShadows = false;
|
||||
int shadowSlicesScaleFactor = estimatedScaleFactor;
|
||||
|
||||
while (!allShadowSlicesFitInAtlas && !tooManyShadows)
|
||||
{
|
||||
s_UnusedAtlasSquareAreas.Clear();
|
||||
s_UnusedAtlasSquareAreas.Add(new RectInt(0, 0, atlasSize, atlasSize));
|
||||
allShadowSlicesFitInAtlas = true;
|
||||
|
||||
for (int shadowRequestIndex = 0; shadowRequestIndex < totalShadowSlicesCount; ++shadowRequestIndex)
|
||||
{
|
||||
var resolution = m_SortedShadowResolutionRequests[shadowRequestIndex].requestedResolution / shadowSlicesScaleFactor;
|
||||
|
||||
if (resolution < AreaShadowUtils.MinimalAreaLightShadowResolution())
|
||||
{
|
||||
tooManyShadows = true;
|
||||
break;
|
||||
}
|
||||
|
||||
bool foundSpaceInAtlas = false;
|
||||
|
||||
// Try to find free space in the atlas
|
||||
for (int unusedAtlasSquareAreaIndex = 0; unusedAtlasSquareAreaIndex < s_UnusedAtlasSquareAreas.Count; ++unusedAtlasSquareAreaIndex)
|
||||
{
|
||||
RectInt atlasArea = s_UnusedAtlasSquareAreas[unusedAtlasSquareAreaIndex];
|
||||
int atlasAreaWidth = atlasArea.width;
|
||||
if (atlasAreaWidth < resolution)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
int atlasAreaHeight = atlasArea.height;
|
||||
int atlasAreaX = atlasArea.x;
|
||||
int atlasAreaY = atlasArea.y;
|
||||
|
||||
// we can use this atlas area for the shadow request
|
||||
ref ShadowResolutionRequest shadowRequest = ref m_SortedShadowResolutionRequests.UnsafeElementAtMutable(shadowRequestIndex);
|
||||
shadowRequest.offsetX = (ushort)atlasAreaX;
|
||||
shadowRequest.offsetY = (ushort)atlasAreaY;
|
||||
shadowRequest.allocatedResolution = (ushort)resolution;
|
||||
|
||||
// this atlas space is not available anymore, so remove it from the list
|
||||
s_UnusedAtlasSquareAreas.RemoveAt(unusedAtlasSquareAreaIndex);
|
||||
|
||||
// make sure to split space so that the rest of this square area can be used
|
||||
int remainingShadowRequestsCount = totalShadowSlicesCount - shadowRequestIndex - 1; // (no need to add more than that)
|
||||
int newSquareAreasCount = 0;
|
||||
int newSquareAreaWidth = resolution; // we split the area in squares of same size
|
||||
int newSquareAreaHeight = resolution;
|
||||
int newSquareAreaX = atlasAreaX;
|
||||
int newSquareAreaY = atlasAreaY;
|
||||
|
||||
while (newSquareAreasCount < remainingShadowRequestsCount)
|
||||
{
|
||||
newSquareAreaX += newSquareAreaWidth;
|
||||
if (newSquareAreaX + newSquareAreaWidth > (atlasAreaX + atlasAreaWidth))
|
||||
{
|
||||
newSquareAreaX = atlasAreaX;
|
||||
newSquareAreaY += newSquareAreaHeight;
|
||||
if (newSquareAreaY + newSquareAreaHeight > (atlasAreaY + atlasAreaHeight))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// replace the space we removed previously by new smaller squares (inserting them in this order ensures shadow maps will be packed at the side of the atlas, without gaps)
|
||||
s_UnusedAtlasSquareAreas.Insert(unusedAtlasSquareAreaIndex + newSquareAreasCount, new RectInt(newSquareAreaX, newSquareAreaY, newSquareAreaWidth, newSquareAreaHeight));
|
||||
++newSquareAreasCount;
|
||||
}
|
||||
|
||||
foundSpaceInAtlas = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!foundSpaceInAtlas)
|
||||
{
|
||||
allShadowSlicesFitInAtlas = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!allShadowSlicesFitInAtlas && !tooManyShadows)
|
||||
{
|
||||
shadowSlicesScaleFactor *= 2;
|
||||
}
|
||||
}
|
||||
|
||||
m_TooManyShadowMaps = tooManyShadows;
|
||||
m_ShadowSlicesScaleFactor = shadowSlicesScaleFactor;
|
||||
m_TotalShadowSlicesCount = totalShadowSlicesCount;
|
||||
m_TotalShadowResolutionRequestCount = totalShadowResolutionRequestsCount;
|
||||
m_AtlasSize = atlasSize;
|
||||
}
|
||||
|
||||
public int GetTotalShadowSlicesCount() => m_TotalShadowSlicesCount;
|
||||
|
||||
public int GetTotalShadowResolutionRequestCount() => m_TotalShadowResolutionRequestCount;
|
||||
|
||||
public bool HasTooManyShadowMaps() => m_TooManyShadowMaps;
|
||||
|
||||
public int GetShadowSlicesScaleFactor() => m_ShadowSlicesScaleFactor;
|
||||
|
||||
public int GetAtlasSize() => m_AtlasSize;
|
||||
|
||||
public bool HasSpaceForLight(int originalVisibleLightIndex)
|
||||
{
|
||||
return m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[originalVisibleLightIndex] != -1;
|
||||
}
|
||||
|
||||
public ShadowResolutionRequest GetSortedShadowResolutionRequest(int sortedShadowResolutionRequestIndex)
|
||||
{
|
||||
return m_SortedShadowResolutionRequests[sortedShadowResolutionRequestIndex];
|
||||
}
|
||||
|
||||
public ShadowResolutionRequest GetSliceShadowResolutionRequest(int originalVisibleLightIndex, int sliceIndex)
|
||||
{
|
||||
int sortedShadowResolutionRequestIndex = m_VisibleLightIndexToSortedShadowResolutionRequestsFirstSliceIndex[originalVisibleLightIndex];
|
||||
return m_SortedShadowResolutionRequests[sortedShadowResolutionRequestIndex];
|
||||
}
|
||||
|
||||
public static void ClearStaticCaches()
|
||||
{
|
||||
s_UnusedAtlasSquareAreas = null;
|
||||
s_ShadowResolutionRequests = null;
|
||||
s_VisibleLightIndexToCameraSquareDistance = null;
|
||||
s_CompareShadowResolutionRequest = null;
|
||||
s_SortedShadowResolutionRequests = null;
|
||||
}
|
||||
|
||||
static int EstimateScaleFactorNeededToFitAllShadowsInAtlas(in NativeArray<ShadowResolutionRequest> shadowResolutionRequests, int endIndex, int atlasSize)
|
||||
{
|
||||
long totalTexelsInShadowAtlas = atlasSize * atlasSize;
|
||||
|
||||
long totalTexelsInShadowRequests = 0;
|
||||
for (int shadowRequestIndex = 0; shadowRequestIndex < endIndex; ++shadowRequestIndex)
|
||||
{
|
||||
totalTexelsInShadowRequests += shadowResolutionRequests[shadowRequestIndex].requestedResolution * shadowResolutionRequests[shadowRequestIndex].requestedResolution;
|
||||
}
|
||||
|
||||
int estimatedScaleFactor = 1;
|
||||
while (totalTexelsInShadowRequests > totalTexelsInShadowAtlas * estimatedScaleFactor * estimatedScaleFactor)
|
||||
{
|
||||
estimatedScaleFactor *= 2;
|
||||
}
|
||||
|
||||
return estimatedScaleFactor;
|
||||
}
|
||||
|
||||
// Sort array in decreasing requestedResolution order,
|
||||
// sub-sorting in light distance to camera
|
||||
// then grouping in increasing visibleIndex
|
||||
static Func<ShadowResolutionRequest, ShadowResolutionRequest, int> CreateCompareShadowResolutionRequesPredicate()
|
||||
{
|
||||
return (ShadowResolutionRequest curr, ShadowResolutionRequest other) =>
|
||||
{
|
||||
return (((curr.requestedResolution > other.requestedResolution)
|
||||
|| (curr.requestedResolution == other.requestedResolution && s_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] < s_VisibleLightIndexToCameraSquareDistance[other.visibleLightIndex])
|
||||
|| (curr.requestedResolution == other.requestedResolution && s_VisibleLightIndexToCameraSquareDistance[curr.visibleLightIndex] == s_VisibleLightIndexToCameraSquareDistance[other.visibleLightIndex] && curr.visibleLightIndex < other.visibleLightIndex)))
|
||||
? -1 : 1;
|
||||
};
|
||||
}
|
||||
|
||||
static void InsertionSort(ShadowResolutionRequest[] array, int startIndex, int lastIndex)
|
||||
{
|
||||
int i = startIndex + 1;
|
||||
|
||||
while (i < lastIndex)
|
||||
{
|
||||
var curr = array[i];
|
||||
int j = i - 1;
|
||||
|
||||
// Sort in priority order using the comparison function
|
||||
while ((j >= 0) && s_CompareShadowResolutionRequest(curr, array[j]) < 0)
|
||||
{
|
||||
array[j + 1] = array[j];
|
||||
j--;
|
||||
}
|
||||
|
||||
array[j + 1] = curr;
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9883d51b2827fc540ab57491ab8a6346
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Passes/AreaLightsShadowAtlasLayout.cs
|
||||
uploadId: 884030
|
||||
@@ -0,0 +1,331 @@
|
||||
using Unity.Mathematics;
|
||||
using UnityEngine;
|
||||
using UnityEngine.Rendering;
|
||||
using UnityEngine.Rendering.Universal;
|
||||
|
||||
namespace BadDog.Rendering.AreaLight
|
||||
{
|
||||
public static class AreaShadowUtils
|
||||
{
|
||||
internal const int k_MinimumAreaLightShadowResolution = 16;
|
||||
internal const float k_Sqrt2 = 1.4142135623730950488016887242097f;
|
||||
|
||||
public const float k_DefaultAreaLightShadowCone = 120.0f;
|
||||
public const float k_MinAreaLightShadowCone = 90.0f;
|
||||
public const float k_MaxAreaLightShadowCone = 179.0f;
|
||||
public const float k_MinShadowNearPlane = 0.01f;
|
||||
public const float k_MaxShadowNearPlane = 10.0f;
|
||||
|
||||
public static bool IsValidShadowCastingLight(LightData lightData, int index, LightType lightType,
|
||||
LightShadows lightShadows, float shadowStrength)
|
||||
{
|
||||
if (index == lightData.mainLightIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (lightType != LightType.Rectangle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return lightShadows != LightShadows.None && shadowStrength > 0f;
|
||||
}
|
||||
|
||||
public static bool IsValidShadowCastingLight(in VisibleLight visibleLight, int mainLightIndex, int visibleLightIndex)
|
||||
{
|
||||
if (visibleLightIndex == mainLightIndex)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (visibleLight.lightType != LightType.Rectangle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
Light light = visibleLight.light;
|
||||
if (!BGAreaLightManager.TryGetRegisteredAreaLight(light, out _))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return CanLightCastShadows(light);
|
||||
}
|
||||
|
||||
public static bool CanLightCastShadows(Light light)
|
||||
{
|
||||
if (light == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return light.shadows != LightShadows.None && light.shadowStrength > 0f;
|
||||
}
|
||||
|
||||
public static int MinimalAreaLightShadowResolution()
|
||||
{
|
||||
return k_MinimumAreaLightShadowResolution;
|
||||
}
|
||||
|
||||
public static bool TryGetAreaLightParameters(Light light, out float shadowCone, out Vector2 areaSize)
|
||||
{
|
||||
shadowCone = k_DefaultAreaLightShadowCone;
|
||||
areaSize = Vector2.one;
|
||||
|
||||
if (light == null || light.type != LightType.Rectangle)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!BGAreaLightManager.TryGetRegisteredAreaLight(light, out var areaLight))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
areaSize = areaLight.GetSize();
|
||||
shadowCone = Mathf.Clamp(areaLight.GetShadowCone(), k_MinAreaLightShadowCone, k_MaxAreaLightShadowCone);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public static float GetAreaLightOffsetForShadows(Vector2 areaSize, float coneAngle)
|
||||
{
|
||||
float halfMinSize = Mathf.Min(areaSize.x, areaSize.y) * 0.5f;
|
||||
float halfAngle = coneAngle * 0.5f;
|
||||
float cotanHalfAngle = 1.0f / Mathf.Tan(halfAngle * Mathf.Deg2Rad);
|
||||
float offset = halfMinSize * cotanHalfAngle;
|
||||
|
||||
return -offset;
|
||||
}
|
||||
|
||||
public static void ExtractRectangleAreaLightMatrix(
|
||||
VisibleLight visibleLight,
|
||||
float forwardOffset,
|
||||
float areaLightShadowCone,
|
||||
float shadowNearPlane,
|
||||
Vector2 areaSize,
|
||||
int shadowResolution,
|
||||
float normalBiasMax,
|
||||
bool reverseZ,
|
||||
out Matrix4x4 shadowMatrix,
|
||||
out Matrix4x4 viewMatrix,
|
||||
out Matrix4x4 projMatrix,
|
||||
out ShadowSplitData splitData)
|
||||
{
|
||||
Vector2 viewportSize = new Vector2(shadowResolution, shadowResolution);
|
||||
float aspectRatio = Mathf.Approximately(areaSize.y, 0f) ? 1.0f : areaSize.x / areaSize.y;
|
||||
float spotAngle = areaLightShadowCone;
|
||||
visibleLight.spotAngle = spotAngle;
|
||||
float guardAngle = CalcGuardAnglePerspective(visibleLight.spotAngle, viewportSize.x, 1, normalBiasMax,
|
||||
180.0f - visibleLight.spotAngle);
|
||||
|
||||
shadowMatrix = ExtractSpotLightMatrixInternal(
|
||||
visibleLight,
|
||||
forwardOffset,
|
||||
visibleLight.spotAngle,
|
||||
shadowNearPlane,
|
||||
guardAngle,
|
||||
aspectRatio,
|
||||
reverseZ,
|
||||
out viewMatrix,
|
||||
out projMatrix,
|
||||
out splitData);
|
||||
}
|
||||
|
||||
public static Matrix4x4 GetShadowTransform(Matrix4x4 proj, Matrix4x4 view)
|
||||
{
|
||||
if (SystemInfo.usesReversedZBuffer)
|
||||
{
|
||||
proj.m20 = -proj.m20;
|
||||
proj.m21 = -proj.m21;
|
||||
proj.m22 = -proj.m22;
|
||||
proj.m23 = -proj.m23;
|
||||
}
|
||||
|
||||
Matrix4x4 worldToShadow = proj * view;
|
||||
|
||||
Matrix4x4 textureScaleAndBias = Matrix4x4.identity;
|
||||
textureScaleAndBias.m00 = 0.5f;
|
||||
textureScaleAndBias.m11 = 0.5f;
|
||||
textureScaleAndBias.m22 = 0.5f;
|
||||
textureScaleAndBias.m03 = 0.5f;
|
||||
textureScaleAndBias.m13 = 0.5f;
|
||||
textureScaleAndBias.m23 = 0.5f;
|
||||
|
||||
return textureScaleAndBias * worldToShadow;
|
||||
}
|
||||
|
||||
internal static bool FastApproximately(float a, float b)
|
||||
{
|
||||
return Mathf.Abs(a - b) < 0.000001f;
|
||||
}
|
||||
|
||||
internal static bool FastApproximately(Vector4 a, Vector4 b)
|
||||
{
|
||||
return FastApproximately(a.x, b.x)
|
||||
&& FastApproximately(a.y, b.y)
|
||||
&& FastApproximately(a.z, b.z)
|
||||
&& FastApproximately(a.w, b.w);
|
||||
}
|
||||
|
||||
|
||||
static Matrix4x4 ExtractSpotLightMatrixInternal(
|
||||
VisibleLight vl,
|
||||
float forwardOffset,
|
||||
float spotAngle,
|
||||
float nearPlane,
|
||||
float guardAngle,
|
||||
float aspectRatio,
|
||||
bool reverseZ,
|
||||
out Matrix4x4 view,
|
||||
out Matrix4x4 proj,
|
||||
out ShadowSplitData splitData)
|
||||
{
|
||||
splitData = new ShadowSplitData();
|
||||
splitData.cullingSphere.Set(0.0f, 0.0f, 0.0f, float.NegativeInfinity);
|
||||
splitData.cullingPlaneCount = 6;
|
||||
|
||||
Matrix4x4 localToWorldOffset = vl.localToWorldMatrix;
|
||||
CoreMatrixUtils.MatrixTimesTranslation(ref localToWorldOffset, Vector3.forward * forwardOffset);
|
||||
view = localToWorldOffset.inverse;
|
||||
view.m20 *= -1;
|
||||
view.m21 *= -1;
|
||||
view.m22 *= -1;
|
||||
view.m23 *= -1;
|
||||
|
||||
proj = ExtractSpotLightProjectionMatrix(vl.range - forwardOffset, spotAngle, nearPlane - forwardOffset,
|
||||
aspectRatio, guardAngle);
|
||||
|
||||
Matrix4x4 deviceProjMatrix = GetGPUProjectionMatrix(proj, false, reverseZ);
|
||||
Matrix4x4 deviceViewProj = CoreMatrixUtils.MultiplyPerspectiveMatrix(deviceProjMatrix, view);
|
||||
|
||||
splitData.cullingMatrix = deviceViewProj;
|
||||
splitData.cullingNearPlane = nearPlane - forwardOffset;
|
||||
|
||||
return GetShadowTransform(proj, view);
|
||||
}
|
||||
|
||||
static float CalcGuardAnglePerspective(float angleInDeg, float resolution, float filterWidth,
|
||||
float normalBiasMax, float guardAngleMaxInDeg)
|
||||
{
|
||||
float angleInRad = angleInDeg * 0.5f * Mathf.Deg2Rad;
|
||||
float res = 2.0f / resolution;
|
||||
float texelSize = math.cos(angleInRad) * res;
|
||||
float beta = normalBiasMax * texelSize * k_Sqrt2;
|
||||
float guardAngle = math.atan(beta);
|
||||
texelSize = math.tan(angleInRad + guardAngle) * res;
|
||||
guardAngle = math.atan((resolution + math.ceil(filterWidth)) * texelSize * 0.5f) * 2.0f * Mathf.Rad2Deg -
|
||||
angleInDeg;
|
||||
guardAngle *= 2.0f;
|
||||
|
||||
return guardAngle < guardAngleMaxInDeg ? guardAngle : guardAngleMaxInDeg;
|
||||
}
|
||||
|
||||
static Matrix4x4 ExtractSpotLightProjectionMatrix(float range, float spotAngle, float nearPlane,
|
||||
float aspectRatio, float guardAngle)
|
||||
{
|
||||
float fov = spotAngle + guardAngle;
|
||||
float nearZ = Mathf.Max(nearPlane, k_MinShadowNearPlane);
|
||||
|
||||
float e = 1.0f / Mathf.Tan(fov / 180.0f * Mathf.PI / 2.0f);
|
||||
float a = aspectRatio;
|
||||
float n = nearZ;
|
||||
float f = n + range;
|
||||
|
||||
Matrix4x4 mat = new Matrix4x4();
|
||||
|
||||
if (a < 1)
|
||||
{
|
||||
mat.m00 = e;
|
||||
mat.m11 = e * a;
|
||||
}
|
||||
else
|
||||
{
|
||||
mat.m00 = e / a;
|
||||
mat.m11 = e;
|
||||
}
|
||||
|
||||
mat.m22 = -(f + n) / (f - n);
|
||||
mat.m23 = -2 * f * n / (f - n);
|
||||
mat.m32 = -1;
|
||||
|
||||
return mat;
|
||||
}
|
||||
|
||||
static Matrix4x4 GetGPUProjectionMatrix(Matrix4x4 projectionMatrix, bool invertY, bool reverseZ)
|
||||
{
|
||||
float4x4 gpuProjectionMatrix = math.transpose(projectionMatrix);
|
||||
if (invertY)
|
||||
{
|
||||
gpuProjectionMatrix.c1 = -gpuProjectionMatrix.c1;
|
||||
}
|
||||
|
||||
float multiplier = reverseZ ? -0.5f : 0.5f;
|
||||
gpuProjectionMatrix.c2 = gpuProjectionMatrix.c2 * multiplier + gpuProjectionMatrix.c3 * 0.5f;
|
||||
|
||||
return math.transpose(gpuProjectionMatrix);
|
||||
}
|
||||
|
||||
internal static Vector4 GetRectangleLightShadowBias(ref VisibleLight shadowLight, ref ShadowData shadowData, float shadowResolution, float nearPlane, AreaLightShadowSettings.BGAreaLightShadowMode shadowFilterMode)
|
||||
{
|
||||
Light unityLight = shadowLight.light;
|
||||
|
||||
float userDepthBias = 0.0f;
|
||||
float userNormalBias = 0.0f;
|
||||
|
||||
if (unityLight != null)
|
||||
{
|
||||
userDepthBias = unityLight.shadowBias;
|
||||
userNormalBias = unityLight.shadowNormalBias;
|
||||
|
||||
if (BGAreaLightManager.TryGetRegisteredAreaLight(unityLight, out var areaLight))
|
||||
{
|
||||
userDepthBias = areaLight.GetShadowDepthBias();
|
||||
userNormalBias = areaLight.GetShadowNormalBias();
|
||||
}
|
||||
}
|
||||
|
||||
float spotLikeAngle = k_DefaultAreaLightShadowCone;
|
||||
Vector2 size = Vector2.one;
|
||||
if (TryGetAreaLightParameters(shadowLight.light, out float areaCone, out size))
|
||||
{
|
||||
spotLikeAngle = areaCone;
|
||||
}
|
||||
|
||||
float aspectRatio = Mathf.Approximately(size.y, 0f) ? 1.0f : (size.x / size.y);
|
||||
float guardAngle = CalcGuardAnglePerspective(spotLikeAngle, shadowResolution, 1, userNormalBias, 180.0f - spotLikeAngle);
|
||||
|
||||
float actualNearPlane = Mathf.Max(nearPlane, k_MinShadowNearPlane);
|
||||
Matrix4x4 proj = ExtractSpotLightProjectionMatrix(
|
||||
shadowLight.range,
|
||||
spotLikeAngle,
|
||||
actualNearPlane,
|
||||
aspectRatio,
|
||||
guardAngle);
|
||||
Matrix4x4 deviceProjectionYFlip = GetGPUProjectionMatrix(proj, true, SystemInfo.usesReversedZBuffer);
|
||||
|
||||
float worldTexelSize = 2.0f / Mathf.Max(deviceProjectionYFlip.m00, 1e-6f) / Mathf.Max(shadowResolution, 1.0f) * k_Sqrt2;
|
||||
float depthBias = -userDepthBias * worldTexelSize;
|
||||
float normalBias = -userNormalBias * worldTexelSize;
|
||||
|
||||
if (shadowFilterMode != AreaLightShadowSettings.BGAreaLightShadowMode.Off)
|
||||
{
|
||||
float kernelRadius = shadowFilterMode switch
|
||||
{
|
||||
AreaLightShadowSettings.BGAreaLightShadowMode.PCF2x2 => 1.5f, // 2x2 PCF
|
||||
AreaLightShadowSettings.BGAreaLightShadowMode.Tent5x5 => 2.5f, // 5x5 Tent
|
||||
AreaLightShadowSettings.BGAreaLightShadowMode.Tent7x7 => 3.5f, // 7x7 Tent
|
||||
AreaLightShadowSettings.BGAreaLightShadowMode.PCSS => 3.5f, // PCSS (similar to 7x7)
|
||||
_ => 2.5f
|
||||
};
|
||||
|
||||
depthBias *= kernelRadius;
|
||||
normalBias *= kernelRadius;
|
||||
}
|
||||
|
||||
return new Vector4(depthBias, normalBias, (float)LightType.Rectangle, 0.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 33d629c65edb99d4da2cecc5488d96e3
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Passes/AreaShadowUtils.cs
|
||||
uploadId: 884030
|
||||
@@ -0,0 +1,24 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Unity.Collections;
|
||||
using Unity.Collections.LowLevel.Unsafe;
|
||||
|
||||
namespace BadDog.Rendering.AreaLight
|
||||
{
|
||||
internal static class NativeArrayExtensionsCompat
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref readonly T UnsafeElementAt<T>(this NativeArray<T> array, int index)
|
||||
where T : struct
|
||||
{
|
||||
return ref UnsafeUtility.ArrayElementAsRef<T>(NativeArrayUnsafeUtility.GetUnsafeReadOnlyPtr(array), index);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe ref T UnsafeElementAtMutable<T>(this NativeArray<T> array, int index)
|
||||
where T : struct
|
||||
{
|
||||
return ref UnsafeUtility.ArrayElementAsRef<T>(NativeArrayUnsafeUtility.GetUnsafePtr(array), index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 52eb969895db2a54b8133d587dd9642f
|
||||
AssetOrigin:
|
||||
serializedVersion: 1
|
||||
productId: 346790
|
||||
packageName: Realtime Area Light for URP
|
||||
packageVersion: 1.3.0
|
||||
assetPath: Packages/com.baddog.rendering.arealight/Runtime/Passes/NativeArrayExtensionsCompat.cs
|
||||
uploadId: 884030
|
||||
Reference in New Issue
Block a user