了解了OpenGL的渲染流程和经常使用API后,就能够简单的绘制出图形了。可是在绘制中可能会碰到些意想不到的问题。bash
在默认状况下,咱们所渲染的每一个点、线或三角形都会在屏幕上进行光栅化,而且会按照在组合图元批次时指定的顺序进行排列,这在某些状况下会产生问题。其中一个可能出现的问题是,若是咱们绘制一个由不少个三角形组成的实体对象,那么第一个绘制的三角形可能会被后面绘制的三角形覆盖。函数
图中,圆环上有些三角形在圆环的背面,而另外一些则在圆环的正面。咱们应该是看不到背面的。性能
OpenGL中最简单的实体多边形就是三角形,它只有3个边。光栅化硬件最欢迎三角形,而如今三角形已是OpenGL中支持的惟一一种多边形了。每3个顶点定义一个新的三角形。下图是两个三角形,它们是用6个顶点进行绘制的,这6个顶点编号依次为V0到V5。测试
请注意链接顶点的线段上所标示的箭头。在绘制第一个三角形时,线条将按照从V0到V1,再到V2,最后回到V0的顺序来绘制一个闭合的三角形。这个路径是按照顶点被指定的顺序沿着顺时针方向的,这种方向特性也体如今了第二个三角形中。这种顺序与方向结合来指定顶点的方式称为环绕。图中右边的三角形就被称做是顺时针环绕的。若是咱们将左边三角形的V4和V5的位置进行交换,咱们就获得了逆时针环绕。下图是两个三角形,它们的缠绕方向相反。spa
在默认状况下,OpenGL认为具备逆时针方向环绕的多边形是正面的。这意味着图3的左侧是三角形的正面,而右侧是三角形的背面。3d
咱们经常但愿为一个多边形的正面和背面分别设置不一样的物理特征。咱们能够彻底隐藏一个多边形的背面,或者给它设置-种不一样的颜色和反射属性。纹理图像在背面三角形中也是相反的。在一个场景中,使全部的多边形保持环绕方向的一致,并使用正面多边 形来绘制全部实心物体的外表面是很是重要的。code
若是想改变OpenGL的这个默认行为,能够调用下面这个函数。cdn
/*
GL_CW:顺时针环绕的多边形将为正面
GL_CCW:逆时针环绕的多边形将为正面
*/
g1FrontFace(GL_CW) ;
复制代码
对于这个问题,一个可能的解决办法是,对这些三角形进行排序,而且首先渲染那些较远的三角形,再在它们上方渲染那些较近的三角形。这种方式称为“油画法" ( painters algorithm )。对象
这种方法在计算机图形处理中是很是低效的,主要缘由有两个。blog
油画法弊端:若是三个三角形是叠加的状况,油画法将没法处理!
在任何状况下,咱们都应该只能看到正面,看不到背面,那为什么还要浪费资源绘制背面呢?对三角形的区分正面和背面的缘由之一就是为了剔除。背面剔除可以极大的提升性能,并修正图1出现的问题。这种方式是很是高效的,在渲染的图元装配阶段就总体抛弃了一些三角形,并无执行任何不恰当的光栅化操做。
使用glEnable
和glDisable
函数便可设置表面剔除功能。
glEnable(GL_CULL_FACE); //开启
glDisable(GL_CULL_FACE) ; //关闭
复制代码
指明剔除的是正面仍是背面调用函数glCullFace
。
/*
mode的可选值:
GL_FRONT
GL_BACK
GL_FRONT_AND_BACK
*/
void glCullFace(GLenum mode);
复制代码
表面剔除后的效果
仔细观察不难发现咱们在进行表面剔除后仍然会有一些不现实的画面。
就算背面剔除可以消除位于对象背面的三角形,那么若是是重叠的独立对象又该怎么办呢?咱们以前提到过油画法,这种方法是根据一种油画使用的技术而得名的。咱们只要先简单地绘制背景,再在上面绘制较近的对象。这样作可能只要在画布上进行次数很少的绘制( 在手工绘制时更加有用),但对于图形硬件来讲,这样作会致使在同一个片断区域重复进行绘制,而每一次绘制都会产生性能开销。若是开销过大则致使光栅化过程变慢,咱们将这种方式称为“填充受限”。可是将油画法颠倒过来使用,实际上将会加速填充性能。首先绘制那些离观察者较近的对象,而后再绘制那些较远的对象。
深度测试将消除那些应该被已存在像素覆盖的像素,这将节省可观的存储器带宽。
深度测试是另一种高效消除隐藏表面的技术。它的概念很简单:在绘制一 个像素时,将一个值(称为z值)分配给它,这个值表示它到观察者的距离。而后,当另一个像素须要在屏幕上的一样位置进行绘制时,新像素的z值将与已经存储的像素的z值进行比较。若是新像素的z值比较大,那么它距离观察者就比较近,这样就在原来的像素上面,因此原来的像素就会被新的像素覆盖。若是新像素的z值更低,那么它 就必须位于原来像素的后面,不能遮住原来的像素。在内部,这个任务是经过深度缓冲区实现的,它存储了屏幕上每一个像素的深度值。
咱们在使用GLUT设置OpenGL窗口的时候,应该请求一个深度缓冲区并启用深度测试。
//申请一个颜色缓冲区和一个深度缓冲区。
glutInitDisplayMode(GLUT_DOUBLEI_GLUT_RGBA | GLUT_DEPTH);
//启用深度测试
glEnable(GL_DEPTH_TEST);
复制代码
若是没有深度缓冲区,那么启用深度测试的命令将被忽略。