美文网首页
OpenGL 正背面剔除

OpenGL 正背面剔除

作者: AndyGF | 来源:发表于2020-07-12 15:07 被阅读0次

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

    问题场景, 问题产生原因, 解决方案及原理, 我们将通过绘制一个三维图形作为示例来讲解. 具体代码会放到最后.

    一. 绘制甜甜圈:

    • 甜甜圈初始化.png

    二. 旋转甜甜圈:

    将甜甜圈沿 Y 轴旋转一点. 就会出现如下图所示的效果.

    • 甜甜圈旋转.jpeg

    在此示例中, 使用的是默认的平行光, 不考虑现实中的光照散射 和 折射问题. 物体在光照下是有两面的, 正面(光照面) 和 背面(背光的面),上图中红色部分就是正面,黑色为背面,背面是不应该看到的. 但是此时我们并没有告诉 OpenGL 怎么处理这个情况, 所以 OpenGL 就会按照绘图顺序去绘制, 而这些黑色最后到缓存区的, 替换了本就显示的红色部分, 最终就导致我们看到了黑色.

    在 OpenGL 中三维图形的面是有正背面之分的, 下图是默认的正背面配置, 也中常用的.

    • OpenGL默认正背面.png

    正背面区分 - 默认设置
    正面: 按照逆时针顶点连接顺序的三角形⾯
    背面: 按照顺时针顶点连接顺序的三⻆形⾯

    注意: 正背面是可以改成与之相反的.

    那么这个问题怎么解决呢, 其实很简单, 那就是不绘制背面, 针对此类问题, OpenGL 给出了解决方案, 就是开启正背面剔除功能, 在绘制前, 会先检测哪些是背面, 把这些面从需要绘制的面中丢弃. 这样就看不到背面.
    开启/关闭正背面剔除功能代码如下:

      // 开启正背剔除面功能
      glEnable(GL_CULL_FACE);
      // 正面为逆时针(默认配置, 可以不写)
      glFrontFace(GL_CCW);
      // 剔除背面
      glCullFace(GL_BACK);
    
      // 关闭正背剔除面功能
      glDisable(GL_CULL_FACE);
    

    开启正背面剔除功能后的效果:

    • 甜甜圈开启正背面剔除.png

    甜甜圈Demo :

    //演示了OpenGL背面剔除,深度测试,和多边形模式
    #include "GLTools.h"    
    #include "GLMatrixStack.h"
    #include "GLFrame.h"
    #include "GLFrustum.h"
    #include "GLGeometryTransform.h"
    
    #include <math.h>
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    ////设置角色帧,作为相机
    GLFrame             viewFrame;
    //使用GLFrustum类来设置透视投影
    GLFrustum           viewFrustum;
    GLTriangleBatch     torusBatch;
    GLMatrixStack       modelViewMatix;
    GLMatrixStack       projectionMatrix;
    GLGeometryTransform transformPipeline;
    GLShaderManager     shaderManager;
    
    //标记:背面剔除、深度测试
    int iCull = 0;
    int iDepth = 0;
    
    //渲染场景
    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);
        }
        
        if (iDepth) {
            glEnable(GL_DEPTH_TEST);
        } else {
            glDisable(GL_DEPTH_TEST);
        }
        
        //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();
    }
    
    void SetupRC()
    {
        //1.设置背景颜色
        glClearColor(0.3f, 0.3f, 0.3f, 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);
    }
    
    //键位设置,通过不同的键位对其进行设置
    //控制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();
    }
    
    //窗口改变
    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);
    }
    
    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();
    }
    
    
    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);
        
        
        //添加右击菜单栏
        // 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);
        
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        
        SetupRC();
        
        glutMainLoop();
        return 0;
    }
    

    相关文章

      网友评论

          本文标题:OpenGL 正背面剔除

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