<p>If you are doing a perspective image and your model has implicit intersections then, if you use "linear Z", those intersections will appear in the wrong places. </p>

<p>For example, consider a simple ground plane with a line of telephone poles, receding into the distance,  which pierce the ground (and continue below). The implicit intersections will be determined by the interpolated depth values. If those are not interpolated with <code>1/Z</code>, then when the projected vertices have been computed with perspective, the image will look incorrect.</p>

<p>I apologise for the non-aesthetic quality of the following illustrations but I did them back in '97. </p>

<p>The first image shows the required rendering effect. (Note that the blue "pylons" go quite a long distance under the ground plane so are clipped at the bottom of the images)</p>

<p><a href="https://i.sstatic.net/E1PME.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/E1PME.png" alt="enter image description here"></a></p>

<p>This second image shows the result of using a non-reciprocal depth buffer: (Apologies for the change of scale - these were copied out of an old MS Word doc and I've no idea what has happened with the scaling.)</p>

<p><a href="https://i.sstatic.net/fouWc.png" rel="nofollow noreferrer"><img src="https://i.sstatic.net/fouWc.png" alt="enter image description here"></a></p>

<p>As you can see, the results are incorrect.</p>

<p>On another note, are sure you <em>really</em> want a linear Z representation? If you are rendering perspective, surely one wants more precision closer to the camera than at a distance?</p>

<p>Re your later comment:</p>

<blockquote>
  <p>“if those are not interpolated with 1/Z” that I don’t understand. What interpolation is that?</p>
</blockquote>

<p>The first thing to note is that, with a standard perspective projection, straight lines in world space remain straight lines in perspective space. Distances/lengths, however, are not preserved.</p>

<p>For simplicity, let us assume a trivial perspective transform is used to project the vertices, i.e.
<span class="math-container">$$X_{Screen} = \frac{X_{World}}{Z_{World}}$$</span>
<span class="math-container">$$Y_{Screen} = \frac{Y_{World}}{Z_{World}}$$</span>
We <em>should</em> also compute a reciprocal screen-space depth, e.g. <span class="math-container">$$Z_{Screen} = \frac{1}{Z_{World}}$$</span> but linear Z in the depth buffer would, to me, require something like: <span class="math-container">$$Z_{Screen} = scale*Z_{World}$$</span> <em>(We can assume here that scale=1)</em></p>

<p>Let's assume we have a line with world space end points<span class="math-container">$$
  \begin{matrix} \begin{bmatrix}
    0 \\
    0 \\
    1 \\
    \end{bmatrix} and 
   \begin{bmatrix}
    200 \\
    0   \\
    10  \\
    \end{bmatrix}\\
  \end{matrix}
$$</span>
With the perspective mapping these map to screen space coordinates
 <span class="math-container">$$ \begin{matrix}   \begin{bmatrix}
    0 \\
    0 \\
    1 \\
    \end{bmatrix} and \begin{bmatrix}
    20 \\
    0  \\
    0.1 \\
    \end{bmatrix}
 \end{matrix}$$</span></p>

<p>The rendering system/hardware will linearly interpolate the screen space z, so at the 1/2 way point of the line, as it appears on-screen, i.e. at pixel (10, 0), we would get a projected (inverse) Z value of 0.55, which corresponds to to a world space Z value value of ~1.818. Given the starting and end Z values, this is about 20% along the length of the line.</p>

<p>If instead, we tried to interpolate using the original Z values, we'd end up with Z corresponding to a world space value of 5.5. As long as nothing intersects, you might be ok (I've not thought about it too thoroughly) but anything with implicit intersections <em>will</em> be incorrect.</p>

<p>What I haven't mentioned is that once you introduce perspective correct texturing (or even perspective correct shading), you must do per-pixel interpolation of 1/w and, additionally, also compute, per pixel, the reciprocal of that interpolated value.</p>

CC BY-SA 4.0
2018-09-04T16:16:55.890Z
I don't think I'll be able to understand this answer without more math/diagrams. And yes, more precision, closer, probably makes sense, but a scaling from linear by `far / z`, which is standard, doesn't make sense. It yields a depth buffer that becomes more linear the closer the two clip planes are to each other. It seems like a conflation of two concepts: screen space-linear Z, and a non-constant depth buffer mapping for a performance hack.
Jessy
CC BY-SA 4.0
2018-09-05T02:51:38.407Z
Specifically, it’s the, “if those are not interpolated with 1/Z” that I don’t understand. What interpolation is that?
Jessy
CC BY-SA 4.0
2018-09-05T11:48:53.160Z
I'll add some additional text to hopefully explain
Simon F
CC BY-SA 4.0
2018-09-06T20:26:30.483Z
Thanks! I think the problem comes down to "The rendering system/hardware will linearly interpolate the screen space z". I was under the impression that NDC position would be computed as `(x, y, z) / w` per-fragment, but apparently, instead, we have to deal with a linearly-interpolated version of `(x/w, y/w, z/w)`? That doesn't seem reasonable to me in 2018, but it would be good to know if that's the hack we have to live with for now anyway!
Jessy
CC BY-SA 4.0
2018-09-07T08:01:08.263Z
To perform perspective correct texturing/shading/whatever, you need to linearly interpolate (Val/w) values, and then, per fragment, do a division by the linearly interpolated 1/w. It's a bit hard to explain just in a comment, but there is a little bit of an explanation in https://computergraphics.stackexchange.com/a/4799/209.  Alternatively, do a search for Jim Blinn's article "Hyperbolic Interpolation"
Simon F
CC BY-SA 4.0
2018-09-08T00:30:33.760Z
That article doesn't seem to be available without paying and I can't see a preview to know if it's worth it. 

Even if linear interpolation for a reciprocal was necessary for something, it could be interpolated along with the original values, and I don't think it would ever be the right choice for storing depth. I amended the question to emphasize position.
Jessy
CC BY-SA 4.0
2018-09-10T23:57:18.953Z
Okay, I've read the article now! From that, what I'm gathering is that it's not a problem that the division happens. Rather, the problem is that depth doesn't get written by dividing the NDC position's Z value by the interpolated clip space 1 / W value. Sound right? I don't understand why that wouldn't happen, if it happens for all the other interpolated values, such as UVs.
Jessy
CC BY-SA 4.0
2018-09-11T07:43:46.050Z
You *could* do that (there were proposals for a "W" buffer), but it's unnecessary for perspective depth comparisons as straight lines in world space remain straight lines in perspective space. If you ever try doing a traditional perspective drawing with pencil/ruler (e.g. using vanishing points) this may become evident.

What *doesn't* get preserved is relative lengths but that doesn't matter as you go along a line as it's "just a line". It's when you add something that does depend on distances, e.g. texturing, that you need to do the per-pixel division.
Simon F
CC BY-SA 4.0
2018-09-04T12:25:06.293Z
2019-01-30T09:25:08.350Z
2019-01-30T09:25:08.350Z
Simon F
<p>Using Z/W for the depth buffer goes deeper than just clipping against the near and far planes. As Simon alluded to, this has to do with interpolation between the vertices of a triangle, during rasterization.</p>

<p>Z/W is the unique option that allows NDC depth values to be correctly calculated for points in the interior of the triangle, by simply linearly interpolating the NDC depth values from the vertices, <em>in screen space</em>. In principle, we could use any function we like to map camera-space Z to the depth buffer value—but any other choice than Z/W would require more complicated math to be done per pixel, which would be slower, and more difficult to build in hardware.</p>

<p>Note that if you use a linear depth buffer, then of course linearly interpolating depth values will be correct in <em>world</em> space...but not, in general, in screen space! And it is screen space that matters for rasterization, as we need to be able to generate perspective-correct depth values (and other attribute values, like UVs) for each pixel center, or other sample point, within the screen-space bounds of a triangle being rasterized.</p>

CC BY-SA 4.0
2018-09-09T10:28:32.373Z
I don’t know how to design a GPU, but it seems to me that all that’s needed is to interpolate Z instead of Z/W, for linear depth, and Z/W interpolation could still happen afterwards for anything visible. I still can’t tell if this is a matter of good reasoning or one of “nobody cares so we don’t bother updating”.
Jessy
CC BY-SA 4.0
2018-09-09T18:42:17.337Z
Interpolating Z instead of Z/W _does not give correct results_ in screen space. Z/W does.
Nathan Reed
CC BY-SA 4.0
2018-09-10T05:00:23.203Z
Right. But if the depth buffer is quantized to a lower precision than position, then, aside from being performant when it works, it's not a good idea to store a scaled chunk of screen space Z. If linear interpolation is all we get, then clipping needs to happen in view space. And Z needs to be interpolated before division by W, for the depth buffer, and after it, for what you've gone over.

So is the answer to my question, "because GPUs have always only interpolated in clip space because it was the only practical solution on the first GPUs, and it has worked out well enough since"?
Jessy
CC BY-SA 4.0
2018-09-10T05:17:12.653Z
I'm not following what you mean about "quantized to a lower precision than position", or "store a scaled chunk of screen space Z".
Nathan Reed
CC BY-SA 4.0
2018-09-10T05:21:10.537Z
Also, "Z needs to be interpolated before division by W, for the depth buffer"—no. That's what I've been trying to explain. You get the _wrong answers_ if you interpolate Z (or anything else) in screen space without dividing it by W first. You seem to be stuck on this idea that a linear Z buffer would just work if we didn't divide by W. But it won't work—it won't interpolate in screen space properly.
Nathan Reed
CC BY-SA 4.0
2018-09-11T00:17:31.910Z
Sorry, I meant "clip space", not "view space" in my last comment. I'm not convinced that interpolating in clip space instead of NDC space won't work for Z, but I am convinced (as mentioned in my last comment to Simon) that interpolating 1/ZW in NDC space and then dividing the interpolated 1/Z by 1/W would work. Trouble is, that step doesn't happen. What ends up getting stored in the depth buffer is linear depth, divided by distance from the camera (which is useful as you've said), scaled  by the far clip plane – this last part seems a hack to achieve 1 at the far clip plane, to avoid division.
Jessy
CC BY-SA 4.0
2018-09-09T04:49:52.037Z
2018-09-09T04:49:52.037Z
Nathan Reed
<p><em>Background:</em><br>
I found that it is very easy to use a linear depth buffer, using only a slight modification to the canonical vertex transformation. The simplest method is found at the bottom of <a href="https://www.mvps.org/directx/articles/linear_z/linearz.htm" rel="noreferrer">https://www.mvps.org/directx/articles/linear_z/linearz.htm</a>.</p>

<p>However, the caveat is that it only works for triangles that don't need to be clipped against the near or far planes. (And an alternate solution, of performing the perspective divide in the vertex shader, will yield a similar problem for the other four frustum planes.)</p>

<p>Because clipping requires linear interpolation to work across all four clip space coordinates, I think it's impossible to work with linear depth, using  only a vertex shader. But the reason for that is all down to Z being divided by W.</p>

<p>Why is that done? X and Y need to be divided by distance from the camera, but the Z coordinate does not, to fit perfectly into the NDC box.</p>

<projections><clipping>
Why do GPUs divide clip space Z by W, for position?
Jessy
# Why do GPUs divide clip space Z by W, for position?

(Asked by: username_0 on 2018-09-04)

*Background:*  
I found that it is very easy to use a linear depth buffer, using only a slight modification to the canonical vertex transformation. The simplest method is found at the bottom of <https://www.mvps.org/directx/articles/linear_z/linearz.htm>.

However, the caveat is that it only works for triangles that don't need to be clipped against the near or far planes. (And an alternate solution, of performing the perspective divide in the vertex shader, will yield a similar problem for the other four frustum planes.)

Because clipping requires linear interpolation to work across all four clip space coordinates, I think it's impossible to work with linear depth, using only a vertex shader. But the reason for that is all down to Z being divided by W.

Why is that done? X and Y need to be divided by distance from the camera, but the Z coordinate does not, to fit perfectly into the NDC box.

**Tags:** projections, clipping


---

## 2 answers

### Answer by username_1 on 2018-09-04. Score: 15

If you are doing a perspective image and your model has implicit intersections then, if you use "linear Z", those intersections will appear in the wrong places.

For example, consider a simple ground plane with a line of telephone poles, receding into the distance, which pierce the ground (and continue below). The implicit intersections will be determined by the interpolated depth values. If those are not interpolated with `1/Z`, then when the projected vertices have been computed with perspective, the image will look incorrect.

I apologise for the non-aesthetic quality of the following illustrations but I did them back in '97.

The first image shows the required rendering effect. (Note that the blue "pylons" go quite a long distance under the ground plane so are clipped at the bottom of the images)

[![enter image description here](https://i.sstatic.net/E1PME.png)](https://i.sstatic.net/E1PME.png)

This second image shows the result of using a non-reciprocal depth buffer: (Apologies for the change of scale - these were copied out of an old MS Word doc and I've no idea what has happened with the scaling.)

[![enter image description here](https://i.sstatic.net/fouWc.png)](https://i.sstatic.net/fouWc.png)

As you can see, the results are incorrect.

On another note, are sure you *really* want a linear Z representation? If you are rendering perspective, surely one wants more precision closer to the camera than at a distance?

Re your later comment:

> “if those are not interpolated with 1/Z” that I don’t understand. What interpolation is that?

The first thing to note is that, with a standard perspective projection, straight lines in world space remain straight lines in perspective space. Distances/lengths, however, are not preserved.

For simplicity, let us assume a trivial perspective transform is used to project the vertices, i.e.
$$X\_{Screen} = \frac{X\_{World}}{Z\_{World}}$$
$$Y\_{Screen} = \frac{Y\_{World}}{Z\_{World}}$$
We *should* also compute a reciprocal screen-space depth, e.g. $$Z\_{Screen} = \frac{1}{Z\_{World}}$$ but linear Z in the depth buffer would, to me, require something like: $$Z\_{Screen} = scale\*Z\_{World}$$ *(We can assume here that scale=1)*

Let's assume we have a line with world space end points$$
\begin{matrix} \begin{bmatrix}
0 \\
0 \\
1 \\
\end{bmatrix} and
\begin{bmatrix}
200 \\
0 \\
10 \\
\end{bmatrix}\\
\end{matrix}
$$
With the perspective mapping these map to screen space coordinates
$$ \begin{matrix} \begin{bmatrix}
0 \\
0 \\
1 \\
\end{bmatrix} and \begin{bmatrix}
20 \\
0 \\
0.1 \\
\end{bmatrix}
\end{matrix}$$

The rendering system/hardware will linearly interpolate the screen space z, so at the 1/2 way point of the line, as it appears on-screen, i.e. at pixel (10, 0), we would get a projected (inverse) Z value of 0.55, which corresponds to to a world space Z value value of ~1.818. Given the starting and end Z values, this is about 20% along the length of the line.

If instead, we tried to interpolate using the original Z values, we'd end up with Z corresponding to a world space value of 5.5. As long as nothing intersects, you might be ok (I've not thought about it too thoroughly) but anything with implicit intersections *will* be incorrect.

What I haven't mentioned is that once you introduce perspective correct texturing (or even perspective correct shading), you must do per-pixel interpolation of 1/w and, additionally, also compute, per pixel, the reciprocal of that interpolated value.

> **Comments:**
> **username_0**: I don't think I'll be able to understand this answer without more math/diagrams. And yes, more precision, closer, probably makes sense, but a scaling from linear by `far / z`, which is standard, doesn't make sense. It yields a depth buffer that becomes more linear the closer the two clip planes are to each other. It seems like a conflation of two concepts: screen space-linear Z, and a non-constant depth buffer mapping for a performance hack.  
> **username_0**: Specifically, it’s the, “if those are not interpolated with 1/Z” that I don’t understand. What interpolation is that?  
> **username_1**: I'll add some additional text to hopefully explain  
> **username_0**: Thanks! I think the problem comes down to "The rendering system/hardware will linearly interpolate the screen space z". I was under the impression that NDC position would be computed as `(x, y, z) / w` per-fragment, but apparently, instead, we have to deal with a linearly-interpolated version of `(x/w, y/w, z/w)`? That doesn't seem reasonable to me in 2018, but it would be good to know if that's the hack we have to live with for now anyway!  
> **username_1**: To perform perspective correct texturing/shading/whatever, you need to linearly interpolate (Val/w) values, and then, per fragment, do a division by the linearly interpolated 1/w. It's a bit hard to explain just in a comment, but there is a little bit of an explanation in https://computergraphics.stackexchange.com/a/4799/209.  Alternatively, do a search for Jim Blinn's article "Hyperbolic Interpolation"  
> **username_0**: That article doesn't seem to be available without paying and I can't see a preview to know if it's worth it.   Even if linear interpolation for a reciprocal was necessary for something, it could be interpolated along with the original values, and I don't think it would ever be the right choice for storing depth. I amended the question to emphasize position.  
> **username_0**: Okay, I've read the article now! From that, what I'm gathering is that it's not a problem that the division happens. Rather, the problem is that depth doesn't get written by dividing the NDC position's Z value by the interpolated clip space 1 / W value. Sound right? I don't understand why that wouldn't happen, if it happens for all the other interpolated values, such as UVs.  
> **username_1**: You *could* do that (there were proposals for a "W" buffer), but it's unnecessary for perspective depth comparisons as straight lines in world space remain straight lines in perspective space. If you ever try doing a traditional perspective drawing with pencil/ruler (e.g. using vanishing points) this may become evident.  What *doesn't* get preserved is relative lengths but that doesn't matter as you go along a line as it's "just a line". It's when you add something that does depend on distances, e.g. texturing, that you need to do the per-pixel division.  

---

### Answer by username_2 on 2018-09-09. Score: 7

Using Z/W for the depth buffer goes deeper than just clipping against the near and far planes. As username_1 alluded to, this has to do with interpolation between the vertices of a triangle, during rasterization.

Z/W is the unique option that allows NDC depth values to be correctly calculated for points in the interior of the triangle, by simply linearly interpolating the NDC depth values from the vertices, *in screen space*. In principle, we could use any function we like to map camera-space Z to the depth buffer value—but any other choice than Z/W would require more complicated math to be done per pixel, which would be slower, and more difficult to build in hardware.

Note that if you use a linear depth buffer, then of course linearly interpolating depth values will be correct in *world* space...but not, in general, in screen space! And it is screen space that matters for rasterization, as we need to be able to generate perspective-correct depth values (and other attribute values, like UVs) for each pixel center, or other sample point, within the screen-space bounds of a triangle being rasterized.

> **Comments:**
> **username_0**: I don’t know how to design a GPU, but it seems to me that all that’s needed is to interpolate Z instead of Z/W, for linear depth, and Z/W interpolation could still happen afterwards for anything visible. I still can’t tell if this is a matter of good reasoning or one of “nobody cares so we don’t bother updating”.  
> **username_2**: Interpolating Z instead of Z/W _does not give correct results_ in screen space. Z/W does.  
> **username_0**: Right. But if the depth buffer is quantized to a lower precision than position, then, aside from being performant when it works, it's not a good idea to store a scaled chunk of screen space Z. If linear interpolation is all we get, then clipping needs to happen in view space. And Z needs to be interpolated before division by W, for the depth buffer, and after it, for what you've gone over.  So is the answer to my question, "because GPUs have always only interpolated in clip space because it was the only practical solution on the first GPUs, and it has worked out well enough since"?  
> **username_2**: I'm not following what you mean about "quantized to a lower precision than position", or "store a scaled chunk of screen space Z".  
> **username_2**: Also, "Z needs to be interpolated before division by W, for the depth buffer"—no. That's what I've been trying to explain. You get the _wrong answers_ if you interpolate Z (or anything else) in screen space without dividing it by W first. You seem to be stuck on this idea that a linear Z buffer would just work if we didn't divide by W. But it won't work—it won't interpolate in screen space properly.  
> **username_0**: Sorry, I meant "clip space", not "view space" in my last comment. I'm not convinced that interpolating in clip space instead of NDC space won't work for Z, but I am convinced (as mentioned in my last comment to username_1) that interpolating 1/ZW in NDC space and then dividing the interpolated 1/Z by 1/W would work. Trouble is, that step doesn't happen. What ends up getting stored in the depth buffer is linear depth, divided by distance from the camera (which is useful as you've said), scaled  by the far clip plane – this last part seems a hack to achieve 1 at the far clip plane, to avoid division.  

---

