美文网首页OpenGL
07-正背面剔除&深度测试-绘制甜甜圈

07-正背面剔除&深度测试-绘制甜甜圈

作者: SPIREJ | 来源:发表于2019-05-22 15:26 被阅读0次

    第一步:搭架子此处代码有详细的注解,不再画图分析

    //包含了大部分GLTools中类似C语言的独立函数
    #include "GLTools.h"
    //矩阵工具类 加载单元矩阵/矩阵/矩阵相乘/压栈/出栈/平移/缩放/旋转
    #include "GLMatrixStack.h"
    //矩阵工具类,表示位置 通过设置vOrigin/vForward/vUp
    #include "GLFrame.h"
    //矩阵工具类,用来快速设置正/透视投影矩阵,完成坐标从3D->2D映射过程
    #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()
    {
       
    }
    
    void SetupRC()
    {
        
    }
    
    //键位设置,通过不同的键位对其进行设置
    //控制Camera的移动,从而改变视口
    void SpecialKeys(int key, int x, int y)
    {
        
    }
    
    //窗口改变
    void ChangeSize(int w, int h)
    {
      
    }
    
    
    int main(int argc, char* argv[])
    {
        gltSetWorkingDirectory(argv[0]);
        
        glutInit(&argc, argv);
        //申请一个颜色缓存区、深度缓存区、双缓存区、模板缓存区
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        //初始化window的尺寸
        glutInitWindowSize(800, 600);
        //创建window的名称
        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();
        //开启runloop运行循环
        glutMainLoop();
        return 0;
    }
    

    第二步:完善必要的函数

    1、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);
    }
    

    2、特殊键位功能设置函数

    //控制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、在SetupRC()函数中创建一个甜甜圈

    //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, 50, 20);
        
        //5.点的大小(方便点填充时,肉眼观察)
        glPointSize(4.0f);
    

    2、在RenderScene()函数渲染

    //渲染场景
    void RenderScene()
    {
        //1.清除窗口 颜色缓冲区 和 深度缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //2.压栈 - 把摄像机矩阵压入模型矩阵中
        modelViewMatix.PushMatrix(viewFrame);
        
        //3.设置绘图颜色
        GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
        
        //4.1
        //使用平面着色器 --> 看不出问题
        //参数1:平面着色器
        //参数2:模型视图投影矩阵
        //参数3:颜色
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
        
        //5.绘制
        torusBatch.Draw();
        
        //6.出栈
        modelViewMatix.PopMatrix();
        
        //7.交换缓存区
        glutSwapBuffers();
    }
    
    使用平面着色器

    运行好像没什么问题,是因为使用的是平面着色器,没有光源,正背面都是一个色,所以看不出问题。下面的图是换成默认点光源着色器后的运行效果,就发现了问题

    使用默认光源着色器

        //4.2
        //使用默认光源着色器
        //通过光源、阴影效果跟提现立体效果
        //参数1:GLT_SHADER_DEFAULT_LIGHT 默认光源着色器
        //参数2:模型视图矩阵
        //参数3:投影矩阵
        //参数4:基本颜色值
        shaderManager.UseStockShader(GLT_SHADER_DEFAULT_LIGHT, transformPipeline.GetModelViewMatrix(), transformPipeline.GetProjectionMatrix(), vGreen);
    
    使用默认点光源着色器

    3、添加一个右键菜单栏,看看线填充,点填充各自效果

    3.1main()开启一个右键菜单栏

        glutCreateMenu(RightClickMenu);
        glutAddMenuEntry("开启/关闭背面消除", 1);
        glutAddMenuEntry("开启/关闭深度测试", 2);
        glutAddMenuEntry("面填充", 3);
        glutAddMenuEntry("线填充", 4);
        glutAddMenuEntry("点填充", 5);
        glutAttachMenu(GLUT_RIGHT_BUTTON);
    

    3.2 右键菜单栏函数功能开关实现RightClickMenu().

    void RightClickMenu(int key)
    {
        /*
         glPolygonMode 控制多边形的正面和背面的绘图模式
         
         GL_POINT表示显示顶点,多边形用点显示
         GL_LINE表示显示线段,多边形用轮廓显示
         GL_FILL表示显示面,多边形采用填充形式
         */
        switch (key) {
                case 1:
                
                break;
                
                case 2:
                
                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;
                
            default:
                break;
        }
        
        glutPostRedisplay();
    }
    
    绘图模式 - 面、线、点

    可以看到 不管使用面、线、还是点的绘图模式,渲染都会存在问题。解决这种问题,需要决定那部分是对观察者可见的,哪部分对观察者不可见,对不可见的部分,应该尽早丢弃(即看不见的部分不渲染,节省资源,提高效率)

    4. 用正背面剔除(Face Culling)解决渲染问题

    4.1 右键菜单控制开启/关闭正背面剔除功能

    iDepth = !iDepth;
    

    4.2RenderScene()glEnable, glDisable

        //开启/关闭正背面剔除功能
        if (iCull) {
            glEnable(GL_CULL_FACE);
        }else {
            glDisable(GL_CULL_FACE);
        }
    

    4.3 效果

    开启了正背面剔除但没开启深度测试

    5. 开启深度测试

    5.1 同开启正背面剔除逻辑一样

    //开启/关闭深度测试
        if (iDepth) {
            glEnable(GL_DEPTH_TEST);
        }else {
            glDisable(GL_DEPTH_TEST);
        }
    

    5.2效果

    开启正背面剔除和深度测试

    相关文章

      网友评论

        本文标题:07-正背面剔除&深度测试-绘制甜甜圈

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