Learning to love your z_buffer 中文 (z缓冲)

Learning to Love your Z-buffer.


By Steve Baker
  • We British pronounce
    the letter 'Z' as "zed" - but
    Americans pronounce it "zee".

    ...although that's just to make
    The Alphabet Song rhyme.

    -- Steve

Introduction

One of the more common OpenGL programming problems that I see concerns the poor precision of the Z buffer.

Many of the early 3D adaptors for the PC have a 16 bit Z buffer, some others have 24 bits - and the very best have 32 bits. If you are lucky enough to have a 32 bit Z buffer, then Z-precision may not seem to be an issue for you. However, if you expect your program to be portable, you'd better give it some thought.

The precision of Z matters because the Z buffer determines which objects are hidden behind which others - and if you don't have enough precision to resolve the distance between two nearby objects, they will randomly show through each other - sometimes in large zig-zags, sometimes in stripes.

This is commonly called 'flimmering' or 'Z-fighting' and it's very disturbing to the user.

The Near Clip Plane

The near clip plane (zNear for short) is typically set using gluPerspective() or glFrustum() - although it's also possible to set it by setting the GL_PROJECTION matrix directly.

Some graphics programmers call zNear 'hither' and zFar 'yonder'.

Beginners frequently place zNear at a very short distance because they don't want polygons close to the eye to be clipped against the near plane - and because it isn't obvious why you'd want to do anything else.

Positioning of zNear too close to the eye is the cause of flimmering (in almost every case) - and the remainder of this document explains why that is.

The Resolution of Z.

What people often fail to realise is that in nearly all machines, the Z buffer is non-linear. The actual number stored in the Z buffer memory is related to the Z coordinate of the object in this manner:
  z_buffer_value = (1<<N) * ( a + b / z )

  Where:

     N = number of bits of Z precision
     a = zFar / ( zFar - zNear )
     b = zFar * zNear / ( zNear - zFar )
     z = distance from the eye to the object

  ...and z_buffer_value is an integer.

This means that Z (and hence the precision of Z) is proportional to the reciprocal of the z_buffer_value - and hence there is a LOT of precision close to the eye and very little precision off in the distance.

This reciprocal behaviour is somewhat useful because you need objects that are close to the eye to be rendered in great detail - and you need better Z precision for detailed objects.

However, an undesirable consequence of this is that many of your Z buffer's bits are wasted - storing insanely fine detail close to the near clip plane. If you pull the near clip closer to your eye, then ever more bits are dedicated to the task of rendering things that are that close to you, at considerable cost to the precision a bit further out.

It follows that in most cases, flimmering can be greatly reduced - or even eliminated by moving the near clip plane further from your eye.

Z Calculator

You can use this handy JavaScript calculator to see how your near and far clip plane choices affect Z precision at various ranges. Make sure you use the same units for all three distances - the precision result is in those same units. Make a change to the inputs and hit return and the results will be instantly recalculated.

Input Your Data Here:

  • Num.bits in Z buffer(Usually 24 on modern graphics cards - 16 on older devices)
    zNear(This number should be as large as you can tolerate)
    zFar(This number can be very big for negligable penalty)
    Z distance(This should lie between zNear and zFar for meaningful results)

Results:

  • Resolution of Z Buffer at this range =(The smallest depth separation it can resolve at this range)
    Value in Z Buffer =(The actual number that'll be written into memory)

How Bad Is It?

Talking about how large the error is for absolute values of zNear (in feet, meters, lightyears or angstroms) is meaningless. Given the math used to convert Z into the number in the Z buffer, it's also fairly meaningless to talk about absolute values in "OpenGL units" either.

Instead we have to think about the ratio of distances to objects in your scene to the value of zNear.

This equation applies:

  delta = z * z / ( zNear * (1<<N) - z )

  Where:

     N     = number of bits of Z precision
     zNear = distance from eye to near clip plane
     z     = distance from the eye to the object
     delta = the smallest resolvable Z separation
             at this range.

This equation is approximate - it only applies if zNear is much smaller than zFar - which is true for nearly all applications.

For another way to think about this, suppose we choose to think about the range at which there is a n% error in Z due to the precision of the Z buffer. For ease of discussion, I'll call the ratio of that range to the value of zNear 'Zn%'.

Hence, Z5% is the "range at which there is a 5% error in Z" divided by zNear.

For a 16 bit Z buffer, the value of Z5% is about 3500. It varies *slightly* depending on the value of zFar, and for very small values of zFar, it does get a little bigger - but for practical applications, 3500 is a good rule-of-thumb.

What this means in practice, is that if you place zNear at 1 meter (in whatever units your database uses), then when an object is at 3,500 meters, there will be a 5% error in it's Z value.

  For 16 bit Z:

       Z10%   = ~8000 *
       Z5%    =  3500
       Z1%    =   666
       Z0.1%  =    66
       Z0.01% =     6

  • * NB: The larger the range, the more the zFar distance starts to affect the precision - but for most practical applications, it doesn't make enough of a difference to matter.

The table tell us that value for Z1% is 666 and Z10% is ~8000. So with our 1 meter zNear, we can expect better than 1% precision below 666 meters, better than 5% precision below 3500 meters and better than 10% at under 8000 meters.

Now, if your zNear is at 10cm, you'll see a 5% error at just 350 meters (3500*0.1m)- and out at 3.5km meters, the error will be around 33% - over a kilometer in error. An airplane flying behind a huge mountain could suddenly pop into view in front of it when we are are only a couple of miles away!

You can see that the placement of zNear is really critical in a 16 bit Z system.

For a 24 bit Z buffer, the ratios are 256 times larger so Z1% is ~170,000, and Z5% is about a million.

For a 32 bit Z buffer, even Z1% is about 45 million and we are unlikely to care about Z5% and Z10% metrics!

Caveats

There are other sources of Z error (although with a 16 bit Z buffer, they are likely to be pretty negligable compared to the error in storing Z).

Some machines offer alternative depth buffering algorithms. These tend to fall into two catagories:

  • The W-buffer (aka OOZ-buffer - One-Over-Z) stores the reciprocal of the usual Z-buffer value. This is generally harder to compute and results in a W that is essentially linear and has uniform error everywhere. That drastically improves Z-precision at larger ranges, but greatly reduces precision close to the eye where it's needed. Several PC cards (eg 3Dfx) support OOZ buffers, but not necessarily under OpenGL.
  • Floating point or Logrithmic Z (these are pretty similar concepts actually). This also tends to even out the distribution of bits over the Z range - but without completely eliminating the improved resolution close to the eye. SGI Infinite Reality machines do this - and claim to get the equivelent *useful* Z precision using 15 bits as a conventional machine gets with 24bits.

Conclusion

Always put zNear as far from the eye as you can tolerate.





学会爱你的Z缓冲。


由史蒂夫·贝克
  • 我们英国发音 
    字母'Z'为“捷思” -但 
    美国人发音为“ZEE”。

    ......尽管这只是为了让 
    字母歌韵。 

    - 史蒂夫

介绍

其中,我看到的关注Z缓存的精度差比较常见的OpenGL编程问题。

多为PC早期的3D适配器有一个16位Z缓存,另一些人有24位 - 而最好的有32位。 如果你足够幸运,有一个32位Z缓存,那么Z-精度可能似乎不是你的问题。 但是,如果你希望你的程序是可移植的,你最好给它一些思考。

的Z-事项的精度,因为Z缓存决定哪些对象背后都隐藏着哪些人 - 如果你没有足够的精度,以解决附近的两个物体之间的距离,他们会随机通过对方表明 - 有时在大型锯齿牛头犬有时在条纹。

这通常被称为“flimmering”或“Z-战斗',这是非常令人不安的用户。

近剪裁平面

近剪裁平面(zNear的简称)通常设置使用gluPerspective()或glFrustum() - 尽管它也可以通过直接设置GL_PROJECTION矩阵来设置它。

一些图形程序员称之为zNear'这儿'和zFar'那边'。

初学者经常把zNear在很短的距离,因为他们不想多边形靠近眼睛要针对近平面裁剪 - 因为它不是显而易见的,为什么你想要做别的。

定位zNear太靠近眼睛是flimmering的原因(在几乎所有情况下) - 和这个文件的剩余部分解释这是为什么。

Z.的决议

人们往往没有意识到的是,在几乎所有的机器,在Z缓冲区是非线性的。   存储在Z缓冲器存储器的实际数目是相关于Z以这种方式的对象的坐标:
   z_buffer_value =(1 << N)*(A + B / z)的

  其中:

      N是精准ž位数
      A = zFar /(zFar  -  zNear)
      B = zFar * zNear /(zNear  -  zFar)
      Z =距离从眼睛到对象

   ...和z_buffer_value是整数。

这意味着,Z(并且因此Z的精度)正比于z_buffer_value的倒数 - 因此存在的精度接近眼睛和很少精度关在远处了很多。

你需要更好ž的精度进行详细的对象 - 因为你需要是接近了非常详细呈现的眼睛对象这种相互的行为是有点用处。

然而,这方面的一个不良后果是,许多的Z缓冲器的比特被浪费 - 存储出奇细节靠近邻近剪辑平面。 如果你拉附近的剪辑更接近你的眼睛,越看越位专门用于渲染的东西是任务的接近你,在相当大的代价,以精密远一点出来。

它遵循在大多数情况下,flimmering可以大大减少 - 或甚至通过从眼睛进一步移动邻近剪辑平面消除。

ž计算器

你可以使用这个方便的JavaScript计算器,看看你的远近裁剪面的选择将如何影响ž精度在不同的范围。  确保您使用相同的单位所有三个距离 - 精度结果是那些相同的单位。 进行了更改输入并回车,结果将立即重新计算。

输入您的数据在这里:

  • 在Z轴缓冲Num.bits(通常24现代显卡 - 对旧设备16)
    zNear(这个数字应该是一样大,你能容忍)
    zFar(这个数字是非常大的损失可以忽略不计)
    Z方向的距离(这应该zNear和zFar之间躺了有意义的结果)

结果:

  • Z缓存的分辨率在这个范围=(最小深度分离可以在这个范围内解决)
    在Z轴缓冲值=(那会被写入到内存的实际数量)

怎么不好呢?

谈到如何大的误差是zNear的绝对值(英尺,米,光年还是埃)是没有意义的。   鉴于用来转换成ž在Z缓冲区数的数学,这也是没有意义的谈论中的“OpenGL的单位”绝对值两种。

相反,我们必须考虑到的物体在你的场景zNear值的距离比。

该方程适用于:

  增量= Z * Z /(zNear *(1 << N) -  Z)

  其中:

      N是精准ž位数
      zNear =距离从眼睛到近裁剪面
      Z =距离从眼睛到对象
     三角洲=最小分辨Z间距
             在此范围内。

这个方程是近似的 - 它仅适用于如果zNear比zFar小得多 - 这是真实的,几乎所有的应用程序。

对于另一种方式来想一想,假设我们选择考虑在其中有在Z轴的%的误差是由于Z缓存的精度范围内。 为便于讨论,我会打电话给这个范围对zNear“锌%”的价值之比。

因此,Z5%是“范围时,有Z中有5%的误差”被zNear分。

对于一个16位的Z缓冲器中,Z5%的值是大约3500。它变化* *略微取决于zFar的值,而对于zFar的非常小的值,但它确实会有点大 - 但对于实际应用中,3500是一个好的规则的拇指。

这意味着在实践中,是,如果你把zNear在1米(以任何单位数据库使用),那么当一个目的是为3500米,将有5%的误差,在它的Z值。

  对于16位Z:

        Z10%=〜8000 *
        Z5%= 3500
        Z1%= 666
        Z0.1%= 66
        Z0.01%= 6

  • *注:在该范围时,所述zFar距离开始影响精度越 - 但是对于大多数实际应用中,它不会使足够的差,以无关紧要的。

该表告诉我们,Z1%,该值是666和Z10%是〜8000。 所以我们1米zNear,我们可以预期优于1%的精度低于666米,超过5%的精度低于3500米和优于10%,好于下8000米。

现在,如果你的zNear为10cm处,你会看到一个5%的误差在350米(3500 *0.1米) - 进出的3.5公里米,误差约为33% - 在错误公里。 飞机飞行的背后隐藏着巨大的山会突然弹出到视图在它面前的时候,我们只有几英里远!

你可以看到,安置zNear的是一个16位位Z系统的真正关键。

对于24位Z缓存,该比率为256倍等Z1%为〜17万,而Z5%是一百万。

对于32位Z缓存,即使Z1%约为45亿美元,我们不太可能去关心Z5%和Z10%的指标!

注意事项

有面向Z其他误差源(虽然带有一个16位的Z缓冲器,他们很可能比在存储Z中的误差是相当微不足道)。

有些机器提供替代的深度缓冲算法。 这些往往分为两大名作:

  • 在W-缓冲液(又名OOZ缓冲区 - 一超过-Z)存储通常的Z缓冲器值的倒数。 这通常是难以计算的结果和在W是基本上线性的,并具有均匀的错误无处不在。 在更大的范围内,大大提高了Z-精度,但大大降低了精密接近的地方,它所需的眼球。 几个PC卡(如3Dfx的)支持OOZ缓冲,但在OpenGL的不一定。
  • 浮点或Logrithmic Z(这些都是非常类似的概念其实)。 这也倾向于拉平比特在z范围的分布 - 但没有完全消除了改进的分辨率接近眼球。 SGI无限现实机器做到这一点 - 并声称得到equivelent *有用* Z使用15位作为一个传统的机器得到与24位精度。

结论

始终把zNear尽可能远离眼睛,你可以容忍的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值