<p>The screenshot you posted states the answer:</p>
<blockquote>
<p>The value gets mapped into [0, 1] for storage in the depth buffer later, but that doesn't help, since the initial mapping to [-1, 1] has already destroyed all the precision in the far half of the range.</p>
</blockquote>
<p>That [-1, 1] mapping cannot be changed by your projection matrix or anything else. It is a fundamental part of the OpenGL-defined fixed-function transformation sequence. It's the transformation from clip-coordinates to NDC coordinates. NDC coordinates range from [-1, 1] (without <code>glClipControl</code>).</p>
<p>Yes, you can use a projection matrix that maps the camera-space Z range to [1, 0] in NDC space. However, this creates two problems:</p>
<ol>
<li><p>This is done by using a clip space whose Z range is effectively [W, 0]. But OpenGL doesn't care what you <em>think</em> the range is; for GL, clip space is still [-W, W]. This means you will get <em>no clipping</em> in the [0, -W] parts of your scene. You can compensate for this by using a user-defined clip plane, but those aren't performance free.</p>
</li>
<li><p>It doesn't actually fix the problem. The site you cited was written well after <code>glClipControl</code>, so it doesn't go into details for why this doesn't work. But <a href="https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html" rel="nofollow noreferrer">this blog post</a> was written beforehand, so it goes into those details.</p>
<p>Because OpenGL still thinks the NDC space Z range is [-1, 1], it is going to map that space to [Rn, Rf] in the window-space transform, where <code>Rn</code> and <code>Rf</code> are the <code>glDepthRange</code> near and far values. So the transformation to window-space depth is this: you halve the NDC Z value, scale up by the difference between Rn and Rf... and then add half of that difference to get the window-space Z.</p>
<p>And that addition is the problem. As stated on that site:</p>
<blockquote>
<p>That 0.5 bias locks the floating point exponent and pretty much destroys any and all additional precision that the encoding of floating point values brings close to zero, since now there are just 24 bits of mantissa to handle the unfortunate 1/z shape.</p>
</blockquote>
<p>That site suggests using a [-1, 1] depth range, but that's not allowed in unextended OpenGL. That's why it suggests <code>glDepthRangeNV</code>.</p>
</li>
</ol>

CC BY-SA 4.0
2022-09-13T08:33:16.390Z
thanks for answering, great help.But when using glDepthRange(-1, 1) ，the mapping from ndc space to window space would be (1 - (-1)) * 0.5 * depth + (1 + -1),in which case the "add" operation wouldn't destroy the high precision near zero?
Grown
CC BY-SA 4.0
2022-08-31T14:12:04.543Z
2022-08-31T14:12:04.543Z
Nicol Bolas
<p>I read this article (<a href="https://developer.nvidia.com/content/depth-precision-visualized" rel="nofollow noreferrer">https://developer.nvidia.com/content/depth-precision-visualized</a>) about reverseZ and I'm confused about why is glClipControl necessary.Can't we juse modify ProjectionMatrix to make the depth in [0,1] and use glDepthRange(-1,1) to not do an extra (0.5* depth + 0.5) remapping before put it in the depth buffer?
<a href="https://i.sstatic.net/tl4n4.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/tl4n4.png" alt="enter image description here" /></a></p>

<projections><depth-map><precision>
Why need glClipControl-like extensions when implementing ReverseZ in opgl?
Grown
# Why need glClipControl-like extensions when implementing ReverseZ in opgl?

(Asked by: username_0 on 2022-08-31)

I read this article (<https://developer.nvidia.com/content/depth-precision-visualized>) about reverseZ and I'm confused about why is glClipControl necessary.Can't we juse modify ProjectionMatrix to make the depth in [0,1] and use glDepthRange(-1,1) to not do an extra (0.5\* depth + 0.5) remapping before put it in the depth buffer?
[![enter image description here](https://i.sstatic.net/tl4n4.png)](https://i.sstatic.net/tl4n4.png)

**Tags:** projections, depth-map, precision


---

## 1 answers

### Answer by username_1 on 2022-08-31. Score: 2

The screenshot you posted states the answer:

> The value gets mapped into [0, 1] for storage in the depth buffer later, but that doesn't help, since the initial mapping to [-1, 1] has already destroyed all the precision in the far half of the range.

That [-1, 1] mapping cannot be changed by your projection matrix or anything else. It is a fundamental part of the OpenGL-defined fixed-function transformation sequence. It's the transformation from clip-coordinates to NDC coordinates. NDC coordinates range from [-1, 1] (without `glClipControl`).

Yes, you can use a projection matrix that maps the camera-space Z range to [1, 0] in NDC space. However, this creates two problems:

1. This is done by using a clip space whose Z range is effectively [W, 0]. But OpenGL doesn't care what you *think* the range is; for GL, clip space is still [-W, W]. This means you will get *no clipping* in the [0, -W] parts of your scene. You can compensate for this by using a user-defined clip plane, but those aren't performance free.
2. It doesn't actually fix the problem. The site you cited was written well after `glClipControl`, so it doesn't go into details for why this doesn't work. But [this blog post](https://outerra.blogspot.com/2012/11/maximizing-depth-buffer-range-and.html) was written beforehand, so it goes into those details.

   Because OpenGL still thinks the NDC space Z range is [-1, 1], it is going to map that space to [Rn, Rf] in the window-space transform, where `Rn` and `Rf` are the `glDepthRange` near and far values. So the transformation to window-space depth is this: you halve the NDC Z value, scale up by the difference between Rn and Rf... and then add half of that difference to get the window-space Z.

   And that addition is the problem. As stated on that site:

   > That 0.5 bias locks the floating point exponent and pretty much destroys any and all additional precision that the encoding of floating point values brings close to zero, since now there are just 24 bits of mantissa to handle the unfortunate 1/z shape.

   That site suggests using a [-1, 1] depth range, but that's not allowed in unextended OpenGL. That's why it suggests `glDepthRangeNV`.

> **Comments:**
> **username_0**: thanks for answering, great help.But when using glDepthRange(-1, 1) ，the mapping from ndc space to window space would be (1 - (-1)) * 0.5 * depth + (1 + -1),in which case the "add" operation wouldn't destroy the high precision near zero?  

---

