在介绍正背面剔除与深度测试之前,首先我们需要准备一个案例说明为什么我们需要使用他们。这边我们先绘制一个甜甜圈,因为OpenGL中提供了相关的模型,所以我们直接使用即可。
部分代码:
void SetupRC()
{
glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
shaderManager.InitializeStockShaders();
viewFrame.MoveForward(7.0);//移动物体
//4.创建一个甜甜圈 是点光源着色器才有暗面
//void gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
//参数1:GLTriangleBatch 容器帮助类
//参数2:外边缘半径
//参数3:内边缘半径
//参数4、5:主半径和从半径的细分单元数量,一般是两倍
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);
glPointSize(4.0f);
}
void RenderScene()
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
//把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//单元着色器无法运用模型视图矩阵,投影矩阵
//使用平面着色器
//参数1:平面着色器
//参数2:模型视图投影矩阵
//参数3:颜色
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
torusBatch.Draw();
modelViewMatix.PopMatrix();
glutSwapBuffers();
}
这样我们就绘制出了一个甜甜圈
甜甜圈.png
正背面剔除
但是当我们旋转这个甜甜圈时,就出现了一些bug
甜甜圈2.png
这又是为什么呢?因为一个物体在光照下是有两面的:阳面(光照覆盖的面)和阴面(背光的面),在OpenGL中也是一样的道理,甜甜圈的背光面其实是看不到的部分,OpenGL将它绘制成了黑色,而在甜甜圈在旋转过程中,OpenGL不知道该显示哪些界面,导致本来是观察者不应该看到且该丢弃部分,不仅看到了,而且没有将隐藏部分丢弃。
此时,我们就需要使用正背面剔除,把观察者看不到的部分丢弃掉不做绘制,这样不仅能解决上面的bug,还能将渲染性能提高50%左右。代码也很简单,在 RenderScene中加入如下的代码
if (iCull) {
glEnable(GL_CULL_FACE);//开启背面剔除
glCullFace(GL_BACK);//剃除背面,
}
else{
glDisable(GL_CULL_FACE);
}
甜甜圈3.png
深度测试
正背面剔除帮助我们解决了上面的问题,但是旋转过程中又出现了一个新的问题,如图显示,甜甜圈缺了一个口,这又是为什么呢
甜甜圈4.png
想要解决这个问题,我们得先了解3D图形中关于深度的相关概念:
深度
深度其实就是该像素点在3D世界中距离摄像机的距离,Z值。
深度缓冲区
深度缓存区,就是⼀块内存区域,专⻔存储着每个像素点(绘制在屏幕上的)深度值.深度值(Z值)越⼤,则离摄像机就越远。
为什么需要深度缓冲区
在不使⽤深度测试的时候,如果我们先绘制⼀个距离⽐较近的物理,再绘制距离较远的物理,则距离远的位图因为后绘制,会把距离近的物体覆盖掉. 有了深度缓冲区后,绘制物体的顺序就不那么要的. 实际上,只要存在深度缓冲区,OpenGL 都会把像素的深度值写⼊到缓冲区中. 除⾮调⽤glDepthMask(GL_FALSE).来禁⽌写⼊.
什么是深度测试
深度缓冲区(DepthBuffffer)和颜⾊缓存区(ColorBuffffer)是对应的.颜⾊缓存区存储像素的颜⾊信息,⽽深度缓冲区存储像素的深度信息. 在决定是否绘制⼀个物体表⾯时, ⾸先要将表⾯对应的像素的深度值与当前深度缓冲区中的值进⾏⽐较. 如果⼤于深度缓冲区中的值,则丢弃这部分.否则利⽤这个像素对应的深度值和颜⾊值.分别更新深度缓冲区和颜⾊缓存区. 这个过程称为”深度测试”。
通过概念不难发现,上面的问题其实是由于像素点的深度导致的,在不开启深度测试的情况下,就有可能出现像素点被覆盖的问题,导致类似甜甜圈出现缺口这样的问题,此时我们就需要将OpenGL深度测试功能打开。
代码依旧很简单
//开启深度测试
if (iDepth) {
glEnable(GL_DEPTH_TEST);
}
else{
glDisable(GL_DEPTH_TEST);
}
开启后,继续测试,发现问题已经解决。
甜甜圈5.png
网友评论