美文网首页
OpenGL-08-入门级案例3:绘制7种图元连接图形、几何图形

OpenGL-08-入门级案例3:绘制7种图元连接图形、几何图形

作者: 宇宙那么大丶 | 来源:发表于2020-07-22 00:06 被阅读0次

    根据之前介绍的7种图元连接方式:点、线、线段、线环、三角形、三角形带、三角形扇 进行绘制图形。还有OpenGL中现成的几何图形API调用。

    一、效果图

    001.gif

    二、流程图

    image.png

    三、源码

    
    //======导入头文件======
    #include "GLShaderManager.h"
    #include "GLTools.h"
    #include <GLUT/GLUT.h>
    
    //矩阵工具类。加载单元矩阵、矩阵、矩阵相乘、压栈、出栈、缩放、平移、旋转
    #include "GLMatrixStack.h"
    //矩阵工具类。表示位置。通过设置origin、forward、up
    #include "GLFrame.h"
    //矩阵工具类。快速设置正投影、透视投影。完成3D->2D映射过程
    #include "GLFrustum.h"
    //三角形批次类。利用它将顶点、光照、纹理、颜色等数据传入存储着色器
    #include "GLBatch.h"
    //变换管道类。用来快速在代码中传输视图矩阵、投影矩阵、视图投影变换矩阵等。
    #include "GLGeometryTransform.h"
    //数学库
    #include <math.h>
    //======导入头文件======
    
    //======声明全局变量======
    //存储着色器
    GLShaderManager shaderManager;
    //视图模型矩阵
    GLMatrixStack modelViewMatrix;
    //透视矩阵
    GLMatrixStack projectionMatrix;
    //观察者视图坐标
    GLFrame cameraFrame;
    //图形环绕时用到的视图坐标
    GLFrame objectFrame;
    //图元绘制时的投影方式
    GLFrustum viewFrustum;
    //容器类
    //点、线、线段、线环、三角形、三角形带、三角形扇
    GLBatch                pointBatch;
    GLBatch                lineBatch;
    GLBatch                lineStripBatch;
    GLBatch                lineLoopBatch;
    GLBatch                triangleBatch;
    GLBatch             triangleStripBatch;
    GLBatch             triangleFanBatch;
    //几何变换管道。存储 模型矩阵、投影矩阵、模型视图矩阵
    GLGeometryTransform transformPipeline;
    //定义个黑色
    GLfloat vBlack[] = {0.0f,0.0f,0.0f,1.0f};
    //绿色
    GLfloat vGreen[] = {0.0f,1.0f,0.0f,1.0f};
    //记录当前按空格的次数,用来切换图案
    int nStep = 0;
    
     
    
    //======声明全局变量======
    
    //填充颜色+画黑边
    void DrawWireFramedBatch(GLBatch* pBatch)
    {
        /*------------1、填充颜色-画绿色部分----------------*/
        /* GLShaderManager 中的Uniform 值——平面着色器
         参数1:平面着色器
         参数2:运行为几何图形变换指定一个 4 * 4变换矩阵
              --transformPipeline 变换管线(指定了2个矩阵堆栈)
         参数3:颜色值
        */
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
        pBatch->Draw();
        
        /*-----------2、画边框-黑色边框-------------------*/
     
        /*
         glEnable(GLenum mode); 用于启用各种指定功能。功能由参数决定
         glDisable(GLenum mode); 用于关闭指定功能 功能由参数决定
         参数列表:http://blog.csdn.net/augusdi/article/details/23747081
         注意:glEnable() 不能写在glBegin() 和 glEnd()中间
         */
        //----开启----
        //开启多边形偏移
        glEnable(GL_POLYGON_OFFSET_LINE);
        //设置偏移量
        glPolygonOffset(-1.0f, -1.0f);
        //开启反锯齿,让线变得更光滑好看
        glEnable(GL_LINE_SMOOTH);
        //开启颜色混合
        glEnable(GL_BLEND);
        //设置混合因子
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        //设置正面背面的填充模式
        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(GL_FRONT_AND_BACK, GL_FILL);
        glLineWidth(1.0f);
        //关闭偏移
        glDisable(GL_POLYGON_OFFSET_LINE);
        //关闭颜色混合
        glDisable(GL_BLEND);
        //关闭反锯齿
        glDisable(GL_LINE_SMOOTH);
         
        
        
    }
    
    //窗口变化时候的回调函数
    void changeSize(int w,int h)
    {
        //防止宽高比中除数为0的bug
        if(h==0) h=1;
        
        //1、创建窗口
        glViewport(0, 0, w, h);
        //2、创建投影矩阵->透视投影(角度、宽高比、最小距离、最大距离)
        viewFrustum.SetPerspective(35.0f, float(w)/float(h), 1.0f, 500.0f);
        //3、拿到这个投影矩阵,把它加载到我们声明的透视矩阵
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        //4、在我们的视图模型矩阵 顶部载入单元矩阵,相当于初始化
        modelViewMatrix.LoadIdentity();
        //5、设置变换管道
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
        
    }
    
    
    //所有必要的初始化操作在这里进行
    void setupRC()
    {
        //1、背景色
        glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
        //2、初始化着色器
        shaderManager.InitializeStockShaders();
        //3、开启深度测试
        glEnable(GL_DEPTH_TEST);
        //4、观察者的初始化位置
        cameraFrame.MoveForward(-15.0f);
        //5、设置顶点数据
        //6、提交批次类 ==注意没一个批次类对于一种图形
        //=====点====
        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
            
        };
        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();
        
    }
    
    //进行渲染
    void RenderScene(void)
    {
        //1、清楚缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT);
        //2、压栈 -> 为了记录最初状态,用来撤回 顶部是单元矩阵
        modelViewMatrix.PushMatrix();
        //3、初始化一个观察者矩阵
        M3DMatrix44f mCamreaMatrix;
        //把观察者矩阵放进去
        cameraFrame.GetCameraMatrix(mCamreaMatrix);
        //矩阵相乘放入顶部 : 单元矩阵(1) * mCamreaMatrix = New_mCamreaMatrix
        modelViewMatrix.MultMatrix(mCamreaMatrix);
        
        //4、初始化一个物体本身矩阵
        M3DMatrix44f mObjectMatrix;
        objectFrame.GetMatrix(mObjectMatrix);
        //New_mCamreaMatrix * mObjectMatrix = New_mObjectMatrix
        modelViewMatrix.MultMatrix(mObjectMatrix);
        
        //5、使用平面着色器存储栈顶的矩阵 (平面着色器,几何图形的变换矩阵,颜色)
        shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
        
        //6、设置属性,draw,还原属性
        switch (nStep) {
            case 0:
                //设置点大小
                glPointSize(5.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;
                
                
            default:
                break;
        }
        //7、出栈、进行撤回操作。
        modelViewMatrix.PopMatrix();
        
        //8、进行交换缓冲区
        glutSwapBuffers();
    }
    
    
    //空格键回调
    void KeyPressFunc(unsigned char key, int x, int y)
    {
        //这里32刚好对应空格键
        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();
    }
    //上下左右键回调
    void SpecialKeys(int key, int x, int y)
    {
        //上下=沿着X轴旋转   左右=沿着Y轴旋转
        if(key == GLUT_KEY_UP)
            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();
    }
    
    
    
    
    
    
    int main(int argc,char *argv[])
    {
        //1、设置工作目录、初始化
        gltSetWorkingDirectory(argv[0]);
        glutInit(&argc, argv);
        //2、初始化缓冲区 双缓冲、颜色、深度、模板缓冲区
        glutInitDisplayMode(GLUT_DOUBLE |GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
        //3、设置窗口大小、标题
        glutInitWindowSize(800, 600);
        glutCreateWindow("jpy");
        //========================
        //4、注册所需回调函数
        //窗口重塑回调
        glutReshapeFunc(changeSize);
        //渲染回调
        glutDisplayFunc(RenderScene);
        //空格回调
        glutKeyboardFunc(KeyPressFunc);
        //特殊键位回调
        glutSpecialFunc(SpecialKeys);
        //========================
        //5、固定操作。初始化GLEW库,做检测
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n",glewGetErrorString(err));
            return 1;
        }
        
        
        //进行初始化操作
        setupRC();
        //开始loop监听各种回调
        glutMainLoop();
        return 0;
    }
    
    

    四、拓展

    我们上面的代码使用的是自己计算各个顶点,传入三角形批次类中,然后进行绘制。那么如果我们进行一些复杂图形绘制,又要计算多少个顶点呢?那得多麻烦啊!!!
    GLTriangleBatch 这个类里面有一些已经封装好的API,我们可以直接调用!!!
    我们来看一下都有哪些:

    0621.gif
    //首先我们要声明一下
    
    //球
    GLTriangleBatch     sphereBatch;
    //环
    GLTriangleBatch     torusBatch;
    //圆柱
    GLTriangleBatch     cylinderBatch;
    //锥
    GLTriangleBatch     coneBatch;
    //磁盘
    GLTriangleBatch     diskBatch;
    

    在SetupRC()中进行初始化

    // 球
        /*
          gltMakeSphere(GLTriangleBatch& sphereBatch, GLfloat fRadius, GLint iSlices, GLint iStacks);
         参数1:sphereBatch,三角形批次类对象
         参数2:fRadius,球体半径
         参数3:iSlices,从球体底部堆叠到顶部的三角形带的数量;其实球体是一圈一圈三角形带组成
         参数4:iStacks,围绕球体一圈排列的三角形对数
         
         建议:一个对称性较好的球体的片段数量是堆叠数量的2倍,就是iStacks = 2 * iSlices;
         绘制球体都是围绕Z轴,这样+z就是球体的顶点,-z就是球体的底部。
         */
        gltMakeSphere(sphereBatch, 3.0, 10, 20);
        
    
        // 环面
        /*
         gltMakeTorus(GLTriangleBatch& torusBatch, GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor);
         参数1:torusBatch,三角形批次类对象
         参数2:majorRadius,甜甜圈中心到外边缘的半径
         参数3:minorRadius,甜甜圈中心到内边缘的半径
         参数4:numMajor,沿着主半径的三角形数量
         参数5:numMinor,沿着内部较小半径的三角形数量
         */
        gltMakeTorus(torusBatch, 3.0f, 0.75f, 15, 15);
    
        // 圆柱
        /*
         void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
         参数1:cylinderBatch,三角形批次类对象
         参数2:baseRadius,底部半径
         参数3:topRadius,头部半径
         参数4:fLength,圆形长度
         参数5:numSlices,围绕Z轴的三角形对的数量
         参数6:numStacks,圆柱底部堆叠到顶部圆环的三角形数量
         */
        gltMakeCylinder(cylinderBatch, 2.0f, 2.0f, 3.0f, 15, 2);
        
    
        //锥
        /*
         void gltMakeCylinder(GLTriangleBatch& cylinderBatch, GLfloat baseRadius, GLfloat topRadius, GLfloat fLength, GLint numSlices, GLint numStacks);
         参数1:cylinderBatch,三角形批次类对象
         参数2:baseRadius,底部半径
         参数3:topRadius,头部半径
         参数4:fLength,圆形长度
         参数5:numSlices,围绕Z轴的三角形对的数量
         参数6:numStacks,圆柱底部堆叠到顶部圆环的三角形数量
         */
        //圆柱体,从0开始向Z轴正方向延伸。
        //圆锥体,是一端的半径为0,另一端半径可指定。
        gltMakeCylinder(coneBatch, 2.0f, 0.0f, 3.0f, 13, 2);
        
    
        // 磁盘
        /*
        void gltMakeDisk(GLTriangleBatch& diskBatch, GLfloat innerRadius, GLfloat outerRadius, GLint nSlices, GLint nStacks);
         参数1:diskBatch,三角形批次类对象
         参数2:innerRadius,内圆半径
         参数3:outerRadius,外圆半径
         参数4:nSlices,圆盘围绕Z轴的三角形对的数量
         参数5:nStacks,圆盘外网到内围的三角形数量
         */
        gltMakeDisk(diskBatch, 1.5f, 3.0f, 13, 3);
    

    在 RenderScene 中进行绘制,直接调用上面代码中的 DrawWireFramedBatch 方法

    switch(nStep) {
            case 0:
                DrawWireFramedBatch(&sphereBatch);
                break;
            case 1:
                DrawWireFramedBatch(&torusBatch);
                break;
            case 2:
                DrawWireFramedBatch(&cylinderBatch);
                break;
            case 3:
                DrawWireFramedBatch(&coneBatch);
                break;
            case 4:
                DrawWireFramedBatch(&diskBatch);
                break;
        }
    

    相关文章

      网友评论

          本文标题:OpenGL-08-入门级案例3:绘制7种图元连接图形、几何图形

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