Hybrid666 125 Report post Posted March 11, 2009 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. 0 Share this post Link to post Share on other sites
Figgles 122 Report post Posted March 11, 2009 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. 0 Share this post Link to post Share on other sites
Hybrid666 125 Report post Posted March 15, 2009 Thanks for the advice on linear Z :) but is my Z to W conversion formula correct? 0 Share this post Link to post Share on other sites
Figgles 122 Report post Posted March 15, 2009 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.0Write a function that:ZNear -> 0.0ZFar -> 1.0Start 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 byf(x) = (x - A) / (B - A) 0 Share this post Link to post Share on other sites
Hybrid666 125 Report post Posted March 16, 2009 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 0 Share this post Link to post Share on other sites
JarkkoL 153 Report post Posted March 16, 2009 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') 0 Share this post Link to post Share on other sites