Convert Z Buffer value to W

Started by
4 comments, last by JarkkoL 15 years, 1 month ago
Hi, I've been trying to convert Z buffer values to linear W values in a pixel shader. I've looked at http://en.wikipedia.org/wiki/Z-buffer, but that didn't seem to give me the results I was looking for, so then I looked on here and found (in another thread) this: float fFarOverNear = (fFar / fNear); return fDepth / (fFarOverNear - (fFarOverNear - 1) * fDepth); which seems to work fine.. but looks too simple to be correct.. is the the right way to do this? Thanks in advance, Hybrid PS. I've also considered making my Z buffer linear so no conversion is necessary, but I'm unsure of the pitfalls of such an approach... but regardless of this, I'd still like to be sure I can convert between Z and W values properly.
Advertisement
In short, the problem is that W is linear [Near, Far], Z is linear [0.0, 1.0], but after the perspective divide, you get Z' = Z/W, which isn't linear. W increases much faster than Z, causing most of the precision to be lost.

Solution: Before perspective divide, multiply Z by W in vertex shader. Then when the perspective divide happens, the result is just Z, which varies linearly from [0.0, 1.0].
Conversely, if the perspective divide already happened, multiply by W again to get linear Z.

Pitfalls:
Linear distribution of Z means that the traditional 90% precision in first ~10% of space means that near objects have less depth precision. If you need to view stuff that is far away, then this isn't a problem, but if you have a lot of up-close action, there may be noticeable artifacts.
Thanks for the advice on linear Z :) but is my Z to W conversion formula correct?
You should be able to derive that by yourself. What are you trying to accomplish that hasn't already been explained? You get Z/W at the end of the vertex shader from the divide by W. Why do you need any of these out-of-context formulae that you don't understand? I wouldn't copy code and then ask someone else if it is correct.

About this...
Quote:
float fFarOverNear = (fFar / fNear);
return fDepth / (fFarOverNear - (fFarOverNear - 1) * fDepth);

I can't tell you if it is correct as the symbols are not defined. I have no idea what the range of these numbers are. I have however already told you how to convert from non-linear Z to linear Z. If you have 'ZFar', 'ZNear', and Z element of [ZNear, ZFar], then you can convert to linear Z easily.

Non-normalized Z (W): Z goes from 'ZNear' to 'ZFar'.
Normalized (linear) Z: Z goes from 0.0 to 1.0

Write a function that:
ZNear -> 0.0
ZFar -> 1.0

Start by mapping ZNear to 0.0. That's easy: Z' = Z - ZNear. When Z= 'ZNear', you get 0.0.

So the function so far is Z' = Z - ZNear.

Now we need to map ZFar to 1.0. We need to just divide by itself to get 1.0. But wait, we've already done a subtraction by ZNear so that ZNear maps to 0.0. Looks like we need subtract that too. That means we don't have ZFar, but ZFar - ZNear. How can we make that go to 1.0? Easy, divide by ZFar - ZNear.

Z' = (Z - ZNear) / (ZFar - ZNear)

That will take values in the form of [ZNear,ZFar] -> [0.0,1.0].


In general, mapping anything from [A,B] to [0.0, 1.0] is done by

f(x) = (x - A) / (B - A)


Hi,

Thanks for the info, but I'm still a bit confused :(

I know how to do a lerp to move a value into a 0-1 range, but my understanding was that Z buffer values weren't linear between ZNear and ZFar, and so a lerp wouldn't work?

I didn't really phrase my original question correctly either tho - I do want to understand the process rather than just be told "yes it's right" / "no it's wrong" :)

Cheers for the help
The value that gets stored to Z-buffer is Z'=Z/W, where Z and W values are the projection space coordinates (i.e. what you output from vertex shader). W can be represented as W=a*Z+b, where a and b coefficients can be calculated from the lower-right 2x2 matrix of the projection matrix. So Z'=Z/(a*Z+b), thus by solving Z, you get: Z=bZ'/(1-aZ')

This topic is closed to new replies.

Advertisement