5.1、深度渲染缓存(Depth Render Buffer)
三角形、线段和点是按它们被GPU处理的顺序被渲染的。如果没有一个深度渲染缓存(或者直接简称为深度缓存),为最后一个对象的绘制而产生的片元总是会覆盖以前渲染的层叠的片元。至今为止本书中的例子一直小心地控制绘图的顺序,以确保在没有深度缓存的情况下仍然可以产生正确的最终图像。
变换使从任意视点渲染场景成为可能。视点决定了场景中的哪一个对象会被渲染在其他对象的前面。例如,当从一个看向面部的视点渲染一个人类头部的几何图形时,所有组成头的后部的三角形都不会出现在渲染的场景中。然而,当依赖于渲染的顺序时,如果GPU最后处理的是头后部的三角形,那么即使不应该可见,最终它们也将是可见的。
在一些情况下,一个程序可以按照正确的渲染顺序从后向前排序对象。可以排序组成人类头部的三角形,以便组成面部的三角形是最后一个被GPU处理的并且覆盖组成头后部的三角形。不幸的是,排序三角形经常会导致重写顶点缓存的内容,并会击溃缓存带来的内存访问优化。如果视点变成面向头的一侧,那么组成面部的部分三角形和组成头后部的部分三角形在渲染场景中将都是可见的,但这需要一个不同的排序顺序。一 个变化的视点必然需要重新计算所绘制对象的正确顺序。此外,光排序可能还不够。在一些情况下,三角形贯穿彼此并且正确地渲染应该包含两个三角形的片元,但是排序会强制一个三角形的所有片元覆盖另一个的片元。
深度渲染缓存是一个可选的输出缓存,并且与像素颜色渲染缓存相似。第2章为了与像素颜色渲染缓存一起使用而介绍了“其他缓存”的概念,参见图2-2。几乎所有的OpenGL ES应用都使用深度缓存,因为几乎所有的OpenGL ES应用都使用坐标系变换来改变渲染的视点。在大部分情况下,一个 深度缓存会消除对于三角形、线段和点进行排序的需求。
注意深度缓存常常也叫做Z缓存,因为如果坐标系的X轴和Y轴对应于屏幕的宽和;高,那么Z轴指示的就是屏幕的内外。一个片元和视点之间的距离大体相当于这个片元沿着Z轴深入屏幕的位置。
每次渲染一个片元时,片元的深度(片元与视点之间的距离)被计算出来并与在深度缓存中为那个片元位置保存的值进行对比。如果这个片元的深度值更小(更接近视点),那么就用这个片元来替换在像素颜色渲染缓存中的那个片元位置的任何颜色,并用刚刚渲染的片元的深度值来更新深度缓存。如果一个片元的深度值比在深度缓存中保存的值更大,这意味着某些已经渲染的片元更接近于视点。在这种情况下,新的片元在还没有更新像素颜色渲染缓存的情况下就会被丢弃。
GPU把对于每个片元的深度的计算作为渲染的-一个固有部分。深度缓存的使用为GPU提供了一个用来保存计算出的深度的地方,之后这个深度又被GPU利用来控制在像素颜色渲染缓存中的片元的置换。
利用GLKit添加深度缓存
GLKView类让添加深度缓存变得很容易。只要设置视图的drawableDepthFormat属性为GLKViewDrawableDepthFormat16或者GLKViewDrawableDepthFormat24而不是默认值即可,具体执行代码为“GLKViewDrawableDepthFormatNone:view. drawable-DepthFormat =GLKViewDrawableDepthFormat16;"。
GLKit支持使用16位或者24位来保存深度值的深度渲染缓存。使用16 位只可以表现65536个不同的深度。如果两个片元的深度非常接近,那么深度缓存可能就没有足够的精度来区分它们。像素颜色渲染缓存中的最终片元颜色可能来自接近同一个深度的片元之一。事实上,这个结果有时也被称为深度冲突(Z-fighting), 因为最终的片元颜色常常会在可能性之间来回闪烁并在渲染场景中制造-个可见的干扰。
使用24位来保存深度值可以区分1700万个不同的深度值,同时这是以消耗更多的GPU稀缺内存为代价的。即使是使用24位,当共面三角形重叠的时候还可能会产生深度冲突,但这不是很常见。
配置OpenGLES状态的可选步骤允许程序改变GPU执行深度测试时所使用的雨数。例如,调用OpenGL ES glDepthFunc(GL_ALWAYS)函数实际上会禁止深度测试, 因为所有渲染的片元会替换像素颜色缓存中这个片元的位置的先前的任何颜色。默认的深度测试函数是GL_LESS,它表示每个片元的颜色只有在该片元的深度值小于深度缓存中该片元的位置所保存的值时才会替换像素颜色缓存的内容。更小的深度值意味着片元更接近视点。glDepthFunc() 指定的深度测试函数的完整集合是GL_NEVER、GL_ALWAYS、GL_LESS、GL_LEQUAL、GL_EQUAL、GL_GEQUAL、GL_GREATER或 者GL_NOTEQUAL。:
在前面的章节中,glClear() 函数已经被用在例子中,它清除了缓存的内容。例如,调用glClear(GL_COLOR_BUFFER__BIT)会设置当前帧缓存的像素颜色缓存中的每个像素的值为glClearColor(函数设定的颜色。调用glClear(GL_DEPTH_BUFFER_BIT)会设置当前帧缓存的深度缓存中的每个值为最大深度值。两个缓存通常用一行代码来清除,通过使用C语言的位OR操作符将参数结合到glClear()中,例如glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)。
注意 几乎所有本书的例子所使用的AGLKContext 中都包含“-setClearColor:” 和“- clear:"方法,这两个方法封装了OpenGL ES的glClearColor()和glClear() 函数。