美文网首页
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之正背面剔除解析

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