美文网首页
OpenGL 综合案例

OpenGL 综合案例

作者: Joker_King | 来源:发表于2020-07-19 14:38 被阅读0次

    在绘制之前,我们先来介绍下一下几个头文件的作用。

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

    接下来我们需要在外部声明几个全局变量。

    // 着色器管理器
    GLShaderManager shaderManager;
    // 模型视图矩阵   
    GLMatrixStack modelViewMatrix;
    // 投影矩阵 
    GLMatrixStack projectionMatrix;
    // 视景体, 用来完成物体从3D到2D的映射
    GLFrustum           viewFrustum;
    // 几何图形变换管道
    GLGeometryTransform transformPipeline;
    // 大球
    GLTriangleBatch torusBatch;
    // 小球             
    GLTriangleBatch sphereBatch; 
    //地板                     
    GLBatch floorBatch;          
    // 角色帧 照相机角色帧
    GLFrame cameraFrame;
    // 添加附加随机球
    #define NUM_SPHERES 50
    GLFrame spheres[NUM_SPHERES];
    

    绘制地板

    main函数工作流程

    int main(int argc, char* argv[])
    {
        // 设置工作目录
        gltSetWorkingDirectory(argv[0]);
        // 初始化GLUT环境
        glutInit(&argc, argv);
        // 设置显示模式
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        // 设置初始化窗口的大小
        glutInitWindowSize(800,600);
        // 创建一个名为OpenGL SphereWorld的窗口
        glutCreateWindow("OpenGL SphereWorld");
        // 注册窗口改变时的回调函数
        glutReshapeFunc(ChangeSize);
        // 注册一个绘图的函数
        glutDisplayFunc(RenderScene);
        // 初始化一个GLEW库,确保OpenGL API对程序完全可用。在试图做任何渲染之前,要检查确定驱动程序的初始化过程中没有任何问题
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        // 自定义函数,做一些变量的初始化
        SetupRC();
        // 开启mainloop,相当于OC中runloop,是一个运行循环
        glutMainLoop();    
        return 0;
    }
    

    glutInitDisplayMode

    函数功能为设置初始显示模式。mode可取以下值或其组合:


    -w638

    SetupRC

    void SetupRC()
    {   //清理背景颜色
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        //1.初始化
        shaderManager.InitializeStockShaders();
        //2.开启深度测试
        glEnable(GL_DEPTH_TEST);
        //3.设置地板顶点数据
        floorBatch.Begin(GL_LINES, 324);
        for(GLfloat x = -20.0; x <= 20.0f; x+= 0.5) {
            floorBatch.Vertex3f(x, -0.55f, 20.0f);
            floorBatch.Vertex3f(x, -0.55f, -20.0f);
            floorBatch.Vertex3f(20.0f, -0.55f, x);
            floorBatch.Vertex3f(-20.0f, -0.55f, x);
        }
        floorBatch.End();
    }
    

    ChangeSize

    void ChangeSize(int nWidth, int nHeight)
    {
        //1.设置视口
        glViewport(0, 0, nWidth, nHeight);
        //2.创建投影矩阵,。
        viewFrustum.SetPerspective(35.0f, float(nWidth)/float(nHeight), 1.0f, 100.0f);
        //viewFrustum.GetProjectionMatrix()  获取viewFrustum投影矩阵,并将其加载到投影矩阵堆栈上
        projectionMatrix.LoadMatrix(viewFrustum.GetProjectionMatrix());
        //3.设置变换管道使用的两个矩阵堆栈(变换矩阵modelViewMatrix ,投影矩阵projectionMatrix)
        //初始化GLGeometryTransform 的实例transformPipeline.通过将它的内部指针设置为模型视图矩阵堆栈 和投影矩阵堆栈实例,来完成初始化。
        //当然这个操作也可以在SetupRC函数中完成,但是在窗口大小改变时或者窗口创建时设置它们并没有坏处。而且这样可以一次性完成矩阵和管线的设置。
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    

    RenderScene

    void RenderScene(void)
    {
        //1.颜色值
        GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
        //2.清除颜色缓存区和深度缓冲区中的数据
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        //3.使用平面着色器,绘制地面。
        shaderManager.UseStockShader(GLT_SHADER_FLAT, transformPipeline.GetModelViewProjectionMatrix(), vFloorColor);
        floorBatch.Draw();
        //4.执行缓存区交换
        glutSwapBuffers();
    }
    

    绘制大球

    SetupRC

    SetupRC中初始化球体的数据。

    // 初始化球体的三角形批次,后面参数依次是,球半径,片段数,堆叠数
    gltMakeSphere(torusBatch, 0.5f, 300, 80);
    

    RenderScene

    RenderScene函数中对大球进行绘制。

    // 定义球体的颜色
    static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
    // 定义一个计时器,让球体旋转起来。
    static CStopWatch    rotTimer;
    float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
        
    // 绘制球体
    // 获取光源位置
    M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
    // 将大球位置平移(3.0)向屏幕里面。
    modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
    // 向模型视图矩阵中压入一个栈,用来处理球体的旋转
    modelViewMatrix.PushMatrix();
    // 让球体转起来
    modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
    // 使用点光源着色器
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                 transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
    // 绘制
    torusBatch.Draw();
    // 绘制完毕后出栈
    modelViewMatrix.PopMatrix();
    

    添加以上代码,此时球体已经被渲染出来了,但是此时它并没有动起来。我们需要提交重新渲染,让它动起来。

    glutPostRedisplay();
    

    RenderScene的最终代码

    void RenderScene(void)
    {
        //1.颜色值(地板,大球,小球颜色)
        static GLfloat vFloorColor[] = { 0.0f, 1.0f, 0.0f, 1.0f};
        static GLfloat vTorusColor[] = { 1.0f, 0.0f, 0.0f, 1.0f };
        
        //2.基于时间动画
        static CStopWatch    rotTimer;
        float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
        
        //2.清除颜色缓存区和深度缓冲区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        //**// 压栈
        modelViewMatrix.PushMatrix();
        
        //3.绘制地面
        shaderManager.UseStockShader(GLT_SHADER_FLAT,
                                     transformPipeline.GetModelViewProjectionMatrix(),
                                     vFloorColor);
        floorBatch.Draw();
        
        
        //4.获取光源位置
        M3DVector4f vLightPos = {0.0f,10.0f,5.0f,1.0f};
       
        //5.使得大球位置平移(3.0)向屏幕里面
        modelViewMatrix.Translate(0.0f, 0.0f, -3.0f);
        //6.压栈(复制栈顶)
        modelViewMatrix.PushMatrix();
        //7.大球自转
        modelViewMatrix.Rotate(yRot, 0.0f, 1.0f, 0.0f);
        //8.指定合适的着色器(点光源着色器)
        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                     transformPipeline.GetProjectionMatrix(), vLightPos, vTorusColor);
        torusBatch.Draw();
        //9.绘制完毕则Pop
        modelViewMatrix.PopMatrix();
        modelViewMatrix.PopMatrix();
        
        //10.执行缓存区交换
        glutSwapBuffers();
        glutPostRedisplay();
    }
    

    绘制小球

    SetupRC

    SetupRC函数中设置小球的模型

    // 设置小球球模型
    gltMakeSphere(sphereBatch, 0.1f, 13, 26);
    // 随机位置放置小球球
    for (int i = 0; i < NUM_SPHERES; i++) {
        //y轴不变,X,Z产生随机值
        GLfloat x = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        GLfloat z = ((GLfloat)((rand() % 400) - 200 ) * 0.1f);
        //在y方向,将球体设置为0.0的位置,这使得它们看起来是飘浮在眼睛的高度
        //对spheres数组中的每一个顶点,设置顶点数据
        spheres[i].SetOrigin(x, 0.0f, z);
    }
    

    RenderScene

    RenderScene函数中绘制小球

    // 画小球
    for (int i = 0; i < NUM_SPHERES; i++) {
        modelViewMatrix.PushMatrix();
        modelViewMatrix.MultMatrix(spheres[i]);
        shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF, transformPipeline.GetModelViewMatrix(),
                                     transformPipeline.GetProjectionMatrix(), vLightPos, vSphereColor);
        sphereBatch.Draw();
        modelViewMatrix.PopMatrix();
    }
    

    接下来我们让一个小球围绕着大球转起来
    我们需要在RenderScene函数中添加以下代码。

    modelViewMatrix.Rotate(yRot * -2.0f, 0.0f, 1.0f, 0.0f);
    modelViewMatrix.Translate(0.8f, 0.0f, 0.0f);
    shaderManager.UseStockShader(GLT_SHADER_POINT_LIGHT_DIFF,transformPipeline.GetModelViewMatrix(),transformPipeline.GetProjectionMatrix(),vLightPos,vSphereColor);
    sphereBatch.Draw();
    

    移动观察者

    首先在模型视图矩阵堆栈中加入一个观察者矩阵,在RenderScene函数中添加如下代码。

    // 创建一个观察者矩阵
    M3DMatrix44f mCamera;
    cameraFrame.GetCameraMatrix(mCamera);
    // 将观察者矩阵压入模型视图堆栈中。
    modelViewMatrix.PushMatrix(mCamera);
    
    。。。。 
    // 在最后需要pop
    modelViewMatrix.PopMatrix();
    

    接下来我们需要实现SpeacialKeys函数。实现键盘的控制

    void SpeacialKeys(int key,int x,int y){
        float linear = 0.1f;
        float angular = float(m3dDegToRad(5.0f));
        
        if (key == GLUT_KEY_UP) {
            cameraFrame.MoveForward(linear);
        }
        if (key == GLUT_KEY_DOWN) {
            cameraFrame.MoveForward(-linear);
        }
        
        if (key == GLUT_KEY_LEFT) {
            cameraFrame.RotateWorld(angular, 0, 1, 0);
        }
        if (key == GLUT_KEY_RIGHT) {
            cameraFrame.RotateWorld(-angular, 0, 1, 0);
        }
    }
    

    相关文章

      网友评论

          本文标题:OpenGL 综合案例

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