DepthBuffer precision

In the attached project you will find an horizontal opaque plane at z=0 and an horizontal translucent plane at z=100.

So the vertical distance between the two is always 100.

If I create a translucent material that outputs SceneDepth-PixelDepth and face the camera down from above, I expect to see a value of 100 on all pixels

(since both SceneDepth and PixelDepth are planar distances from camera, and SceneDepth is the distance of the farher opaque plane).

I expected this to be ~100, due to depth buffer precision, however when I get farther from the origin I get way different values. When I get 1Km from the origin the value is not 100: it’s -0.32.

The project is using default zbuffer precision (R32_FLOAT_X8X24_TYPELESS). If I force r.D3D12.Depth24Bit=1 the value I get is not 100, but 14.5.

To have the proper values my material should output SceneDepth-ConvertFromDeviceZ(ConvertToDeviceZ(PixelDepth)).

There’s a specific switch param to test it in the attached project.

This seems a bit strange (since you never use this in your code or examples, nor seems really cross-compiler-friendly, and it seems a proper conversion is missing in SceneDepth node or in the zbuffer management).

Context: this is needed in our water shaders to easly compute depth for shore waves.

[Attachment Removed]

Hey Marco, I’ll likely need to hand you over to one of our rendering engineers for a more in-depth reason for what’s going on here. As I was testing out the project a bit I noticed that I’d start to see the output depth delta value happening at ~100 meters out. I did a few tests with different methods for getting pixel depth and confirmed that the imprecision is indeed coming from the scene depth. Both the SceneDepth input and SceneTexture:SceneDepth produced the same results.

I do want to flag that a more common technique for doing shorelines right now is to sample RVT Height and get the delta between the Z-height of the water and the Z-height of the underlying landscape. This method avoids any issues you’d see with scene depth imprecision altogether. Do you think that might work in your case?

[Attachment Removed]

Thanks. Yes we currently use rvt height and similar techs for shore line detection too: I simplified the context a bit too much.

[Attachment Removed]

Hello,

There is an unfortunate discrepancy between how SceneDepth goes through the ConvertToDeviceZ path, which introduces a slight offset to avoid a divide-by-zero error, and PixelDepth which is the raw value and must be sent through the ConvertFrom/ToDeviceZ to make it line up with what you get with SceneDepth. In 5.8 we’ve reduced the fudge factor used here in the following changelist:

CL#48658680 (ded918) ConvertFromDeviceZ - decrease fudge factor, as it was causing too large discrepancies in the distance between real SceneDepth and ConvertFromDeviceZ(), breaking temporal reprojection and similar operations. With the new factor at 300km from the origin the error is basically 0 (previously it was 62km). At the same it is still small enough where it’s safe to do random math operations (in e.g. material shaders) for ConvertFromDeviceZ(0.0) without handling this case explicitly.

However, that introduced other issues with legacy materials so we settled on this:

CL#53509798 (1212ce) [Scene Depth Fudge Factor]

* Reduce default fudge factor to 1e-13 to keep legacy material math working. It’s still it’s higher than 5.7 default (1e-8) so rendering will start breaking further away

* Expose SceneDepth fudge factor as a CVar. This can be useful for finding bugs caused by it or reverting to pre 5.8 behavior

If you don’t intend to upgrade to 5.8 you should be able to pull in those changelists fairly easily, but you may still need to send PixelDepth through the fudgefactor code to accurately compare it to SceneDepth if you get far enough away from the origin.

Hope that helps!

[Attachment Removed]