OpenGL入门第4课--深度

1,870 阅读6分钟

深度相关的概念

深度

     深度说白了就是距离观察者的距离,同一个物体的不同顶点距离观察者的距离不一定相同,所以深度是相对于像素点的概念。深度就是该像素点在3D世界中距离摄像机的距离,其实就是该像素点的Z坐标值 。

深度缓冲区

     深度缓冲区就是一块内存区域,专门存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z值)越⼤大, 则离摄像机就越远 。

      深度缓冲区,一般由窗⼝管理系统,GLFW创建.深度值⼀般由16位,24位,32位值表示. 通常是24位.数越高,深度精确度更好 

深度测试

     深度缓冲区(DepthBuffer)和颜色缓冲区(ColorBuffer)是对应的,颜色缓冲区存储像素的颜色信息,而深度缓冲区存储像素的深度信息. 在决定是否绘制一个物体表面时, 首先要将表面对应像素的深度值与当前深度缓冲区中的值进⾏比较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利用这个像素对应的深度值和颜色值.分别更新深度缓冲区和颜色缓冲区. 这个过程称为”深度测试”。

     例如假设有这么一种场景:物体A被物体B遮档了一部分,即A和B有一部分的像素的x,y坐标相同,只有z不同。如图:

                                  

      假设没有深度缓冲区或者不开启深度测试,如果我们先绘制一个距离⽐较近的物体(B),再绘制距离较远的物体,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区并且开启深度测试后,绘制物体的顺序就不那么重要了. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除⾮调⽤ glDepthMask(GL_FALSE).来禁⽌止写入. 

指定深度测试比较运算符

       OpenGL允许修改深度测试中使用的比较运算符,来控制OpenGL什么时候该通过或丢弃一个片段,什么时候去更新深度缓冲。我们可以调用glDepthFunc函数来设置比较运算符。

glDepthFunc(GL_LESS);

这个函数接受下面表格中的比较运算符:

                     

     默认情况下使用的深度函数是GL_LESS,它将会丢弃深度值大于等于当前深度缓冲值的所有片段。

深度冲突

        由于深度缓冲区精度的限制,一个很常见的视觉错误会在两个平面或者三角形非常紧密地平行排列在一起时会发生,深度缓冲没有足够的精度来决定两个形状哪个在前面。结果就是这两个形状不断地在切换前后顺序,这会导致很奇怪的花纹。这个现象叫做深度冲突(Z-fighting),因为它看起来像是这两个形状在争夺(Fight)谁该处于顶端。

       深度冲突是深度缓冲的一个常见问题,当物体在远处时效果会更明显(因为深度缓冲在z值比较大的时候有着更小的精度)一个常用的解决方法就是让深度值之间产生间隔(Polygon Offset ,多边形偏移),代码如下:

glEnable(GL_POLYGON_OFFSET_FILL) //启用多边形偏移
						glPolygonOffset(-1,-1);//指定偏移量

      启用多边形偏移的参数有一下三种:

  • GL_POLYGON_OFFSET_POINT:对应光栅化模式: GL_POINT
  • GL_POLYGON_OFFSET_LINE :对应光栅化模式: GL_LINE
  • GL_POLYGON_OFFSET_FILL:对应光栅化模式: GL_FILL

      glPolygonOffset 需要2个参数: factor , units。每个Fragment的深度值都会增加如下所示的偏移量:Offset = ( m * factor ) + ( r * units);m是指多边形的深度的斜率的最大值,一个多边形越是与近裁剪⾯平⾏,m就越接近于0. r是指能产⽣于窗⼝坐标系的深度值中可分辨的差异最⼩值,它是是由具体OpenGL平台指定的一个常量.一个大于0的Offset 会把模型推到离你(摄像机)更远的位置,相应的一个小于0的Offset会把模型拉近。一般而言,只需要将-1.0 和 -1 这样简单赋值给glPolygonOffset 基本可以满足需求.  

      由于OpenGL是状态机所以用完别忘了关闭多边形偏移:

glDisable(GL_POLYGON_OFFSET_FILL) 

      深度冲突不能够被完全避免,但一般会有一些技巧有助于在你的场景中减轻或者完全避免深度冲突。

         第一个也是最重要的技巧是永远不要把多个物体摆得太靠近,以至于它们的一些三角形会重叠。通过在两个物体之间设置一个用户无法注意到的偏移值,你可以完全避免这两个物体之间的深度冲突。

       第二个技巧是尽可能将近平面设置远一些。在前面我们提到了精度在靠近平面时是非常高的,所以如果我们将平面远离观察者,我们将会对整个平截头体有着更大的精度。然而,将近平面设置太远将会导致近处的物体被裁剪掉,所以这通常需要实验和微调来决定最适合你的场景的平面距离。

      另外一个很好的技巧是牺牲一些性能,使用更高精度的深度缓冲。大部分深度缓冲的精度都是24位的,但现在大部分的显卡都支持32位的深度缓冲,这将会极大地提高精度。所以,牺牲掉一些性能,你就能获得更高精度的深度测试,减少深度冲突。

       我们上面讨论的三个技术是最普遍也是很容易实现的抗深度冲突技术了。还有一些更复杂的技术,但它们依然不能完全消除深度冲突。深度冲突是一个常见的问题,但如果你组合使用了上面列举出来的技术,你可能不会再需要处理深度冲突了。