NV_depth_buffer_float > 1.0

Anyone have any success using NV_depth_buffer_float with a far plane past 1.0?

I’m seeing what appears to be far plane clipping at 1.0 regardless of glDepthRangedNV(), using GL_DEPTH_COMPONENT32F_NV with either texture bound to FBO or renderbuffer bound to FBO.

Could you describe in more detail what you’re doing and what result you are getting?

Drawing GL_POINTS, to FBO with GL_DEPTH_COMPONENT32F_NV bound, set glDepthRangedNV(0, to anything > 1.0).

Then in vertex shader, set xyzw -> (projected x, projected y, something where z > 1.0, w=1), rolling my own projection. Points where z>1.0 get clipped. Can just scale z to 0 <= z <= 1, any everything draws fine.

You would think that with glDepthRangedNV() and a floating point depth buffer, that clip space would then be

-1 <= x <= 1
-1 <= y <= 1
glDepthRangedNV(near,) <= z <= glDepthRangedNV(,far)

Also using glClearDepthdNV(far) for obvious reasons.

Have had no success with ‘far > 1.0’.

Point of the floating depth for me was to have access to the exponent range so simply scaling by 1/far isn’t what I had in mind.

The depth range is part of the viewport transformation, it does not affect clipping at all. NV_depth_buffer_float just removes the clamp restriction on the depth range.

In my opinion the real value this adds is not the extended range of the depth buffer (which I think is pretty much useless in practice), but getting rid of the dreaded additive part of the viewport depth transformation.

Remember the viewport depth transformation in OpenGL is defined as

Zw = Zndc * (f-n)/2 + (f+n)/2

where f and n are the depth range near and far values. Since they’re both clamped to [0, 1] the term (f+n)/2 is always positive – and 0.5 for the default depth range.

Unfortunately, adding 0.5 to a floating point value around 0 (as Zndc is in [-1, 1]) just kills precision… this is where glDepthRangedNV helps: Instead of being restricted to [0, 1] you can specify a depth range of, say, [-1, 1] and effectively turn the viewport depth transformation into a no-op.

But combined with a traditional projection matrix that still doesn’t give you a good distribution of values. Probably the best way of using a float depth buffer is to store 1 / Zview, since this results in a constant relative precision at any point in the representable range. To achieve this you need to

  • set the depth range to [-1, 1]
  • clear depth to 0
  • invert the depth comparison
  • and replace the two bottom rows of your projection matrix with
0 0  0 1
0 0 -1 0

Note that this always results in positive values and the far clip plane is at infinity.

I think it’s unfortunate Nvidia didn’t just make an extension to limit clip space to positive Z like D3D does.

Why is desirable to have an extension that limits clip space to positive z? That just cuts your range of “good precision” in half.

Perhaps the d3d folks could be persuaded to change this. Seems a bit more consistent to have everything in [-1, 1] to boot.

I guess I should have written that differently. The actual purpose of such an extension would be to change the viewport depth transformation from
Zw = Zndc * (f-n)/2 + (f+n)/2
to
Zw = Zndc * (f-n) + n

That way you can get rid of the additive term by setting n = 0 even on hardware that doesn’t support float depth values outside [0, 1], so it would be portable. But to make sure that Zw is in [0, 1] Zndc needs to be restricted to the same range.

I don’t think NDC range is the limiting factor when it comes to depth precision. As I wrote before it is very desirable to store 1 / Zeye in a floating point depth buffer. This is linear in screen space (which is a requirement for Z interpolation) and gives you constant relative precision everywhere. The usual projection matrix results in an extreme concentration of values close to the near plane that is unnecessary and wasteful, especially with a float buffer.

And to preserve float precision throughout the vertex shader, perspective divide, and the viewport transformation, it is best to perform as few operations as possible.

So what you are saying is that NV_depth_buffer_float doesn’t effect fixed function clipspace functionality? So after the vertex shader finishes and does this internally,

clipspace.xyzw = vec4(
gl_Position.x/gl_Position.w,
gl_Position.y/gl_Position.w,
gl_Position.z/gl_Position.w,
gl_Position.w)

It still always clips to the {-1,1} unit cube regardless of NV_depth_buffer_float?

Is this correct?

Which if I am getting this correctly, only -1.0 to 1.0 can ever get written do the z buffer with NV_depth_buffer_float?

Why is desirable to have an extension that limits clip space to positive z? That just cuts your range of “good precision” in half.

So effectively you get a precision hole around zero with depth float, so you would only want to use 0.0 to 1.0, and thus only really use 1.0e-max_non_denormal_exponent to 1.0.

Yes.

Which if I am getting this correctly, only -1.0 to 1.0 can ever get written do the z buffer with NV_depth_buffer_float?

No, the viewport transformation maps NDC depth in the range [-1, 1] to the depth range [n, f]. If you use glDepthRange both n and f get clamped to [0, 1] so the value written to the depth buffer is also between 0 and 1. glDepthRangedNV removes this restriction so you can indeed get any value in the depth buffer.

Most helpfull, thanks for that info!