Render Screen-Space Widget Behind Character?

Okay, so after experimenting around with it for another day which nearly brought me to the edge of my sanity, I’ve realised that, essentially, there’s no way to have a screen-space object look like it’s behind my 3D character. I’ve reverted back to my world-space solution and tweaked it. It would be incredibly handy if Epic could implement some sort of FXAA node for use in post-process materials. As it is, I’ve had to resort to very low-radius Gaussian blur applied to my very noisy, heavily aliased custom stencil. The result is acceptable but certainly not perfect. If anyone else stumbles across this question with similar issues, I’ll try to sum up what I’ve done:

1.Create a widget with your text/graphic/whatever.

2.Create a blueprint actor and place the widget inside. Make sure it’s set to world space.

3.Duplicate the Widget3DPassThrough material that should’ve been automatically applied to your widget and check “Disable Depth Test” on your new copy.

4.In your character blueprint, select your character mesh and check “Render CustomDepth Pass”. Set “CustomDepth Stencil” value to a number of your choosing and remember that number.

5.In the copy of the Widget3DPassThrough material that we just created, use a SceneTexture node to access the custom stencil you just assigned your character mesh to. Multiply this stencil with the nodes that were plugged into the opacity input of the material and plug the result of this multiplication into the opacity input instead to mask out the screen area that will be occupied by your character.

6.Go back to the blueprint containing your widget, select the widget and apply the new copy of the widget material that we just created to it.

7.The widget still selected inside the blueprint, check “Render CustomDepth Pass” and, again, set a CustomDepth Stencil value, as we did with the character mesh. Just make sure it’s not the same value you used on your character.

8.Create a post-process material, make sure that “Blendable Location” is set to After Tonemapping. Inside the material, create a SceneTexture node and set it to PostProcessInput0. Connect it to the A input of a Lerp node. Create a constant 4 Vector and set it to the colour you want to use as your UI/text colour (alternatively, you can use textures, gradients, noise, etc. if you don’t want your UI to just be one flat colour).

9.For the Lerp alpha, I resorted to a custom Gaussian Blur shader node by Tommy Tran which can be found here: Unreal Engine 4 Custom Shaders Tutorial | raywenderlich.com . You need some sort of blur because the stencil on its own will have some very heavy aliasing on it. I modified the shader slightly to work with the custom stencil by changing SceneTextureId to 25 and replacing line 12 (starts with float3 PixelColor) inside the GaussianBlur node with this:

float3 PixelColorA = SceneTextureLookup(Offset, SceneTextureId, 0).rgb;
float PixelColor = 0;
if (PixelColorA.r == 2)
{
PixelColor = 1;
}

You will need to replace the 2 with whatever Stencil Value you used on the widget as this bit of code filters out the widget stencil (otherwise the colour fill and blur will also be applied to the character stencil which we don’t want).

10.Run the result of the Gaussian Blur node through a Component Mask with only the R channel enabled before passing it into the Lerp Alpha. Plug the result of the Lerp into the material’s Emissive. Adjust the Radius value until you get acceptable results between not too blurry and not too aliased.

11.Don’t forget to place your blueprint actor in your level in the spot where you want the widget to show up.

I also had to set “CustomDepth Stencil Write Mask” on my character mesh to “All bits” for things to be sorted properly but I haven’t played around too much with the other options to figure out if sth else might also work and if you’re using stencils for other tasks, using the “All bits” option might interfere with those. If you want it to look even more like a screen-space widget, you can do some additional blueprinting inside your blueprint actor to dynamically adjust the rotation and scale of the widget according to camera rotation and distance. So yeah, for now, without being able to write my own shader code, I think this is the best I’ll be able to do. Hope it’s helpful to someone.

3 Likes