(This is a translation of a [Japanese [Content removed] by Kamikawa.)
Expected Behavior:
When r.SSR.Stencil is enabled, it is expected to mark the areas that should not be calculated in the stencil buffer. After that, the main SSR process begins, and the heavy pixel shader processing is supposed to be correctly culled by Early Stencil.
Explanation of the logic that is supposedly wrong in the code:
It’s about the behavior of StencilSetup in Engine\Shaders\Private\SSRT\SSRTReflections.usf.
Inside void ScreenSpaceReflectionsStencilPS, there is the following code.
#if !defined(TILE_COMPUTE_SSR)
if (RoughnessFade > 0.0 && bNoMaterial)
{
// we are going to compute SSR for this pixel, so we discard this
// pixel shader invocation to not overwrite the stencil buffer and
// therefore execute ScreenSpaceReflectionsPS() for this pixel.
discard;
}
#endif
// we are not going to compute SSR for this pixel, so we clear the color
// since ScreenSpaceReflectionsPS() won't be executed in this pixel.
OutColor = 0;
#if SSR_OUTPUT_FOR_DENOISER
OutClosestHitDistance = DENOISER_INVALID_HIT_DISTANCE;
#endif
According to the comments, the output part corresponds to the area where SSR is NOT computed. However, the actually output stencil looks like the attached image below. So the result appears to be reversed. Also, areas with high roughness is supposed to be discarded, so it is strange that everything except the sky becomes perfectly clean in this state.
[Image Removed]
The image below shows the stencil test during the Actual SSR Pass. In reality, the discarded areas correspond to the sky area that was marked as output.
[Image Removed]
Now let me show the source for the RenderState setup when calling the Main SSR Pass.
Source:
Engine\Source\Runtime\Renderer\Private\ScreenSpaceRayTracing.cpp
if (SSRStencilPrePass)
{
// Clobbers the stencil to pixel that should not compute SSR
GraphicsPSOInit.DepthStencilState =
TStaticDepthStencilState<false, CF_Always, true, CF_Equal,
SO_Keep, SO_Keep, SO_Keep>::GetRHI();
}
In this code, CF_Equal is set for the stencil test. However, since the areas that should NOT be computed are marked in the stencil, CF_NotEqual should be there. However, if simply reversing this, the sky would become the computation target. So, you can guess the problem already exists at the StencilSetup stage.
In the ScreenSpaceReflectionsStencilPS shader, the side that is NOT marked in the stencil and gets discarded mid-way is actually the area that is computed in the ScreenSpaceReflections shader. Let me show the conditions again from earlier in the code:
const float Roughness = GetRoughness(GBuffer);
const bool bNoMaterial = GBuffer.ShadingModelID == 0;
#endif // SUBTRATE_GBUFFER_FORMAT==1
const float RoughnessFade = GetRoughnessFade(Roughness);
#if !defined(TILE_COMPUTE_SSR)
if (RoughnessFade > 0.0 && bNoMaterial)
{
discard;
}
#endif
Since bNoMaterial represents Unlit, the area should not be computed.
if (RoughnessFade > 0.0 && bNoMaterial)
If fixing the stencil test as intended, the current condition would let Unlit materials with high RoughnessFade become the computation target, which is incorrect.
(To be continued)
[Attachment Removed]