OpenGL

八、隐藏面消除解决方案

2020-07-09  本文已影响0人  小山93

1. OpenGL中的正面、背面(隐藏面)

左侧正面,右侧背面.png
glFrontFace(GL_CW);
GL_CW: 设置OpenGL 顺时针环绕的多边形为正面;
GL_CCW: 设置OpenGL 逆时针环绕的多边形为正面

2. 为什么要区分正面、背面

在图形渲染过程中,我们能看到的正面会渲染出颜色,背面黑色。区分了正背面后,可以将背面剔除,以某种方式丢弃这部分数据,不渲染背面,可以提高至少 50% 的渲染性能。

3. 隐藏面消除解决方案

3.1 油画算法

图层由远及近绘制.png
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);
  1. 通过上下左右键位,控制甜甜圈旋转
//键位设置,通过不同的键位对其进行设置
//控制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();
}
  1. 旋转后重绘甜甜圈

//渲染场景
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();
}
  1. 接下来,看下运行效果:
4.1 对甜甜圈进行背面剔除

对甜甜圈进行背面剔除,背面看不到就不绘制。

    // 开启背面剔除
    glEnable(GL_CULL_FACE);
    
    //5.绘制
    torusBatch.Draw();
    
    // 关闭背面剔除
    glDisable(GL_CULL_FACE);
背面剔除的甜甜圈.gif

不过看起来甜甜圈还有些问题,旋转到侧面时,看起来在上半部分或者下半部分缺少了一个块。为什么呢?

image.png

3.3 深度测试

3.3.1 深度

3.3.2 深度缓冲区

是一块内存区域,存储每一个像素点的深度值,一个像素只有一个深度。

3.3.3 深度测试

// 在绘制场景前,清除颜⾊缓存区,深度缓冲
glClearColor(0.0f,0.0f,0.0f,1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

// 开启深度测试
glEnable(GL_DEPTH_TEST);
// 关闭深度测试
glDisable(GL_DEPTH_TEST);

// 打开/关闭深度缓冲区写入 ,GL_FALSE: 关闭写入 GL_TRUE:打开写入
glDepthMask(GLBool value);

// 指定深度测试判断模式,默认GS_LESS,当前深度值 < 存储深度值时通过
glDepthFunc(GLEnum mode);
深度测试判断模式.png

3.3.4 甜甜圈开启深度测试

  // 开启深度测试
    glEnable(GL_DEPTH_TEST);
    
    //5.绘制
    torusBatch.Draw();
    
    // 关闭深度测试
    glDisable(GL_DEPTH_TEST);
开启深度测试后的甜甜圈.gif

现在甜甜圈的展示,已经非常好了!
其实开启了深度测试后,我们甚至不需要使用背面剔除功能。因为深度测试的原理是一个像素内只绘制离观察者最近的图形图像。甜甜圈的背面离观察者远,自然不会绘制。

3.3.5 深度测试存在 Z-Fighting / Z闪烁 的问题

如果AB两个深度值特别相近,已经超出了深度值的精度位数,就无法比较两个值的大小,会出现可能一会儿显示A的图像,一会儿显示B的图像,造成一种闪烁的现象,这个现象叫做Z-Fighting 或者 Z闪烁。

3.3.6 Z-Fighting / Z闪烁 解决方案

// 1. 开启多边形偏移
glEnable(GL_POLYGON_OFFSET_FILL)
// 2. 关闭多边形偏移
glDisable(GL_POLYGON_OFFSET_FILL)
参数列列表: 
GL_POLYGON_OFFSET_POINT   对应光栅化模式: GL_POINT
GL_POLYGON_OFFSET_LINE    对应光栅化模式: GL_LINE
GL_POLYGON_OFFSET_FILL    对应光栅化模式: GL_FILL

// 2. 设定偏移量(一般情况下,设置-1, -1)
glPolygonOffset(Glfloat factor,Glfloat units)

3.3.6 Z-Fighting / Z闪烁 预防方案

3.4 裁剪测试

//1 开启裁剪测试 
glEnable(GL_SCISSOR_TEST);
//2.关闭裁剪测试 
glDisable(GL_SCISSOR_TEST);
//3.指定裁剪窗⼝,  x,y:指定裁剪框左下角位置; width , height:指定裁剪尺⼨
void glScissor(Glint x,Glint y,GLSize width,GLSize height);

3.5 混合

// 启用颜色混合
glEnable(GL_BlEND);
// 关闭颜色混合
glDisable(GL_BlEND);

3.5.1 混合方程式

Cf = (Cs * S) + (Cd * D)
Cf :最终计算参数的颜⾊
Cs : 源颜⾊
Cd :⽬标颜色
S:源混合因⼦子
D:⽬标混合因⼦

// 选择混合方程式
void glbBlendEquation(GLenum mode);

// 设置混合因⼦, S:源混合因子 D:⽬标混合因⼦
void glBlendFunc(GLenum S,GLenum D); 
// 常见的混合因子
void glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);

// 设置混合因⼦及Alpha因⼦
// strRGB: 源颜⾊的混合因子 
// dstRGB: ⽬标颜色的混合因子 
// strAlpha: 源颜⾊的Alpha因子 
// dstAlpha: ⽬标颜色的Alpha因⼦
void glBlendFuncSeparate(GLenum strRGB,GLenum dstRGB ,GLenum strAlpha,GLenum dstAlpha);

// 设置常量混合颜色,默认黑色
void glBlendColor(GLclampf red ,GLclampf green ,GLclampf blue ,GLclampf alpha );
可选择的混合方程式.png 混合因子.png
上一篇下一篇

猜你喜欢

热点阅读