OpenGL & Metal

正背面剔除、深度测试

2020-07-13  本文已影响0人  黑眼豆豆_

关于正背面剔除和深度测试的理解,我们用一个案例来解释。

我们通过OpenGL绘制一个甜甜圈。如下图

甜甜圈.jpg
如果同时通过点击上下左右按钮让它进行旋转,如果想知道怎么绘制,请移步我之前的简书(传送门 用OpenGL绘制用OpenGL绘制金字塔、圆环、扇形)。
但是有不同的几点。
//
    //参数1:GLTriangleBatch 容器帮助类
    //参数2:外边缘半径
    //参数3:内边缘半径
    //参数4、5:主半径和从半径的细分单元数量
gltMakeTorus(torusBatch, 1.0f, 0.3f, 52, 26);

这是OpenGL为我们提供的官方的方法进行甜甜圈的绘制,注意:参数4和参数5的比例是2:1。torusBatch相当于一个指针,相当于将参数放入torusBatch进行调用。

//键位设置,通过不同的键位对其进行设置
//控制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();
}

和之前的案例不同,我们这次将物体(ObjectFrame)固定住,让观察者进行移动,
那么在void RenderScene()中只需要将viewFrame压入模型视图矩阵(modelViewMatix)中即可。

//2.把摄像机矩阵压入模型矩阵中
modelViewMatix.PushMatrix(viewFrame);
    //使用默认光源着色器
    //通过光源、阴影效果跟提现立体效果
    //参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
    //参数2:模型视图矩阵
    //参数3:投影矩阵
    //参数4:基本颜色值
    shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vRed);

一个甜甜圈就绘制好了。接下来,我们旋转甜甜圈。


旋转甜甜圈.gif

我们可以看到天天圈有的地方变成了黑色的,有的地方是红色的。那么是为什么呢?

正背面剔除

原因

在渲染一个立体图形时,我们需要决定哪些面应该管观察者看到,而哪些面不应该被观察者看到,如果不需要被观察者看到,应该直接丢弃,不应该绘制,这种情况叫做隐藏面消除。那么在上面的例子中,就是因为加载了我们不应该看到的面,所以才导致混乱。

画家算法

于是,我们使用正背面剔除

正背面剔除原理

魔方.jpg
在我们观察一个正方体如魔方的时候,最多可以可以看到3个面,那我们看不到面怎么处理呢?那就直接丢弃掉,此时还能提高性能

正背面的区分

正背面剔除用法

//打开正背面剔除
glEnable(GL_CULL_FACE);
//关闭正背面剔除
glDisable(GL_CULL_FACE);

注意,打开后记得关闭,否则会对全局产生影响。

//设置正背面
glFrontFace(GL_CCW);

GL_CCW是一个GLenum类型的枚举值。我们默认选择GL_CCW。

参数 含义
GL_CCW 顶点顺序为逆时针方向的表面为正面
GL_CW 顶点顺序为顺时针方向的表面为正面
//剔除背面
glCullFace(GL_BACK);

GL_BACK是一个GLenum类型的枚举值。我们默认选择GL_BACK。

参数 含义
GL_FRONT 正面
GL_BACK 背面
GL_FRONT_AND_BACK 正面和背面

实现

//开启正背面剔除
glEnable(GL_CULL_FACE);
glFrontFace(GL_CCW);
glCullFace(GL_BACK);

加上这3行代码后就可以开启正背面剔除了,当然,下面2行可以不用设置,因为OpenGL默认设置GL_CCW和GL_BACK。

再看效果,诶,还有问题,好像被啃了一口?我们再看原因


选择甜甜圈.gif

深度测试

混乱原因

结合图来看,

image.png
当我们选择到这个位置的时候,因为有2个面(正面1和正面2)对着观察者,所以就会出现2个面都是正面的情况,那么接下来再转一下,因为2个面都是正面,所以导致OpenGL因为不知道具体该显示哪个面而出现混乱,出现类似于被啃了一口的样子。

深度测试原理

深度测试用法

//开启深度测试
glEnable(GL_DEPTH_TEST);
//关闭深度测试
glEnable(GL_DEPTH_TEST);
//指定深度测试判断模式
void glDepthFunc(GLEnum mode);
参数 含义
GL_ALWAYS 总是通过测试
GL_NEVER 总是不通过测试
GL_LESS 当前深度值<存储的深度值时通过
GL_EQUAL 当前深度值=存储的深度值时通过
GL_LEQUAL 当前深度值<=存储的深度值时通过
GL_GREATER 当前深度值>存储的深度值时通过
GL_NOTEQUAL 当前深度值!=存储的深度值时通过
GL_GEQUAL 当前深度值>=存储的深度值时通过

深度测试风险--Z-Fighting(Z 冲突)

Z-Fighting(Z 冲突)解决方案

//启用多边形偏移
glEnable(GL_POLYGON_OFFSET_FILL);
参数 含义
GL_POLYGON_OFFSET_POINT 填充方法是点填充
GL_POLYGON_OFFSET_LINE 填充方法是线填充
GL_POLYGON_OFFSET_FILL 填充方法是面填充

Z-Fighting(Z 冲突)预防

上一篇 下一篇

猜你喜欢

热点阅读