深度测试是一种有效的用于隐藏表面消除的技巧,OpenGL提供了一些函数,允许在幕后完成这个任务。
它的概念非常简单:当一个像素被绘制时,它将被设置一个值(称为Z值),以表示它和观察者之间的距离。以后,当这个屏幕需要绘制另一个像素时,新像素的值就会与原先已经存在的那个像素进行比较。如果新像素的Z值更高,它就更靠近观察者,因此位于以前那个像素的前面。如果新像素的Z值更低,则它必须位于原先那个像素的后面,而不能遮住前面的那个像素。
为了启用深度测试,只要简单的调用
glEnable(GL_DEPTH_TEST);
我们通过以下函数绘制一大一小两个球
void DrawBigRedSphere()
{
GLfloat mat_specular[] = { 1.0, 0.0, 0.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat mat_ambient[] = { 1.0, 0.0, 0.0, 1.0 };
GLfloat mat_diffuse[] = { 1.0, 0.0, 0.0, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glutSolidSphere(0.5, 32, 32);
}
void DrawSmallBlueSphere()
{
GLfloat mat_specular[] = { 0.0, 0.0, 1.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat mat_ambient[] = { 0.0, 0.0, 1.0, 1.0 };
GLfloat mat_diffuse[] = { 0.0, 0.0, 1.0, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glutSolidSphere(0.3, 32, 32);
}
DrawBigRedSphere绘制的是大的红球,DrawSmallBlueSphere绘制的是小的蓝球,绘制的时候都采用默认坐标,即绘制在屏幕的中央,并且Z轴也不动,那么,从理论上来讲,大球会包含小球,小球就被大球遮掩了,我们显示要的应该也是这种效果。
看一下未开启深度测试情况下的情况我们先绘制大球,再绘制小球的和用相反顺序再绘制的情形:
代码如下:
void SceneShow(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
);
glPushMatrix();
glTranslatef(-0.5, 0.0, 0.0);
DrawBigRedSphere();
DrawSmallBlueSphere();
glPopMatrix();
glPushMatrix();
glTranslatef(0.5, 0.0, 0.0);
DrawSmallBlueSphere();
DrawBigRedSphere();
glPopMatrix();
glFlush();
}
我们会观察到很明显的绘制顺序导致的问题,左边的图因为先绘制大球再绘制小球,小的篮球竟然覆盖掉了应该在其外部的红球。实际使用中,我们可以都自己判断,然后用右边的正确绘制方式,随着图形的增多,会增加一定的计算量,OpenGL提供的深度测试就是为我们提供了更简便高效的方式,事实上,由于OpenGL使用的方式因为可以在测试后,直接绘制出最终图形而忽略绘制被覆盖掉的图形,效率上比覆盖绘制会更高一些(需要硬件支持)。
上述代码,仅仅需要添加一行,
glEnable(GL_DEPTH_TEST);
以启用深度测试,效果大不一样: