Refactor : NiloToon 셀프 섀도우 double-draw 제거

이전 fix는 cullResults 기반 DrawRendererList + manual DrawRenderer 둘 다
호출해서 캐릭터가 frustum 안에 있을 때 shadow map에 두 번 그려졌음.
manual draw가 cullResults에 의존하지 않으므로 DrawRendererList 자체가
불필요. 제거하여 GPU 부하 ~절반 감소 + 코드 단순화.

- RG path: PassData.rendererListHandle, RendererListParams,
  CreateRendererList, UseRendererList, DrawRendererList 모두 제거
- Legacy path: cullingParameters/cullResults 계산, context.Cull,
  context.DrawRenderers 모두 제거 (terrainCrashSafeGuard 분기 자체가
  무의미해짐)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
user 2026-05-05 22:39:03 +09:00
parent ed764d7f83
commit 67c99af14f

View File

@ -615,43 +615,7 @@ namespace NiloToon.NiloToonURP
} }
#endif #endif
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Streamingle: cullResults / custom culling 코드 제거. manual DrawRenderer가 cullResults에 의존하지 않으므로 불필요.
// set culling for shadow camera -> do culling
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
camera.TryGetCullingParameters(out var cullingParameters);
// update culling matrix
cullingParameters.cullingMatrix = shadowCamProjectionMatrix * shadowCamViewMatrix;
// update culling planes
GeometryUtility.CalculateFrustumPlanes(cullingParameters.cullingMatrix, cameraPlanes);
for (int i = 0; i < cameraPlanes.Length; i++)
{
cullingParameters.SetCullingPlane(i, cameraPlanes[i]);
}
CullingResults cullResults;
bool terrainExist = false;
if (settings.terrainCrashSafeGuard)
{
terrainExist = Terrain.activeTerrains.Length != 0;
}
if (settings.perfectCullingForShadowCasters && !terrainExist)
{
// use the above new cullResults in DrawRenderers() below,
// so even a renderer is not visible in the perspective of main camera,
// it can still render correctly in shadow camera's perspective due to this new culling
// (2021-07-14) unity will crash if code running this line and terrain exist in scene
// (2024-03-21) enable this will make VLB's SRP batcher mode flicker randomly, not sure why, should we do something to revert this culling line?
cullResults = context.Cull(ref cullingParameters); // original working code, but will crash if terrain exist
}
else
{
// (2021-07-14) a special temp fix to avoid terrain crashing unity, but will make shadow culling not always correctly if shadow caster is not existing on screen
cullResults = renderingData.cullResults;
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Set uniform (before context.DrawRenderers) // Set uniform (before context.DrawRenderers)
@ -721,16 +685,9 @@ namespace NiloToon.NiloToonURP
*/ */
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// draw all char renderer using SRP batching (must set all uniforms and executed before draw!) // Streamingle: cullResults 의존성 완전 제거. context.DrawRenderers + custom culling 모두 제거.
// characterList의 모든 NiloToon 캐릭터를 manual draw로 그려 카메라 frustum 무관하게 shadow map 채움.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
ShaderTagId shaderTagId = new ShaderTagId("NiloToonSelfShadowCaster");
var drawSetting = CreateDrawingSettings(shaderTagId, ref renderingData, SortingCriteria.CommonOpaque);
var filterSetting = new FilteringSettings(RenderQueueRange.opaque);
context.DrawRenderers(cullResults, ref drawSetting, ref filterSetting); // using custom cullResults from shadow camera's perspective, instead of main camera's cull result
// Streamingle: cullResults 우회용 manual draw.
// perfectCullingForShadowCasters=false 또는 terrain 존재 시 cullResults가 main camera cull로 fallback →
// 캐릭터가 frustum 밖이면 shadow map에 안 그려짐. 같은 renderer가 두 번 그려질 수 있으나 depth test가 처리.
DrawNiloToonCharsManuallyLegacy(cmd); DrawNiloToonCharsManuallyLegacy(cmd);
context.ExecuteCommandBuffer(cmd); context.ExecuteCommandBuffer(cmd);
cmd.Clear(); cmd.Clear();
@ -832,8 +789,7 @@ namespace NiloToon.NiloToonURP
// copy and edit of https://docs.unity3d.com/6000.0/Documentation/Manual/urp/render-graph-draw-objects-in-a-pass.html // copy and edit of https://docs.unity3d.com/6000.0/Documentation/Manual/urp/render-graph-draw-objects-in-a-pass.html
private class PassData private class PassData
{ {
// Create a field to store the list of objects to draw // Streamingle: RendererListHandle 제거. manual DrawRenderer로 대체됨.
public RendererListHandle rendererListHandle;
public bool shouldRender; public bool shouldRender;
public Matrix4x4 _NiloToonSelfShadowWorldToClip; public Matrix4x4 _NiloToonSelfShadowWorldToClip;
public Vector4 _NiloToonSelfShadowParam; public Vector4 _NiloToonSelfShadowParam;
@ -861,20 +817,9 @@ namespace NiloToon.NiloToonURP
UniversalRenderingData renderingData = frameContext.Get<UniversalRenderingData>(); UniversalRenderingData renderingData = frameContext.Get<UniversalRenderingData>();
UniversalLightData lightData = frameContext.Get<UniversalLightData>(); UniversalLightData lightData = frameContext.Get<UniversalLightData>();
SortingCriteria sortFlags = SortingCriteria.CommonOpaque; //cameraData.defaultOpaqueSortFlags; // Streamingle: RendererList 기반 그리기 제거. ExecutePass의 manual DrawRenderer가
RenderQueueRange renderQueueRange = RenderQueueRange.opaque; // characterList를 직접 그리므로 cullResults에 의존하는 RendererList는 불필요 + double-draw 방지.
FilteringSettings filterSettings = new FilteringSettings(renderQueueRange, ~0); // RendererList CPU 컬링 비용도 절약.
// Redraw only objects that have their LightMode tag set to "NiloToonSelfShadowCaster"
ShaderTagId shadersToOverride = new ShaderTagId("NiloToonSelfShadowCaster");
// Create drawing settings
DrawingSettings drawSettings = RenderingUtils.CreateDrawingSettings(shadersToOverride, renderingData, cameraData, lightData, sortFlags);
// Create the list of objects to draw
var rendererListParameters = new RendererListParams(renderingData.cullResults, drawSettings, filterSettings);
// create RT (temp) // create RT (temp)
// Create texture properties that match the screen size // Create texture properties that match the screen size
@ -892,8 +837,6 @@ namespace NiloToon.NiloToonURP
shouldRender = true; shouldRender = true;
} }
// Convert the list to a list handle that the render graph system can use
passData.rendererListHandle = renderGraph.CreateRendererList(rendererListParameters);
passData.shouldRender = shouldRender; passData.shouldRender = shouldRender;
RenderTextureDescriptor renderTextureDescriptor = new RenderTextureDescriptor(shadowMapSize, shadowMapSize, RenderTextureFormat.Shadowmap, 16); RenderTextureDescriptor renderTextureDescriptor = new RenderTextureDescriptor(shadowMapSize, shadowMapSize, RenderTextureFormat.Shadowmap, 16);
@ -904,8 +847,6 @@ namespace NiloToon.NiloToonURP
// Set the render target as the color and depth textures of the active camera texture // Set the render target as the color and depth textures of the active camera texture
UniversalResourceData resourceData = frameContext.Get<UniversalResourceData>(); UniversalResourceData resourceData = frameContext.Get<UniversalResourceData>();
builder.UseRendererList(passData.rendererListHandle);
//builder.SetRenderAttachment(resourceData.activeColorTexture, 0); //builder.SetRenderAttachment(resourceData.activeColorTexture, 0);
builder.SetRenderAttachmentDepth(shadowMapRT, AccessFlags.Write); builder.SetRenderAttachmentDepth(shadowMapRT, AccessFlags.Write);
@ -1335,13 +1276,8 @@ namespace NiloToon.NiloToonURP
cmd.SetKeyword(GlobalKeyword.Create(_NILOTOON_RECEIVE_SELF_SHADOW_Keyword), true); cmd.SetKeyword(GlobalKeyword.Create(_NILOTOON_RECEIVE_SELF_SHADOW_Keyword), true);
// Draw the objects in the list (uses main camera cullResults — character가 frustum 안일 때만) // Streamingle: cullResults 의존성 완전 제거. manual draw만 사용.
cmd.DrawRendererList(data.rendererListHandle); // characterList의 모든 NiloToon 캐릭터를 직접 그려 카메라 frustum 무관하게 shadow map 채움.
// Streamingle: cullResults 우회용 manual draw.
// 카메라가 캐릭터 frustum 밖이면 rendererListHandle에 캐릭터가 없어 shadow map이 비어 그림자 사라짐.
// 모든 NiloToon 캐릭터의 모든 NiloToonSelfShadowCaster pass를 수동으로 한 번 더 그려 cullResults 의존성 제거.
// 같은 renderer가 두 번 그려질 수 있으나 depth test가 처리하므로 시각적 영향 없음.
DrawNiloToonCharsManually(cmd); DrawNiloToonCharsManually(cmd);
} }