1. OpenGL中的正面、背面(隐藏面)
- 在上图右侧三角形中,线条是从 V0 ->V1 -> V2 -> V0 沿着顶点顺时针方向形成⼀个闭合三角形。 这种顺序与方向结合来指定顶点的⽅式称为环绕。
- 环绕方式由两种:一种是顺时针环绕,一种是逆时针环绕。
- 在 OpenGL 中默认具有逆时针⽅向环绕的多边形为正面。当我们从正面看屏幕上的这个图时,左侧三角形展示的是正面,右侧三角形展示的是背面。假设我们绕到这个图的背面去看时,顺时针和逆时针的方向就变了。 所以如555555t是否为正面,是由环绕方向及观察者方向共同决定的。
- 这种默认可以更改,更改代码如下。但是由于大家都习惯逆时针方向为正面,所以不建议更改。
glFrontFace(GL_CW);
GL_CW: 设置OpenGL 顺时针环绕的多边形为正面;
GL_CCW: 设置OpenGL 逆时针环绕的多边形为正面
2. 为什么要区分正面、背面
在图形渲染过程中,我们能看到的正面会渲染出颜色,背面黑色。区分了正背面后,可以将背面剔除,以某种方式丢弃这部分数据,不渲染背面,可以提高至少 50% 的渲染性能。
3. 隐藏面消除解决方案
3.1 油画算法
- 先渲染场景中离观察者较远的图层,再渲染较近的图层。
- 下图中有多个图层依次叠加在一起,遵循油画算法,先渲染红色图层、再绘制黄色图层、最后渲染灰色图层。这样叠加在一起,可以自然的将背面藏起来,让大家看不到。
3.1.1 油画算法存在的问题
- 这种方法是非常低效的,因为对于图层重叠的每个像素都要进行多次渲染。
- 如果图层之间,不是平行的一层一层的叠加,而是交叉叠放时, 没有明确的先后顺序,油画算法就不能解决问题了。如下图这样:
3.2 使用正面、背面剔除
//1.开启正背面剔除
glEnable(GL_CULL_FACE);
//2.关闭正背面剔除
glDisable(GL_CULL_FACE);
//3. 指定剔除面:mode 参数的可用值为GL_FRONT、GL_BACK、GL_FRONT_AND_BACK
glCullFace(GLenum mode);
3.2.1 代码实践
我们通过一个 3D 的甜甜圈案例,来看看正背面剔除的效果。
1. 首先画一个 3D 甜甜圈.
OpenGL有一个甜甜圈的批次类,可以直接供给我们使用。
// 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);
2. 通过上下左右键位,控制甜甜圈旋转
//键位设置,通过不同的键位对其进行设置
//控制Camera的移动,从而改变视口
void SpecialKeys(int key, int x, int y)
{
//1.判断方向
if(key == GLUT_KEY_UP)
//2.根据方向调整观察者位置
viewFrame.RotateWorld(m3dDegToRad(-5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_DOWN)
viewFrame.RotateWorld(m3dDegToRad(5.0), 1.0f, 0.0f, 0.0f);
if(key == GLUT_KEY_LEFT)
viewFrame.RotateWorld(m3dDegToRad(-5.0), 0.0f, 1.0f, 0.0f);
if(key == GLUT_KEY_RIGHT)
viewFrame.RotateWorld(m3dDegToRad(5.0), 0.0f, 1.0f, 0.0f);
//3.重新刷新
glutPostRedisplay();
}
3. 旋转后重绘甜甜圈
//渲染场景
void RenderScene()
{
//1.清除窗口和深度缓冲区
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
//2.把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
//3.设置绘图颜色
GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
//使用默认光源着色器
//通过光源、阴影效果跟提现立体效果
//参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
//参数2:模型视图矩阵
//参数3:投影矩阵
//参数4:基本颜色值
shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);
//5.绘制
torusBatch.Draw();
//6.出栈 绘制完成恢复
modelViewMatix.PopMatrix();
//7.交换缓存区
glutSwapBuffers();
}
4. 运行效果
- 我们绘制的是红色的甜甜圈,在OpenGL中,正面绘制的是我们设置的颜色,背面绘制的黑色。
- 最初展示时,我们看到的是正面红色,这是正确的。但是随着观察者旋转,展示出红黑相间的样子,最终转到背面时,展示全黑色。
- OpenGL 正面背面都绘制时,显示的效果就错乱了。
5. 对甜甜圈进行背面剔除
对甜甜圈进行背面剔除,背面看不到就不绘制。
// 开启背面剔除
glEnable(GL_CULL_FACE);
//5.绘制
torusBatch.Draw();
// 关闭背面剔除
glDisable(GL_CULL_FACE);
不过看起来甜甜圈还有些问题,旋转到侧面时,看起来在上半部分或者下半部分缺少了一个块。为什么呢?
- 这是因为整个甜甜圈,我们能看到的都是正面。如下图中的ABCD四个区域,都是正面。当甜甜圈旋转到侧面对着我们时,AB区域就重合了。但是这两个都是正面,哪个该显示,哪个不该显呢?
- 我们直观的看上去,知道应该显示的A区,因为A区离我们更近,所以在重叠时,A区应该挡住B区。
- 但是OpenGL它不知道哪个离我们近呀。应该怎么让它知道呢?这个时候,我们就需要借助深度测试了。下篇文章中讲解