美文网首页
3.绘制基本图元、环、金字塔与矩阵的压栈出栈

3.绘制基本图元、环、金字塔与矩阵的压栈出栈

作者: 脩脩脩 | 来源:发表于2020-07-10 18:36 被阅读0次

    一、案例

    基本的图元装配方式 在之前的文章介绍过。
    这个案例是对这些基本的图元进行操作 以及用这些图元完成一些简单的图形。


    gif1.gif

    流程:

    程序流程图

    main 函数:程序入口

    int main(int argc,char* argv[])
    {
        gltSetWorkingDirectory(argv[0]);
        glutInit(&argc, argv);
        //申请一个颜色缓存区,深度缓存区,双缓存区,模板缓存区
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        //设置windo的名称
        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();
        glutMainLoop();
        return 0;
    }
    

    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();
    }
    

    setupRC():初始化图形数据配置(顶点数据,图元链接方式,矩阵观察者,相应的矩阵设置)

    void setupRC()
    {
        //清屏
        glClearColor(0.9, 0.9, 0.9, 1.0);
        //初始化着色管理器
        shaderManager.InitializeStockShaders();
       //开启深度测试:这个功能在后面讲解。
        glEnable(GL_DEPTH_TEST);
        //将模型视图矩阵和投影矩阵放到变换管道中,能帮助快速进行矩阵相乘
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
        //观察者视角往前移动 -15
        cameraFrame.MoveForward(-15.0f);
         //定义一些点,三角形形状。
         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个顶点(包括圆心)
          //添加闭合的终点
           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();
    }
    

    RenderScene():处理图形渲染,系统和手动触发,每次渲染前需要清除缓存。

    void RenderScene(void)
    {
        //清除缓存
        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);
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
        switch (nStep) {
            case 0:
                glPointSize(4.0f);
                pointBatch.Draw();
                break;
            case 1:
                glLineWidth(4.0f);
                lineBatch.Draw();
                break;
            case 2:
                lineStripBatch.Draw();
                glLineWidth(1.0f);
                break;
            case 3:
                lineLoopBatch.Draw();
                glLineWidth(1.0);
                break;
            case 4:
                DrawWireFramedBatch(&triangleBatch);
                break;
            case 5:
                DrawWireFramedBatch(&triangleStripBatch);
                break;
            case 6:
                DrawWireFramedBatch(&triangleFanBatch);
                break;
        }
        //还原到以前的模型视图矩阵(单位矩阵)
        modelViewMatrix.PopMatrix();
        //缓冲区交换
        glutSwapBuffers();
    }
    //处理图形的填充和边框的处理。
    void DrawWireFramedBatch(GLBatch *pBatch)
    {
        //绿色
        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(GL_FRONT_AND_BACK, GL_LINE);
    
        glLineWidth(2.0f);
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vBlack);
        pBatch->Draw();
        
        glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
        glDisable(GL_POLYGON_OFFSET_LINE);
        glLineWidth(1.0f);
        glDisable(GL_BLEND);
        glDisable(GL_LINE_SMOOTH);
    }
    

    SpecialKeys(): 监听键盘上下左右

    void SpecialKeys(int key, int x, int y)
    {
        if (key == GLUT_KEY_UP) {
            //绕x轴转
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0, 0.0, 0.0);
        }
        if (key == GLUT_KEY_DOWN) {
            //绕x轴转
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0, 0.0, 0.0);
        }
        if (key == GLUT_KEY_LEFT) {
            //绕x轴转
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0, 1.0, 0.0);
        }
        if (key == GLUT_KEY_RIGHT) {
            //绕x轴转
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0, 1.0, 0.0);
        }
        glutPostRedisplay();
    }
    

    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;
            default:
                break;
        }
        glutPostRedisplay();
    }
    

    二、关于矩阵的堆栈

    这个案例相比于之前的案例多出了一个新的知识点:矩阵的压栈与出栈。

    2.1:初始化一个单元矩阵,矩阵堆栈中默认是存在一个单元矩阵的,但是也可以自己初始化一个。此时堆栈中存在一个矩阵。
    modelViewMatrix.LoadIdentity();
    
    2.2:压栈:把栈顶的矩阵复制一份后再压到栈顶。此时堆栈中有两个单元矩阵
    modelViewMatrix.PushMatrix();
    
    2.3:矩阵相乘:
    • 获取一个观察者矩阵
     M3DMatrix44f mCamera;
     cameraFrame.GetCameraMatrix(mCamera);
    
    • 让观察者矩阵乘以矩阵堆栈栈顶矩阵, 相乘得到一个新的矩阵,再压入栈顶:此时堆栈中为一个原单元矩阵,一个观察者矩阵。
    //矩阵乘以矩阵.
    modelViewMatrix.MultMatrix(mCamera);
    M3DMatrix44f mObjectFrame;
    
    • 还是矩阵相乘的过程,取出刚刚入栈的观察者矩阵乘以物体矩阵,得到一个新的模型视图矩阵,再压入栈顶。
    /*只要使用GetMatrix 函数就可以获取矩阵堆栈顶部的值,这个函数可以进行2次重载, 
    用来使用GLShaderManager的使用,或者是获取顶部矩阵的顶点副本数据.*/
    objectFrame.GetMatrix(mObjectFrame);
    //矩阵乘以矩阵.
    modelViewMatrix.MultMatrix(mObjectFrame);
    

    2.4:出栈:将视图模型矩阵pop出栈
    //还原到以前的模型视图矩阵(单位矩阵,就是恢复成原来还没有处理矩阵前的状态)

    modelViewMatrix.PopMatrix();
    
    图1

    图1 by Style_月月

    相关文章

      网友评论

          本文标题:3.绘制基本图元、环、金字塔与矩阵的压栈出栈

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