Sign in to follow this  
Hybrid666

Convert Z Buffer value to W

Recommended Posts

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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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)


Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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')

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this