美文网首页
004-OpenGL-正背面剔除

004-OpenGL-正背面剔除

作者: 沉默Coder | 来源:发表于2020-07-13 20:34 被阅读0次

    当看到文章标题的时候,你是否有一个疑问,什么是正背面剔除?对于没有OpenGL实践经验的同学来说,并不清楚什么是正背面剔除,先不要着急,我们先通过一个例子来引入什么是正背面剔除

    绘制一个甜甜圈(其实就是一个圆环)

    绘制过程涉及到的函数:

    Main

    int main(int argc,char *argv[])
    {
    
        //初始化GLUT库,这个函数只是传说命令参数并且初始化glut库
        glutInit(&argc, argv);
        
        /*
         初始化双缓冲窗口,其中标志GLUT_DOUBLE、GLUT_RGBA、GLUT_DEPTH、GLUT_STENCIL分别指
         双缓冲窗口、RGBA颜色模式、深度测试、模板缓冲区
         
         --GLUT_DOUBLE`:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;
         --GLUT_DEPTH`:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;
         --GLUT_STENCIL`:确保我们也会有一个可用的模板缓存区。
         深度、模板测试后面会细致讲到
         */
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
        
        //GLUT窗口大小、窗口标题
        glutInitWindowSize(800, 600);
        glutCreateWindow("Triangle");
        
        /*
         GLUT 内部运行一个本地消息循环,拦截适当的消息。然后调用我们不同时间注册的回调函数。我们一共注册2个回调函数:
         1)为窗口改变大小而设置的一个回调函数
         2)包含OpenGL 渲染的回调函数
         */
        //注册重塑函数
        glutReshapeFunc(changeSize);
        //注册显示函数
        glutDisplayFunc(RenderScene);
    
        /*
         初始化一个GLEW库,确保OpenGL API对程序完全可用。
         在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
         */
        GLenum status = glewInit();
        if (GLEW_OK != status) {
            
            printf("GLEW Error:%s\n",glewGetErrorString(status));
            return 1;
            
        }
        //设置我们的渲染环境
        setupRC();
        glutMainLoop();
        return  0;
    }
    

    main函数作为函数的入口,主要的功能有:

    • 初始化GLUT库,这个函数只是传说命令参数并且初始化glut库
    • 设置视口大小
    • 注册重塑函数
    • 注册显示函数
    • 初始化一个GLEW库,确保OpenGL API对程序完全可用。

    changeSize

    //窗口改变
    void ChangeSize(int w, int h)
    {
        //1.防止h变为0
        if(h == 0)
            h = 1;
        
        //2.设置视口窗口尺寸
        glViewport(0, 0, w, h);
        
        //3.setPerspective函数的参数是一个从顶点方向看去的视场角度(用角度值表示)
        // 设置透视模式,初始化其透视矩阵
        viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 100.0f);
        
        //4.把透视矩阵加载到透视矩阵对阵中
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        //5.初始化渲染管线
        transformPipeline.SetMatrixStacks(modelViewMatix, projectionMatrix);
    }
    

    ChangeSize函数在窗口大小改变时回调,这里的作用主要有:

    • 窗口大小改变时,重新设置视口的大小
    • 设置投影方式(透视投影)
    • 得到投影矩阵并压入到投影栈中
    • 初始化渲染管线并将投影矩阵,模型矩阵压入栈中

    SetupRC

    void SetupRC()
    {
        //1.设置背景颜色
        glClearColor(0.1f, 0.1f, 0.1f, 1.0f );
        
        //2.初始化着色器管理器
        shaderManager.InitializeStockShaders();
        
        //3.将相机向后移动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);
    }
    

    SetupRC函数作用:

    • 设置背景颜色
    • 初始化着色器管理器
    • 设置观察者位置
    • 创建一个甜甜圈
    • 设置点的大小(方便点填充时,肉眼观察)

    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);
        
        //使用默认光源着色器
        //通过光源、阴影效果跟提现立体效果
        //参数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();
    }
    

    RenderScene函数作用:

    • 清除缓冲区(如果不清除,缓冲区可能保留上一次的数据,造成绘制结果不准确)
    • 将模型视图矩阵压入栈中
    • 设置画笔颜色
    • 选用默认光源着色器进行绘制(也可以选用平面着色器)
    • 绘制
    • 绘制完成后将模型视图矩阵出栈
    • 交换缓冲区

    SpecialKeys

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

    SpecialKeys函数的作用:监听键盘的输入事件,当用户输入的时候此方法会回调,如果是方向键发生变化,则改变观察者的位置,从而达到视图旋转的目的

    绘制结果:


    甜甜圈

    一切看起来都很正常,但是当我们旋转一下视图的时候你就会发现结果是这样的:


    旋转后的甜甜圈

    为什么是这样的呢 ?

    这是因为我们设置的观察者的方向是在屏幕的正前方,而我们知道,当一个物体被光照射的时候,有正面和背面的区别,我们看到的正面就是红色部分,而旋转以后,我们就能看到背面,而背面没有光照,呈现黑色

    我们知道了原因,怎么去解决这个问题呢 ?这就是我们今天的主题,正背面剔除
    我们增加一些开关设置:
    在main函数中增加这样一段代码:

    // 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);
    

    增加一个函数:

    void ProcessMenu(int value)
    {
        switch(value)
        {
            case 1:
                iDepth = !iDepth;
                break;
                
            case 2:
                iCull = !iCull;
                break;
                
            case 3:
                glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
                break;
                
            case 4:
                glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
                break;
                
            case 5:
                glPolygonMode(GL_FRONT_AND_BACK, GL_POINT);
                break;
        }
        
        glutPostRedisplay();
    }
    

    当开关的值发生变化的时候,此函数会被回调
    然后修改RenderScene函数

    //渲染场景
    void RenderScene()
    {
        //1.清除窗口和深度缓冲区
        //可以给学员演示一下不清空颜色/深度缓冲区时.渲染会造成什么问题. 残留数据
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //开启/关闭正背面剔除功能
        if (iCull) {
            glEnable(GL_CULL_FACE);
            glFrontFace(GL_CCW);
            glCullFace(GL_BACK);
        }else
        {
            glDisable(GL_CULL_FACE);
        }
        
        //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);
        
        //使用默认光源着色器
        //通过光源、阴影效果跟提现立体效果
        //参数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();
    }
    

    当我们开启了正背面剔除以后,旋转视图以后,显示终于正常了:


    开启正背面剔除后

    但是又引入了一个新的问题,如下图,当我们旋转到一定角度的时候,圆环上有了一个缺口:


    圆环缺口

    这有引入了我们下一章要讲的内容,深度测试

    相关文章

      网友评论

          本文标题:004-OpenGL-正背面剔除

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