美文网首页
视频特效学习02- 在存储着色器的情况下渲染图形并能通过键盘控制

视频特效学习02- 在存储着色器的情况下渲染图形并能通过键盘控制

作者: 小破孩丫 | 来源:发表于2020-04-14 16:59 被阅读0次

    头文件介绍:
    #include "GLFrustum.h":矩阵工具类,用来快速设置正/透视投影矩阵。完成坐标3D->2D映射过程。
    #include "GLTools.h":头文件包含了大部分GLTool中类似C语言的独立函数。
    #include "GLMatrixStack.h":矩阵工具类。可以利用GLMatrixStack加载单元矩阵、矩阵相乘、压栈、出栈、平移、缩放、旋转。
    #include "GLFrame.h":矩阵工具类。表示位置。
    #include "GLGeometryTransform.h":变换管道类。用来快速在代码中传输视图矩阵、投影矩阵、视图投影变换矩阵等。
    #include "GLBatch.h":三角形批次类,可以传输顶点、光照、纹理、颜色数据到存储着色器中。

    #include <stdio.h>
    #include "GLShaderManager.h"
    #include "GLTools.h"
    #include <GLUT/GLUT.h>
    #include "GLFrame.h"
    #include "GLFrustum.h"
    #include "GLMatrixStack.h"
    #include <math.h>
    #include "GLBatch.h"
    #include "GLGeometryTransform.h"
    //存储着色器管理工具类
    GLShaderManager     shaderManager;
    //模型视图矩阵
    GLMatrixStack       modelViewMatrix;
    //投影矩阵
    GLMatrixStack       projectionMatrix;
    //观察者坐标
    GLFrame             cameraFrame;
    //视图坐标
    GLFrame             objectFrame;
    //投影设置
    GLFrustum           viewFrustum;
    //几何变换的管道 方便计算矩阵
    GLGeometryTransform transformPipeline;
    //容器类(7种不同的图元对应7种容器对象)
    GLBatch             pointBatch;
    GLBatch             lineBatch;
    GLBatch             lineStripBatch;
    GLBatch             lineLoopBatch;
    GLBatch             triangleBatch;
    GLBatch             triangleStripBatch;
    GLBatch             triangleFanBatch;
    GLfloat vGreen[] = { 0.0f, 1.0f, 0.0f, 1.0f };
    GLfloat vBlack[] = { 0.0f, 0.0f, 0.0f, 1.0f };
    //跟踪效果步骤
    int  nStep = 0;
    
    //重塑函数
    void changeSize(int w, int h){
        glViewport(0, 0, w, h);
        //由于在这里能够拿到宽高,所以在这里创建投影矩阵,并将其加载到投影矩阵堆栈中
        viewFrustum.SetPerspective(45.0f, float(w) / float(h), 1.0f, 500.0f);
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        
        //载入模型视图矩阵,设置为单元矩阵,可以不写
        modelViewMatrix.LoadIdentity();
    
        //设置变换管线 把变换管线通道添加进去
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    
    //设置点的大小
    void drawPoint(){
        glPointSize(4.0f);
        pointBatch.Draw();
        glPointSize(1.0f);
    }
    
    //设置线的宽度
    void drawLine(){
        glLineWidth(2.0f);
        lineBatch.Draw();
        glLineWidth(1.0f);
    }
    
    
    //设置线条的宽度
    void drawLines(){
        glLineWidth(2.0f);
        lineStripBatch.Draw();
        glLineWidth(1.0f);
    }
    
    //设置环线的宽度
    void drawLoopLines(){
        glLineWidth(2.0f);
        lineLoopBatch.Draw();
        glLineWidth(1.0f);
    }
    
    //金字塔
    void DrawWireFramedBatch(GLBatch* pBatch)
    {
     
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vGreen);
        pBatch->Draw();
     
        //画黑色边框
        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);
        
    
        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);
        
        
    }
    //显示函数
    void renderScene(void){
        //清空缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
        //压栈 为了每一次变化相互独立(可以理解为复制了一份,栈顶长度+1) 不影响后面的变化 此处有两个modelViewMatrix
        modelViewMatrix.PushMatrix();
        
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        //矩阵乘以矩阵堆栈的顶部矩阵,相乘的结果存储在矩阵堆栈顶部
        modelViewMatrix.MultMatrix(mCamera);
        
        M3DMatrix44f mObjectFrame;
        objectFrame.GetMatrix(mObjectFrame);
        //此处的modelViewMatrix的矩阵值 = modelViewMatrix * mCamera * mObjectFrame,经过了两次相乘
        modelViewMatrix.MultMatrix(mObjectFrame);
        
        //核心方法 使用存储着色器-平面着色器 参数2为4*4变换矩阵,通过transformPipeline.GetModelViewProjectionMatrix直接获得模型视图投影变换矩阵,不需要我们计算
        shaderManager.UseStockShader(GLT_SHADER_FLAT,transformPipeline.GetModelViewProjectionMatrix(),vBlack);
        //根据空格键 切换绘制的图形
        switch (nStep) {
            case 0:
                //设置点的大小
                drawPoint();
                break;
            case 1:
                //设置线的宽度
                drawLine();
                break;
            case 2:
                //设置线条的宽度
                drawLines();
                break;
            case 3:
                //设置环线的宽度
                drawLoopLines();
                break;
            case 4:
                DrawWireFramedBatch(&triangleBatch);
                break;
            case 5:
                DrawWireFramedBatch(&triangleStripBatch);
                break;
            case 6:
                DrawWireFramedBatch(&triangleFanBatch);
                break;
                break;
        }
        //出栈 还原到以前的模型视图矩阵
        modelViewMatrix.PopMatrix();
        
        //缓冲区交换
        glutSwapBuffers();
    }
    
    //设置渲染环境
    void setupRC(void){
        //设置背景颜色
        glClearColor(0.8f, 0.6f, 0.8f, 1);
        //.初始化着色器
        shaderManager.InitializeStockShaders();
        //开启深度测试 立体
        glEnable(GL_DEPTH_TEST);
        
        //设置观察距离 也就是观察者和图形的距离
        cameraFrame.MoveForward(-15.0f);
        //定义一些点,三角形形状
        GLfloat vCoast[9] = {
            3,3,0,
            0,3,0,
            3,0,0
        };
        //批次类绘制 Begin-Copy-End 三件套
        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();
    }
    
    //特殊键位函数(上、下、左、右移动)
    void specialKeys(int key, int x, int y){
        //由于需要多方位观察图形,不能每一个顶点坐标都进行旋转,所以只能通过改变观察者的坐标来进行。
        if (key == GLUT_KEY_UP) {
            //此处的1.0表示true
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 1.0f, 0.0f, 0.0f);
        } else if (key == GLUT_KEY_DOWN){
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 1.0f, 0.0f, 0.0f);
        }else if (key == GLUT_KEY_LEFT){
            objectFrame.RotateWorld(m3dDegToRad(-5.0f), 0.0f, 1.0f, 0.0f);
        }else if (key == GLUT_KEY_RIGHT){
            objectFrame.RotateWorld(m3dDegToRad(5.0f), 0.0f, 1.0f, 0.0f);
        }
         glutPostRedisplay();
    }
    
    //根据空格次数。切换不同的“窗口名称”
    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();
    }
    
    //检查OpenGL API是否安全可用
    int checkOpenGLInit(void){
       GLenum status = glewInit();
        if(status != GLEW_OK){
            printf("GLEW Error:%s\n",glewGetErrorString(status));
            return 1;
        }
        return 0;
    }
    
    
    int main(int argc,char *argv[])
    {
        gltSetWorkingDirectory(argv[0]);
        glutInit(&argc, argv);
        glutInitWindowSize(600, 600);
        //申请一个双缓存区、颜色缓存区、深度缓存区、模板缓存区
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH | GLUT_STENCIL);
        //创建window的名称
        glutCreateWindow("Title");
        
        //注册回调函数(改变尺寸)
        glutReshapeFunc(changeSize);
        //点击空格时,调用的函数
        glutKeyboardFunc(keyPressFunc);
        //特殊键位函数(上下左右)
        glutSpecialFunc(specialKeys);
        //显示函数
        glutDisplayFunc(renderScene);
        //判断一下是否能初始化glew库,确保项目能正常使用OpenGL 框架
        checkOpenGLInit();
        //绘制
        setupRC();
        //runloop运行循环
        glutMainLoop();
        
        return 0;
    }
    

    核心是UseStockShader方法,参数1使用的是平面着色器,参数2要传入mvp,也就是模型视图投影矩阵。通过transformPipeline.GetModelViewProjectionMatrix方法快速获得mvp。而transformPipeline的设置需要 SetMatrixStacks(modelViewMatrix, projectionMatrix)方法,modelViewMatrix的矩阵需要相乘获得,projectionMatrix需要LoadMatrix方法获得。

    相关文章

      网友评论

          本文标题:视频特效学习02- 在存储着色器的情况下渲染图形并能通过键盘控制

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