From 2a59a327053a48096cd8ad54d1ab19d9e068c558 Mon Sep 17 00:00:00 2001 From: MelekTaus Moribundus Date: Thu, 9 Jul 2026 18:33:25 +0200 Subject: [PATCH] Clip hero cutout in MotionVectors pass to fix HTrace depth corruption The MotionVectors pass (drawn by HTrace and URP against the real camera depth buffer) was stock URP: no cutout clip and ZWrite on, so it wrote the wall depth back into the hole after opaques. The skybox then failed the depth test there (black disc) and all screen-space effects (SSAO/GI) shaded the hole with the occluded wall's depth. The pass now runs ApplyHeroCutout before URP's fragment entry point. ObjectMotionVectors.hlsl forces its own entry via include_with_pragmas, so the pragmas are declared explicitly and the file is plain-included. Complements the earlier GBuffer clip; XR MV pass and ShadowCaster stay stock on purpose. Includes render-queue churn on the occluder material. Co-Authored-By: Claude Fable 5 --- Assets/Materials/Hero Cutout Occluder.mat | 2 +- Assets/Unused/HeroCutoutLit.shader | 41 +++++++++++++++++++++-- 2 files changed, 39 insertions(+), 4 deletions(-) diff --git a/Assets/Materials/Hero Cutout Occluder.mat b/Assets/Materials/Hero Cutout Occluder.mat index ae6d770..4466773 100644 --- a/Assets/Materials/Hero Cutout Occluder.mat +++ b/Assets/Materials/Hero Cutout Occluder.mat @@ -16,7 +16,7 @@ Material: m_LightmapFlags: 4 m_EnableInstancingVariants: 0 m_DoubleSidedGI: 0 - m_CustomRenderQueue: -1 + m_CustomRenderQueue: 2000 stringTagMap: RenderType: Opaque disabledShaderPasses: [] diff --git a/Assets/Unused/HeroCutoutLit.shader b/Assets/Unused/HeroCutoutLit.shader index 94b65b3..6dd5164 100644 --- a/Assets/Unused/HeroCutoutLit.shader +++ b/Assets/Unused/HeroCutoutLit.shader @@ -4,7 +4,7 @@ // // It is a faithful copy of URP's Lit.shader - same properties, keywords and includes, so material // values transfer 1:1 and the inspector GUI keeps working. The ONLY change is in four passes -// (ForwardLit, ShadowCaster, DepthOnly, DepthNormals): the stock fragment entry point is replaced by +// (ForwardLit, GBuffer, DepthOnly, DepthNormals): the stock fragment entry point is replaced by // a thin wrapper that runs ApplyHeroCutout() and then delegates to URP's original fragment. All // shading is therefore URP's own - zero reimplementation, so it stays correct across URP updates. Shader "Custom/HeroCutoutLit" @@ -278,7 +278,7 @@ Shader "Custom/HeroCutoutLit" // ------------------------------------- // Shader Stages #pragma vertex LitGBufferPassVertex - #pragma fragment LitGBufferPassFragment + #pragma fragment HeroGBufferFragment // ------------------------------------- // Material Keywords @@ -337,6 +337,17 @@ Shader "Custom/HeroCutoutLit" // Includes #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl" #include "Packages/com.unity.render-pipelines.universal/Shaders/LitGBufferPass.hlsl" + #include "HeroCutout.hlsl" + + // Radial cutout, then delegate to URP's stock GBuffer fragment. Without this, external + // GBuffer consumers (HTrace WSGI renders its own thin GBuffer via this LightMode and + // composites GI after post-processing) still see solid occluder geometry inside the hole + // and stomp the visible skybox/background there with surface GI -> black disc. + GBufferFragOutput HeroGBufferFragment(Varyings input) + { + ApplyHeroCutout(input.positionCS); + return LitGBufferPassFragment(input); + } ENDHLSL } @@ -540,12 +551,36 @@ Shader "Custom/HeroCutoutLit" ColorMask RG HLSLPROGRAM + // The cutout MUST also apply here: this pass has ZWrite On (default) and external + // consumers draw it against the LIVE camera depth attachment BEFORE the skybox + // (HTrace WSGI's object-motion-vectors pass at AfterRenderingOpaques). Without the + // clip it re-fills the hole with occluder depth -> the skybox fails ZTest inside the + // hole -> black disc, and the corrupted depth leaks into _CameraDepthTexture (SSAO/GI). + // + // ObjectMotionVectors.hlsl hardcodes "#pragma fragment frag" via include_with_pragmas, + // so we declare the pragmas ourselves and plain-#include it (plain includes contribute + // no pragmas), then wrap its frag(). + #pragma target 3.5 + #pragma vertex vert + #pragma fragment HeroMotionVectorsFragment + #pragma shader_feature_local _ALPHATEST_ON #pragma multi_compile _ LOD_FADE_CROSSFADE #pragma shader_feature_local_vertex _ADD_PRECOMPUTED_VELOCITY + #pragma multi_compile_instancing + #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/DOTS.hlsl" + #include_with_pragmas "Packages/com.unity.render-pipelines.core/ShaderLibrary/FoveatedRenderingKeywords.hlsl" + #include "Packages/com.unity.render-pipelines.universal/Shaders/LitInput.hlsl" - #include_with_pragmas "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ObjectMotionVectors.hlsl" + #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/ObjectMotionVectors.hlsl" + #include "HeroCutout.hlsl" + + float4 HeroMotionVectorsFragment(Varyings input) : SV_Target + { + ApplyHeroCutout(input.positionCS); + return frag(input); + } ENDHLSL }