Are you rendering with or without Z buffer?
And what does “Before” mean? Just an ordered render list?
You need the Z buffer to be “on” to avoid hands that swing behind the body to accidentally render on top of the body.
But if the Z buffer is on, then an object that may sort “behind” may end up drawing “on top” anyway.
The only way to avoid this in a single pass system is using stencil, turn on z buffer, sort objects front-to-back, and once a pixel is rendered, turn on the stencil bit, and prevent further objects from being rendered there. (Well, that, or rendering an impostor per object to a separate offscreen target.)
While this will “work,” it ■■■■■ up how shadows work, and how ambient occlusion works, and even how anti-aliasing works on some hardware/APIs!
What it sounds like you want can’t be had while also wanting those things to work – it’s mathematically impossible. So of course Unreal won’t support it – they care more about those features than about the particular sorted-renderer you’re imagining. They never said they would support it, they don’t target games that look like that, using Unreal is the wrong tool if that’s what you want – again, assuming I understand the technical details of what you’re suggesting, because you’re not being particularly precise.
All of computer graphics is a hack. (Except possibly ray tracing. But that certainly won’t work with the kind of layer hack you’re talking about!) If you can get what you want by flipping some particular bit, then flip that bit.
There is no “properly” here. What you’re suggesting is logically impossible without giving up something else. You can add depth bias if you want (this is easy in the shader) but then you instead end up with hands poking out “this way” from walls in front of the character.
I see, this is the “separate depth pass for first person character overlay” request. That’s a totally different request than a request for “render X before Y in a general renderer.” Glad you at least agree that that one won’t work!
For that restricted case, there still is no zero-cost way of doing this while supporting the features of the engine, because you need to clear the Z buffer, or allocate a second Z buffer, and I still think ray tracing would break in this mode. Because you won’t get all the features to interact nicely in the general case, I can see that as a reason why they might not add this as an option, because then instead you’d get questions like “why doesn’t interact correctly with when I put in the overlay pass” which would be just as motivated.
If you need this particular way of rendering, I can see three ways to do it:
- Add another render pass of your own, with a new Z buffer (to prevent breaking all the other features that depend on reading Z.) This likely requires some surgery into the C++ rendering pipeline.
- Render the object AGAIN to a separate texture, using a separate scene setup, and composite the image in the postprocessor. This will use more resources, but it will likely work okay, because the “first render” will be used for reflections and shadows and raytracing and such.
- Use the “Custom Depth” map, and compare to “scene depth,” and pull the output depth value up to just ahead of the “scene depth” where the “Custom Depth” is equal to pixel depth, but scene depth is closer. This will screw up hierarchical Z, which is a bit of a bummer for something as big on the screen as a FPS overlay, but what you want is fundamentally unmathematical and thus some compromise is needed.
(Tried option 3, but it turns out only transparent materials can read Custom Depth, and they can’t then output a pixel depth offset.)
You’ll still see the actor clip into the wall if you look in a mirror, though – that’s because you want to allow objects to be in a physically-impossible configuration. The better solution is to make collisions good enough that the objects will naturally not clip through geometry, and perhaps make the character detect when it’s pushing up against a wall and pull up/aside the weapon to avoid the penetration. That’s a much better overall solution anyway, IMO.
Actually, you should probably try the Custom Depth / Adjust-pixel-depth approach. It might just work! The comparison between pixel depth and custom depth is important to make Z testing still work for pixels that would otherwise clip through the wall in front of you.