Convert Z Buffer value to W
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.
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.
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.
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...
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)
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
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
Popular Topics
Advertisement