美文网首页
OpenGL之正背面剔除解析

OpenGL之正背面剔除解析

作者: K哥的贼船 | 来源:发表于2020-07-13 00:43 被阅读0次

我们先看一个例子。先画出OpenGL提供的系统模型——甜甜圈。
绘制过程如下:

1.先定义需要用到的变量

//观察者
GLFrame             viewFrame;
//使用视景体类GLFrustum来设置透视投影
GLFrustum           viewFrustum;
//甜甜圈批次类(容器)
GLTriangleBatch     torusBatch;
//模型视图矩阵堆栈
GLMatrixStack       modelViewMatix;
//投影矩阵堆栈
GLMatrixStack       projectionMatrix;
//几何变换管线
GLGeometryTransform transformPipeline;
//着色器管理类
GLShaderManager     shaderManager;

//标记:背面剔除、深度测试
int iCull = 0;
int iDepth = 0;

2.在main函数中配置渲染环境,开启全局Loop监听屏幕重绘,窗口的改变,特殊键位响应

int main(int argc, char* argv[])
{
    gltSetWorkingDirectory(argv[0]);
    
    glutInit(&argc, argv);
    glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
    glutInitWindowSize(800, 600);
    glutCreateWindow("Geometry Test Program");
    glutReshapeFunc(ChangeSize);
    glutSpecialFunc(SpecialKeys);
    glutDisplayFunc(RenderScene);
    
    GLenum err = glewInit();
    if (GLEW_OK != err) {
        fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
        return 1;
    }
    
    SetupRC();
    
    glutMainLoop();
    return 0;
}

3.在setupRC中,主要设置背景色、初始化着色器管理器、调整观察者与物体距离、创建甜甜圈

void SetupRC()
{
    glClearColor(0.3f, 0.3f, 0.3f, 1.0f );
    
    shaderManager.InitializeStockShaders();
    //往远离屏幕的方向移动7单位,观察者看到的物体会变小
    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);
    
    //5.点的大小(方便点填充时,肉眼观察)
    glPointSize(4.0f);
}

4.在changeSize方法中,去调整视口尺寸,使用GLFrustum类来初始化透视矩阵,并把透视矩阵加载到透视矩阵对阵中,初始化渲染管线,传入模型视图矩阵和投影矩阵。

//窗口改变
void ChangeSize(int w, int h)
{
    //1.防止h变为0
    if(h == 0)
        h = 1;

    glViewport(0, 0, w, h);
    
    //3.第一个参数是一个从顶点方向看去的视场角度(用角度值表示)
    viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
   
    projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
    
    transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
}

5.在RenderScene中

//渲染场景
void RenderScene()
{
    //1.清除窗口和深度缓冲区,不清除会导致背景色和物体颜色不可控,残留之前的数据。
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
    
    //2.把摄像机矩阵压入模型矩阵中,保存初始状态,后面绘制显示完成了要恢复,撤回
    modelViewMatix.PushMatrix(viewFrame);
    
    //3.设置绘图颜色
    GLfloat vRed[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    
    //4.
    //使用平面着色器
    //参数1:平面着色器
    //参数2:模型视图投影矩阵
    //参数3:颜色
   // shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vRed);
    
    //默认光源着色器,通过光源、阴影效果可以更提现3D立体效果
    //参数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();
}

6.在main函数中注册了SpecialKeys的回调,可以通过不同的键位调整观察者位置,从不同视角观察物体。(也可以观察者不动,调整物体的位置,达到同样效果)

void SpecialKeys(int key, int x, int y)
{
    //1.判断方向,上下左右方向键
    if(key == GLUT_KEY_UP)

        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();
}

跑起来可以看到一个甜甜圈就有了。但是发现我们转动甜甜圈的时候,有些部分显示有问题。


问题出现的原因是什么呢?
是首先因为我们用了默认光源着色器,有光的地方就会有阳面和阴面,而我们看到正常的部分就是阳面,出现问题的就是阴面了。而经过我们渲染我们看到了不该看到的面,而OpenGL不知道我们旋转的时候该显示哪一面,所以本身不需要绘制的背光面让我们看到了。但是如果用平面着色器,并不能看到阴面,显示没什么问题,但是根本原因是因为没有光源去区分阳面和阴面,所以我们肉眼看不出来阴面,并不是就不存在阴面。

解决方法

1.油画算法。

油画算法是由远及近的绘制物体,是有个前提条件的,需要层级之间分明,知道层级的远近,近的物体才能把远的物体挡住。而且油画算法在任何发生重叠的地方,虽然底下一层被挡住的部分并不需要显示,但是还是会分别绘制这两个图层,也就是对重叠的像素进行两次写操作,速度会变慢,效率低耗费多。而如果是几个三角形彼此交错的情形,没有孰远孰近,就无法处理了。所以不推荐。

2.采用正背面剔除(隐藏面消除)

这是OpenGL的一种处理3D图形的技巧。这种方法告诉OpenGL只需要绘制观察者看到的面,看不到的就不用绘制出来了。因此它很高效,在渲染的图元装配阶段就丢弃了一些三角形了,节约了片元着色器的性能。一个3D图形,你最多只能看到它的3面,而对于一个立方体来说,如果只需要绘制3面,那相当于提高了50%的效率,何乐而不为呢?
但是使用之前我们得先知道哪些面是需要绘制的,哪些可以不绘制。
OpenGL默认规定了围绕三角形顶点画线的逆时针方向绘制的三角形是正面,当然也提供了方法可以让你改为顺时针为正面,glFrontFace(GLenum mode),其中mode有两种:GL_CW(顺时针),GL_CCW(逆时针),默认为GL_CCW,但是一般不建议修改,因为修改正背面设置后会影响到整个项目的环境。
使用的方法是

//开启/关闭正背面剔除功能
    if (iCull) {
        glEnable(GL_CULL_FACE);
       //设置逆时针为正面,可以不用写,默认就是
        glFrontFace(GL_CCW);
       //设置剔除面为背面,可以不用写,默认就是背面
        glCullFace(GL_BACK);
    }else
    {
        glDisable(GL_CULL_FACE);
    }

我们可以通过新建菜单栏,手动触发开启和关闭正背面剔除的功能。

//添加右击菜单栏,在ProcessMenu方法中去改变我们一开始定义的iCull变量,根据iCull的0或者非0值,执行`glutPostRedisplay();`去重新渲染。
    // Create the Menu
    glutCreateMenu(ProcessMenu);
    glutAddMenuEntry("Toggle depth test",1);
    glutAddMenuEntry("Toggle cull backface",2);
    glutAddMenuEntry("Set Fill Mode", 3);
    glutAddMenuEntry("Set Line Mode", 4);
    glutAddMenuEntry("Set Point Mode", 5);
    glutAttachMenu(GLUT_RIGHT_BUTTON);

当我们右键菜单点击2的时候,就会提交重新渲染,触发RanderScene方法执行我们的正背面剔除。


RenderScene正背面剔除

run起来之后效果如下:


但是可以看到最后一帧有一块被吃了,显然还有待优化,这就涉及到深度缓冲区和深度测试了,欲知后事如何,且看下节分析OpenGL之深度测试

相关文章

  • OpenGL之正背面剔除解析

    我们先看一个例子。先画出OpenGL提供的系统模型——甜甜圈。绘制过程如下: 1.先定义需要用到的变量 2.在ma...

  • OpenGL深度测试

    在上一篇OpenGL正背面剔除中提到了正背面剔除的弊端:如果前后两个点都是正面或是背面,这时OpenGL无法区分哪...

  • OpenGL正背面剔除

    一、在渲染过程中可能产生的问题 1. 原因 在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见的,或者哪些部...

  • OpenGL 正背面剔除

    在渲染过程中可能存在的问题 在绘制3D场景的时候,我们需要决定哪些部分是对观察者可见的,或者哪些部分是对观察者不可...

  • OpenGL: 正背面剔除

    案例分析 1.用平面着色器绘制一个环并旋转, 效果如下: 一切看似没有问题. 2.再用光源着色器绘制一个立体环并旋...

  • OpenGL正背面剔除

    1.在渲染过程中可能产生的问题 在绘制3D场景的时候,我们需要决定哪些部分是对观察者 可⻅见的,或者哪些部分是对观...

  • OpenGL 正背面剔除

    OpenGL 在绘制三维立体图形时, 如果不做处理, 就会产生一些不符合常理或者逻辑问题, 本次讲述正背面显示错...

  • OpenGL正背面剔除

    隐藏⾯面消除 Hidden surface elimination 在绘制3D场景的时候,我们需要决定哪些部分是对...

  • OpenGL 基础渲染(深度测试)

    OpenGL 基础渲染(正背面剔除) - 简书 在上文案例使用了正背面剔除后,出现了新的问题。如下图所示,那我们要...

  • OpenGL--- (四)OpenGL渲染技巧:正背面剔除

    一、什么是正背面剔除? 正背面剔除方案,是OpenGL中针对图形绘制的一种技巧,主要用于处理立体图形绘制时,只绘制...

网友评论

      本文标题:OpenGL之正背面剔除解析

      本文链接:https://www.haomeiwen.com/subject/uqnncktx.html