Translucency with DepthWrite

Translucent object rendering sorted only by object origin is not acceptable in our game. However, we are not able to use OIT due to the shader model limitation, also we feel that OIT is too expensive even if we could.

One option is to do a depth write when rendering translucency. This generally improves the look (although not perfect of course), see image below. To prototype this, I have modified the AllTranslucent pass to allow for DepthWrite

in BasePassRedering.cpp using

PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());

PassDrawRenderState.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilWrite);

See image below for the difference in results, we feel the right version looks better.

However I don’t want to have this enabled for everything, only the selected meshes based on a property added to the Material.

So now the challenge is to figure out how to add how to add this without too many engine changes. It looks like I get the desired DepthWrite set via the

FGraphicsMinimalPipelineStateInitializer

One approach seems to be to let it to come in on

void FMeshPassProcessor::BuildMeshDrawCommands(…)

possibly via the FMeshBatch object by extending that with another boolean.

Did miss a way to do this that already exists, if not, is this reasonable or is there a better approach?

[Image Removed]

Steps to Reproduce

Hello,

Can you provide an idea of the performance budget you have for an alternate translucency technique? Also, is the shader model limitation (SM5?) due to needing the technique to render on mobile?

Hi Alex,

So, we do need things to work on Mobile, in addition when I enable OIT in DX11, I get a warning about Rasterizer Order Views (ROVs) introduced in Shader Model 5.1

https://learn.microsoft.com/en-us/windows/win32/direct3dhlsl/shader-model-5-1-objects

I enabled that by changing the shader model from 5_0 to 5_1 but then I had warnings about an incompatibility with another shader compiler option.

5.1 introduces a compatibility change since it generates different resource binding.

https://learn.microsoft.com/en-us/windows/win32/direct3d12/resource-binding-in-hlsl

So given all that, I am going down the route to add the depth write approach to see if I can get that to work.

We have a LOT of overlapping translucent VFX, so it is important for perf to be really good.

Has OIT ever been tested with DX11?

Apologies for the delay, I’m directing this query to our team that is more experienced with mobile rendering and can better advise on what techniques will perform best there.

Hi Alex,

One alternative technique you could consider that would get closer to correct transparent object self occlusion and not require engine modification would be to render out the transparent objects to the custom depth buffer, then in the color pass, have the transparent material sample custom depth and test fragment depth against that sample. If this approach fits your use case, I’d be happy to whip up a quick sample illustrating a potential implementation.

Best regards.

To support the new parameters on the Materials

e.g. MaterialResource.IsTranslucencyWritingDepth()

I hard coded these next to the other material properties.

But ideally I would like to do this using an extension mechanism.

E.g. using an IDetailCustomization class. But after trying it for a day of building a plugin/modules, I could not get it to work.

I see that this gets called and works:

void FMaterialDetailCustomization::CustomizeDetails( IDetailLayoutBuilder& DetailLayout )But the registration for my MaterialCustomDetails class does never gets its CustomizeDetails method called.

Is there something special about the material editor’s detail panel that doesn’t support extending it?

If it is possible, is there a sample that shows this.

Hi Alex,

Are you in a position to share your attempt at a plugin in which CustomizeDetails isn’t called so we can take a closer look?

Best regards.

I Stephane,

That is a good suggestion, however we actually have two opaque and two translucent passes and we want it to work on a per object basis and the custom depth buffer is shared between object from what I can tell, so it gets more complex.

Here is what I ended up doing in case it is useful for someone else. I added a material TranslucencyWritesDepth bool parameter with an override on the material instance as well. Then in BasePassRendering.cpp, I modified the

FMeshPassProcessor* CreateTranslucencyAllPassProcessor(…)

To use:

PassDrawRenderState.SetDepthStencilAccess(FExclusiveDepthStencil::DepthWrite_StencilWrite); PassDrawRenderState.SetDepthStencilState(TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI());Then in

TranslucencyRendering.cpp

in

RenderTranslucencyViewInner()

I added

if (TranslucencyPass == ETranslucencyPass::TPT_AllTranslucency )
{
	PassParameters->RenderTargets.DepthStencil = FDepthStencilBinding(SceneDepthTexture, ERenderTargetLoadAction::ELoad, ERenderTargetLoadAction::ELoad, FExclusiveDepthStencil::DepthWrite_StencilWrite);
}

Then in

MeshPassProcessor.inl

in

template<typename PassShadersType, typename ShaderElementDataType>void FMeshPassProcessor::BuildMeshDrawCommands(

I added

PipelineState.BlendState = DrawRenderState.GetBlendState();
if (MeshPassType == EMeshPass::TranslucencyAll || MeshPassType == EMeshPass::TranslucencyEnvironment)
{
    if (MaterialResource.IsTranslucencyWritingDepth() && DrawRenderState.GetDepthStencilAccess() == FExclusiveDepthStencil::DepthWrite_StencilWrite)
    {
       PipelineState.DepthStencilState = TStaticDepthStencilState<true, CF_DepthNearOrEqual>::GetRHI();
    }
    else
    {
       PipelineState.DepthStencilState = TStaticDepthStencilState<false, CF_DepthNearOrEqual>::GetRHI();
    }
}
else
{
    PipelineState.DepthStencilState = DrawRenderState.GetDepthStencilState();
}

There were additional modifications but that was the core of it and we can select which translucent meshes we want to behave this way and which continue to behave as before. Thanks for your help.