美文网首页
第五弹 OpenGL下多种图元组合实现&基本渲染流程

第五弹 OpenGL下多种图元组合实现&基本渲染流程

作者: Mr_Lxh | 来源:发表于2019-05-16 21:51 被阅读0次

    通过案例实现OpenGL下多种图元组合实现&基本渲染流程

    • 在前几篇文章中我们熟悉了OpenGL 的基本图形API 和渲染基本流程,这篇文章通过案例来回顾下之前所学的知识,以及各图元实现。由于现有知识无法完整实现相关案例 ,故此案例中涉及到一些还没讲的知识点。这些知识点会在以后文章中详细阐述,不做赘述。

    准备工作

    • 搭建环境,设置空工程(详情见第一弹 环境搭建 空工程见网盘链接)

    代码实现(mian.cpp实现)

    • 引入工具类
    #include "GLTools.h"    
    #include "GLMatrixStack.h"
    #include "GLFrame.h"
    #include "GLFrustum.h"
    #include "GLBatch.h"
    #include "GLGeometryTransform.h"
    
    #include <math.h>
    #ifdef __APPLE__
    #include <glut/glut.h>
    #else
    #define FREEGLUT_STATIC
    #include <GL/glut.h>
    #endif
    
    • GLTools.h头文件包含了大部分GLTool中类似C语言的独立函数
    • GLMatrixStack.h 矩阵的工具类 可以利于GLMatrixStack加载单元矩阵/矩阵/矩阵相乘/压栈/出栈/缩放/平移/旋转
    • GLFrame.h 矩阵工具类,表示位置。设置vOrigin、vForward、vUp、vDown
    • GLFrustum.h 矩阵工具类 用来快速设置正/透视投影矩阵。完成坐标从3D->2D映射过程
    • GLBatch.h 三角形批次类,帮助类。利用它可以传输顶点/光照/纹理/颜色数据到存储着色器中
    • GLGeometryTransform.h 变换管道类 用来快速在代码中传输视图矩阵/投影矩阵/视图投影变换矩阵等
      math.h 数学库 用于数学运算
      GL/glut.h 在Mac系统下 和在windows和Linux上区别:后者使用freeglut的静态库版本需要添加一个宏
    • 声明各种需要的类
    GLShaderManager     shaderManager;//存储着色器管理工具类
    GLMatrixStack       modelViewMatrix;//模型视图矩阵
    GLMatrixStack       projectionMatrix;//投影矩阵
    GLFrame             cameraFrame;//观察者视图坐标
    GLFrame             objectFrame;//设置图形环绕时,视图坐标
    //投影矩阵
    GLFrustum           viewFrustum;//设置图元绘制时的投影方式
    
    //容器类(7种不同的图元对应7种容器对象)
    
    GLBatch             pointBatch;//点
    GLBatch             lineBatch;//线段
    GLBatch             lineStripBatch;//线圈(不闭合)
    GLBatch             lineLoopBatch;//线圈(闭合)
    GLBatch             triangleBatch;//三角形
    GLBatch             triangleStripBatch;//三角形带
    GLBatch             triangleFanBatch;//扇形
    
    • 重要函数的实现

    a. main函数实现

    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("GL_POINTS");
        //注册回调函数(改变尺寸)
        glutReshapeFunc(ChangeSize);
        //点击空格时,调用的函数
        glutKeyboardFunc(KeyPressFunc);
        //特殊键位函数(上下左右)
        glutSpecialFunc(SpecialKeys);
        //显示函数
        glutDisplayFunc(RenderScene);
        
        //判断一下是否能初始化glew库,确保项目能正常使用OpenGL 框架
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        
        //绘制
        SetupRC();
        
        //runloop运行循环
        glutMainLoop();
        return 0;
    }
    

    main函数为整个程序主函数。贯穿整个生命周期,所有程序渲染相关函数均在此函数中调用

    b. changeSize函数实现

    • 创建视口 设置投影方式
    void ChangeSize(int w, int h)
    {
        glViewport(0, 0, w, h);
        //创建投影矩阵,并将它载入投影矩阵堆栈中
        viewFrustum.SetPerspective(35.0f, float(w) / float(h), 1.0f, 500.0f);
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        //调用顶部载入单元矩阵
        modelViewMatrix.LoadIdentity();
    }
    
    

    触发条件:
    1、新建窗口
    2、窗口尺寸发生改变
    处理业务:
    1、设置OpenGL 视口
    2、设置OpenGL投影方式等

    c. KeyPressFunc函数实现

    • 根据空格次数。切换不同的"窗口名称" 切换不同图元实现 重新渲染
    
    void KeyPressFunc(unsigned char key, int x, int y)
    {
        if(key == 32)
        {
            nStep++;
            
            if(nStep > 6)
                nStep = 0;
        }
        
        switch(nStep)
        {
            case 0:
                glutSetWindowTitle("GL_POINTS");
                break;
            case 1:
                glutSetWindowTitle("GL_LINES");
                break;
            case 2:
                glutSetWindowTitle("GL_LINE_STRIP");
                break;
            case 3:
                glutSetWindowTitle("GL_LINE_LOOP");
                break;
            case 4:
                glutSetWindowTitle("GL_TRIANGLES");
                break;
            case 5:
                glutSetWindowTitle("GL_TRIANGLE_STRIP");
                break;
            case 6:
                glutSetWindowTitle("GL_TRIANGLE_FAN");
                break;
        }
        
        glutPostRedisplay();
    }
    
    

    触发条件:
    1、用户敲击“空格”键位
    处理业务:
    1、判断用户输入的是“空格”
    2、设置窗口名称
    3、手动触发重新渲染

    d.SpecialKeys函数实现

    • 点击特殊键位触发
    void SpecialKeys(int key, int x, int y)
    {
        
        if(key == GLUT_KEY_UP)
            //围绕一个指定的X,Y,Z轴旋转。
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
        
        if(key == GLUT_KEY_DOWN)
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
        
        if(key == GLUT_KEY_LEFT)
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
        
        if(key == GLUT_KEY_RIGHT)
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
        
        glutPostRedisplay();
    }
    

    触发条件:
    1、用户使用“上/下/左/右”键位
    处理业务:
    1、计算出围绕X/Y坐标轴上下左右旋转的视觉坐标系

    e.RenderScene函数实现

    • 召唤场景
    void RenderScene(void)
    {
        // Clear the window with current clearing color
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
       
        //压栈
        modelViewMatrix.PushMatrix();
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        
        //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
        modelViewMatrix.MultMatrix(mCamera);
        
        M3DMatrix44f mObjectFrame;
        //只要使用 GetMatrix 函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载。用来使用GLShaderManager 的使用。或者是获取顶部矩阵的顶点副本数据
        objectFrame.GetMatrix(mObjectFrame);
        
        //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果随后简存储在堆栈的顶部
        modelViewMatrix.MultMatrix(mObjectFrame);
        
        /* GLShaderManager 中的Uniform 值——平面着色器
         参数1:平面着色器
         参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
         --transformPipeline.GetModelViewProjectionMatrix() 获取的
         GetMatrix函数就可以获得矩阵堆栈顶部的值
         参数3:颜色值(黑色)
         */
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
        
        switch(nStep) {
            case 0:
                //设置点的大小
                glPointSize(4.0f);
                pointBatch.Draw();
                glPointSize(1.0f);
                break;
            case 1:
                //设置线的宽度
                glLineWidth(2.0f);
                lineBatch.Draw();
                glLineWidth(1.0f);
                break;
            case 2:
                glLineWidth(2.0f);
                lineStripBatch.Draw();
                glLineWidth(1.0f);
                break;
            case 3:
                glLineWidth(2.0f);
                lineLoopBatch.Draw();
                glLineWidth(1.0f);
                break;
            case 4:
                DrawWireFramedBatch(&triangleBatch);
                break;
            case 5:
                DrawWireFramedBatch(&triangleStripBatch);
                break;
            case 6:
                DrawWireFramedBatch(&triangleFanBatch);
                break;
        }
        
        //还原到以前的模型视图矩阵(单位矩阵)
        modelViewMatrix.PopMatrix();
        
        // 进行缓冲区交换
        glutSwapBuffers();
    }
    

    f.SetupRC函数实现

    • 此函数在呈现上下文中进行必要的初始化。这是第一次做任何与openGL 相关的任务
    void SetupRC()
    {
        // 灰色的背景
        glClearColor(0.7f, 0.7f, 0.7f, 1.0f );
        shaderManager.InitializeStockShaders();
        glEnable(GL_DEPTH_TEST);
        //设置变换管线以使用两个矩阵堆栈
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
        cameraFrame.MoveForward(-15.0f);
        
        /*
         常见函数:
         void GLBatch::Begin(GLenum primitive,GLuint nVerts,GLuint nTextureUnits = 0);
          参数1:表示使用的图元
          参数2:顶点数
          参数3:纹理坐标(可选)
         
         //负责顶点坐标
         void GLBatch::CopyVertexData3f(GLFloat *vNorms);
         
         //结束,表示已经完成数据复制工作
         void GLBatch::End(void);
         
         
         */
        //定义一些点,三角形形状。
       
        GLfloat vCoast[9] = {
            3,3,0,0,3,0,3,0,0
            
        };
        
        //用点的形式
        pointBatch.Begin(GL_POINTS, 3);
        pointBatch.CopyVertexData3f(vCoast);
        pointBatch.End();
        
        //通过线的形式
        lineBatch.Begin(GL_LINES, 3);
        lineBatch.CopyVertexData3f(vCoast);
        lineBatch.End();
        
        //通过线段的形式
        lineStripBatch.Begin(GL_LINE_STRIP, 3);
        lineStripBatch.CopyVertexData3f(vCoast);
        lineStripBatch.End();
        
        //通过线环的形式
        lineLoopBatch.Begin(GL_LINE_LOOP, 3);
        lineLoopBatch.CopyVertexData3f(vCoast);
        lineLoopBatch.End();
        
        //通过三角形创建金字塔
        GLfloat vPyramid[12][3] = {
            -2.0f, 0.0f, -2.0f,
            2.0f, 0.0f, -2.0f,
            0.0f, 4.0f, 0.0f,
            
            2.0f, 0.0f, -2.0f,
            2.0f, 0.0f, 2.0f,
            0.0f, 4.0f, 0.0f,
            
            2.0f, 0.0f, 2.0f,
            -2.0f, 0.0f, 2.0f,
            0.0f, 4.0f, 0.0f,
            
            -2.0f, 0.0f, 2.0f,
            -2.0f, 0.0f, -2.0f,
            0.0f, 4.0f, 0.0f};
        
        
        //GL_TRIANGLES 每3个顶点定义一个新的三角形
        triangleBatch.Begin(GL_TRIANGLES, 12);
        triangleBatch.CopyVertexData3f(vPyramid);
        triangleBatch.End();
        
        
        // 三角形扇形--六边形
        GLfloat vPoints[100][3];    
        int nVerts = 0;
        //半径
        GLfloat r = 3.0f;
        //原点(x,y,z) = (0,0,0);
        vPoints[nVerts][0] = 0.0f;
        vPoints[nVerts][1] = 0.0f;
        vPoints[nVerts][2] = 0.0f;
        
        //M3D_2PI 就是2Pi 的意思,就一个圆的意思。 绘制圆形
        for(GLfloat angle = 0; angle < M3D_2PI; angle += M3D_2PI / 6.0f) {
            
            //数组下标自增(每自增1次就表示一个顶点)
            nVerts++;
            /*
             弧长=半径*角度,这里的角度是弧度制,不是平时的角度制
             既然知道了cos值,那么角度=arccos,求一个反三角函数就行了
             */
            //x点坐标 cos(angle) * 半径
            vPoints[nVerts][0] = float(cos(angle)) * r;
            //y点坐标 sin(angle) * 半径
            vPoints[nVerts][1] = float(sin(angle)) * r;
            //z点的坐标
            vPoints[nVerts][2] = -0.5f;
        }
        
        // 结束扇形 前面一共绘制7个顶点(包括圆心)
        //添加闭合的终点
        //课程添加演示:屏蔽177-180行代码,并把绘制节点改为7.则三角形扇形是无法闭合的。
        nVerts++;
        vPoints[nVerts][0] = r;
        vPoints[nVerts][1] = 0;
        vPoints[nVerts][2] = 0.0f;
        
        // 加载!
        //GL_TRIANGLE_FAN 以一个圆心为中心呈扇形排列,共用相邻顶点的一组三角形
        triangleFanBatch.Begin(GL_TRIANGLE_FAN, 8);
        triangleFanBatch.CopyVertexData3f(vPoints);
        triangleFanBatch.End();
        
        //三角形条带,一个小环或圆柱段
        //顶点下标
        int iCounter = 0;
        //半径
        GLfloat radius = 3.0f;
        //从0度~360度,以0.3弧度为步长
        for(GLfloat angle = 0.0f; angle <= (2.0f*M3D_PI); angle += 0.3f)
        {
            //或许圆形的顶点的X,Y
            GLfloat x = radius * sin(angle);
            GLfloat y = radius * cos(angle);
            
            //绘制2个三角形(他们的x,y顶点一样,只是z点不一样)
            vPoints[iCounter][0] = x;
            vPoints[iCounter][1] = y;
            vPoints[iCounter][2] = -0.5;
            iCounter++;
            
            vPoints[iCounter][0] = x;
            vPoints[iCounter][1] = y;
            vPoints[iCounter][2] = 0.5;
            iCounter++;
        }
        
        // 关闭循环
        printf("三角形带的顶点数:%d\n",iCounter);
        //结束循环,在循环位置生成2个三角形
        vPoints[iCounter][0] = vPoints[0][0];
        vPoints[iCounter][1] = vPoints[0][1];
        vPoints[iCounter][2] = -0.5;
        iCounter++;
        
        vPoints[iCounter][0] = vPoints[1][0];
        vPoints[iCounter][1] = vPoints[1][1];
        vPoints[iCounter][2] = 0.5;
        iCounter++;
        
        // GL_TRIANGLE_STRIP 共用一个条带(strip)上的顶点的一组三角形
        triangleStripBatch.Begin(GL_TRIANGLE_STRIP, iCounter);
        triangleStripBatch.CopyVertexData3f(vPoints);
        triangleStripBatch.End();
    }
    
    

    触发条件:
    1、main函数触发
    处理业务:
    1、设置窗口背景颜色
    2、初始化存储着色器shaderManager
    3、设置图形顶点数据
    4、利用GLBatch 三角形批次类,将数据传递到着色器

    g.DrawWireFramedBatch函数实现

    • 为图形之间增加间隔,防止Z-Fighting 开启深度测试 深度偏移 过滤点线锯齿 启用颜色混合
    void DrawWireFramedBatch(GLBatch* pBatch)
    {
        /*------------画绿色部分----------------*/
        /* GLShaderManager 中的Uniform 值——平面着色器
         参数1:平面着色器
         参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
              --transformPipeline 变换管线(指定了2个矩阵堆栈)
         参数3:颜色值
        */
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
        pBatch->Draw();
        
        /*-----------边框部分-------------------*/
        /*
            glEnable(GLenum mode); 用于启用各种功能。功能由参数决定
            参数列表:http://blog.csdn.net/augusdi/article/details/23747081
            注意:glEnable() 不能写在glBegin() 和 glEnd()中间
            GL_POLYGON_OFFSET_LINE  根据函数glPolygonOffset的设置,启用线的深度偏移
            GL_LINE_SMOOTH          执行后,过虑线点的锯齿
            GL_BLEND                启用颜色混合。例如实现半透明效果
            GL_DEPTH_TEST           启用深度测试 根据坐标的远近自动隐藏被遮住的图形(材料
         
         
            glDisable(GLenum mode); 用于关闭指定的功能 功能由参数决定
         
         */
        
        //画黑色边框
        glPolygonOffset(-1.0f, -1.0f);// 偏移深度,在同一位置要绘制填充和边线,会产生z冲突,所以要偏移
        glEnable(GL_POLYGON_OFFSET_LINE);
        
        // 画反锯齿,让黑边好看些
        glEnable(GL_LINE_SMOOTH);
        glEnable(GL_BLEND);
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        
        //绘制线框几何黑色版 三种模式,实心,边框,点,可以作用在正面,背面,或者两面
        //通过调用glPolygonMode将多边形正面或者背面设为线框模式,实现线框渲染
        glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
        //设置线条宽度
        glLineWidth(2.5f);
        
        /* GLShaderManager 中的Uniform 值——平面着色器
         参数1:平面着色器
         参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
             --transformPipeline.GetModelViewProjectionMatrix() 获取的
              GetMatrix函数就可以获得矩阵堆栈顶部的值
         参数3:颜色值(黑色)
         */
        
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
        pBatch->Draw();
    
        // 复原原本的设置
        ////通过调用glPolygonMode将多边形正面或者背面设为全部填充模式
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glDisable(GL_POLYGON_OFFSET_LINE);
        glLineWidth(1.0f);
        glDisable(GL_BLEND);
        glDisable(GL_LINE_SMOOTH);
        
        
    }
    
    

    触发条件:
    1、系统自动触发
    2、开发者手动调用函数触发
    处理业务:
    1、为图形之间增加间隔,防止Z-Fighting

    相关资源:
    链接:https://pan.baidu.com/s/1gQP82YWwEubl_-VphLBn7g 密码:62xi

    相关文章

      网友评论

          本文标题:第五弹 OpenGL下多种图元组合实现&基本渲染流程

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