美文网首页
OpenGL的绘制

OpenGL的绘制

作者: 夜半醒 | 来源:发表于2020-05-23 16:27 被阅读0次

    OpenGL渲染图像

    OpenGL渲染图像需要执行的操作

    • 从OpenGL的几何图元中设置数据,用于构建形状。
    • 使用不同的着色器(shader)对输入的图元数据执行计算操作,判断他们的位置、颜色、以及其他渲染属性。
    • 将输入图元的数学描述转化为与屏幕位置对应的像素片元(shader)。这一步也称为光栅化(rasterization)。
    • 最后,针对光栅化过程产生的每个片元,执行片元着色器(fragment shader),从而决定这个片元的最终颜色和位置。
    • 如果有必要,还需要对每个片元执行一些额外的操作,例如判断片元对应的对象是否可见,或者将片元的颜色与当前屏幕位置的颜色进行融合。

    绘制图形需要做哪些准备呢?

    • 导入框架

      1. #include<GLTools.h> GLTool.h头文件包含了大部分GLTool中类似C语言的独立函数
      2. #include<GLShaderManager.h> 移入了GLTool 着色器管理器(shader Mananger)类。没有着色器,我们就不能在OpenGL(核心框架)进行着色。着色器管理器不仅允许我们创建并管理着色器,还提供一组“存储着色器”,他们能够进行一些初步䄦基本的渲染操作
      3. 在Mac系统下#include<glut/glut.h>
    • 启动GLUT

      1. 程序的总是“main”函数开始处理
        GLTools函数glSetWorkingDrectory用来设置当前工作目录。实际上在Windows中是不必要的,因为工作目录默认就是与程序可执行执行程序相同的目录。但是在Mac OS X中,这个程序将当前工作文件夹改为应用程序捆绑包中的/Resource文件夹。GLUT的优先设定自动进行了这个中设置,但是这样中方法更加安全。

      2. 创建窗口并设置显示模式
        glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
        GLUT_DOUBLE:双缓存窗口,是指绘图命令实际上是离屏缓存区执行的,然后迅速转换成窗口视图,这种方式,经常用来生成动画效果;
        GLUT_DEPTH:标志将一个深度缓存区分配为显示的一部分,因此我们能够执行深度测试;
        GLUT_STENCIL:确保我们也会有一个可用的模板缓存区。
        深度、模板测试后面会细致讲到。

      3. 初始化GLEW库
        重新调用GLEW库初始化OpenGL 驱动程序中所有丢失的入口点,以确保OpenGL API对开发者完全可用。
        调用glewInit()函数一次就能完成这一步。在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。

      4. SetupRC()
        实际上这个函数对GLUT 没有什么影响,但是在实际开始渲染之前,我们这里进行任何OpenGL 初始化都非常方便。这里的RC代表渲染环境,这是一个运行中的OpenGL状态机的句柄。在任何OpenGL 函数起作用之前必须创建一个渲染环境。而GLUT在我们第一次创建窗口时就完成了这项工作。

    int main(int argc, char* argv[])  { 
       gltSetWorkingDirectory(argv[0]);
       
       glutInit(&argc, argv);
       glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
       glutInitWindowSize(800,600);
       
       glutCreateWindow("OpenGL SphereWorld");
       
       glutReshapeFunc(ChangeSize);
       glutDisplayFunc(RenderScene);
       glutSpecialFunc(SpeacialKeys);
       
       GLenum err = glewInit();
       if (GLEW_OK != err) {
           fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
           return 1;
       }
     
       SetupRC();
       glutMainLoop();
       ShutdownRC();
       return 0;
    }
    

    看不懂?没关系!

    main函数中常用函数含义解析

    glutInit() 负责初始化GLUT库。它会处理向程序输入的命令行参数,并且移除其中与控制GLUT如何操作相关的部分。它必须是应用程序第一个GLUT函数,负责设置其他GLUT例程必需的数据结构。
    
    glutInitDisplayMode() 设置了程序所使用的窗口类型。窗口设置更多的OpenGL 特性,例如RAGA颜色空间,使用深度缓存或动画效果。
    
    glutInitWindowsSize() 设置所需的窗口大小,如果不想在这个设置一个固定值,也可以先查询显示设备的尺寸,然后根据计算机的屏幕动态设置窗口的大小。
    
    glutCreateWindow(),它的功能和它的名字一样,如果当前的系统环境可以满足glutInitDisplayMode()的显示模式要求,这里就会创建一个窗口(此时会调用计算机窗口系统的接口)。只有GLUT创建了一个窗口之后(其中包含创建创建OpenGL环境的过程),我们才可以使用OpenGL相关的函数
    
    glewInit()函数,属于另一个辅助库GLEW(OpenGL Extention Wrangler)。GLEW可以简化获取函数地址的过程,并且包含了可以跨平台使用的其他一些OpenGL编程方法。
    
    glutDisplayFunc(),它设置了一个显示回调(diplay callback),即GLUT在每次更新窗口内容的时候回自动调用该例程
    
    glutMainLoop(),这是一个无限执行的循环,它会负责一直处理窗口和操作系统的用户输入等操作。(注意:不会执行在glutMainLoop()之后的所有命令。)
    
    这里要注意最核心的三个方法

    changeSize RenderScene setupRC

    changeSize 触发条件:

    • 新建窗口
    • 窗口尺寸发生调整
      处理业务:
    • 设置OpenGL 视口
    • 设置OpenGL 投影方式等

    RenderScene 触发条件:

    • 系统自动触发
    • 开发者手动调用函数触发
      处理业务:
    • 清理缓存区(颜色, 深度, 模板缓存区等)
    • 使用存储着色器
    • 绘制图形

    setupRC 触发条件:

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

    星图Demo示例:

    setupRC函数中的代码
    void setupRC() {
        // 设置清屏颜色到颜色缓冲区
        glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
        // 初始化着色器管理器
        shaderManager.InitializeStockShaders();
        // 开启深度测试/背面剔除
        glEnable(GL_DEPTH_TEST);
        glEnable(GL_CULL_FACE);
        // 设置大球
        gltMakeSphere(torusBatch, 0.4f, 40, 80);
        // 设置小球
        gltMakeSphere(sphereBatch, 0.1f, 26, 13);
        // 设置地板顶点数据&地板纹理
        GLfloat texSize = 10.0f;
        floorBatch.Begin(GL_TRIANGLE_FAN, 4, 1);
        floorBatch.MultiTexCoord2f(0, 0.0f, 0.0f);
        floorBatch.Vertex3f(-20.0f, -0.41f, 20.0f);
        
        floorBatch.MultiTexCoord2f(0, texSize, 0.0f);
        floorBatch.Vertex3f(20.0f, -0.41f, 20.f);
        
        floorBatch.MultiTexCoord2f(0, texSize, texSize);
        floorBatch.Vertex3f(20.0f, -0.41f, -20.0f);
        
        floorBatch.MultiTexCoord2f(0, 0.0f, texSize);
        floorBatch.Vertex3f(-20.0f, -0.41f, -20.0f);
        floorBatch.End();
        
        // 随机小球顶点坐标数据
        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);
        }
        
        // 命名纹理对象
        glGenTextures(3, uiTextures);
        
        // 将TGA文件加载为2D纹理。
        // 参数1:纹理文件名称
        // 参数2&参数3:需要缩小&放大的过滤器
        // 参数4:纹理坐标环绕模式
        glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
        loadTGATexture("marble.tga", GL_LINEAR_MIPMAP_LINEAR, GL_LINEAR, GL_REPEAT);
        
        
        glBindTexture(GL_TEXTURE_2D, uiTextures[1]);
        loadTGATexture("marslike.tga", GL_LINEAR_MIPMAP_LINEAR,
                       GL_LINEAR, GL_CLAMP_TO_EDGE);
        
        
        glBindTexture(GL_TEXTURE_2D, uiTextures[2]);
        loadTGATexture("moonlike.tga", GL_LINEAR_MIPMAP_LINEAR,
                       GL_LINEAR, GL_CLAMP_TO_EDGE);
    }
    
    changeSize函数中的代码
    // 屏幕更改大小或已初始化
    void changeSize(int nWidth, int nHeight) {
        // 设置视口
        glViewport(0, 0, nWidth, nHeight);
        // 设置投影方式
        viewFrusrum.SetPerspective(35.0f, float(nWidth) / float(nHeight), 1.0f, 100.0f);
        
        // 将投影矩阵加载到投影矩阵堆栈
        projectionMatrix.LoadMatrix(viewFrusrum.GetProjectionMatrix());
        modelViewMatrix.LoadIdentity();
        
        // 将投影矩阵堆栈和模型视图矩阵对象设置到管道中
        transformPipeline.SetMatrixStacks(modelViewMatrix, projectionMatrix);
    }
    
    renderScene函数中的代码
    // 进行调用绘制场景
    void renderScene(void) {
        // 地板颜色值
        static GLfloat vFloorColor[] = {1.0f, 1.0f, 0.0f, 0.75f};
        
        // 时间动画
        static CStopWatch rotTimer;
        float yRot = rotTimer.GetElapsedSeconds() * 60.0f;
        
        // 清除颜色缓存区和深度缓存区
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        
        // 压栈
        modelViewMatrix.PushMatrix();
        
        // 设置观察者矩阵
        M3DMatrix44f mCamera;
        cameraFrame.GetCameraMatrix(mCamera);
        modelViewMatrix.MultMatrix(mCamera);
        
        // 压栈
        modelViewMatrix.PushMatrix();
        
        // 翻转Y轴
        modelViewMatrix.Scale(1.0f, -1.0f, 1.0f);
        modelViewMatrix.Translate(0.0f, 0.8f, 0.0f);
        
        // 指定顺时针为正面
        glFrontFace(GL_CW);
        
        // 绘制地面以外其他部分
        drawSomething(yRot);
        
        // 恢复逆时针为正面
        glFrontFace(GL_CCW);
        
        // 绘制镜面, 恢复矩阵
        modelViewMatrix.PopMatrix();
        
        // 开启混合功能
        glEnable(GL_BLEND);
        
        // 指定glBlendFunc 颜色混合方程式
        glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
        
        // 绑定地面纹理
        glBindTexture(GL_TEXTURE_2D, uiTextures[0]);
        
        // 纹理调整着色器
        shaderManager.UseStockShader(GLT_SHADER_TEXTURE_MODULATE,
                                     transformPipeline.GetModelViewProjectionMatrix(),
                                     vFloorColor,
                                     0);
        
        // 开始绘制
        floorBatch.Draw();
        // 取消混合
        glDisable(GL_BLEND);
        // 绘制地面以外其他部分
        drawSomething(yRot);
        // 绘制完 恢复矩阵
        modelViewMatrix.PopMatrix();
        
        // 交换缓存区
        glutSwapBuffers();
        
        // 提交重新渲染
        glutPostRedisplay();
        
    }
    
    main函数中的代码
    int main(int argc, char *argv[]) {
    
        gltSetWorkingDirectory(argv[0]);
        
        glutInit(&argc, argv);
        glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH);
        glutInitWindowSize(800, 600);
        
        glutCreateWindow("OpenGL SphereWorld");
        
        glutReshapeFunc(changeSize);
        glutDisplayFunc(renderScene);
        glutSpecialFunc(SpeacialKeys);
        
        GLenum err = glewInit();
        if (GLEW_OK != err) {
            fprintf(stderr, "GLEW Error: %s\n", glewGetErrorString(err));
            return 1;
        }
        
        setupRC();
        glutMainLoop();
        shutdownRC();
        
        return 0;
    }
    
    让我们来看一下效果
    效果图.gif
    当我们需要移动视角 也就是改变观察者的位置与角度
    //**3.移动照相机参考帧,来对方向键作出响应
    void SpeacialKeys(int key,int x,int y)
    {
        
        float linear = 0.1f;
        float angular = float(m3dDegToRad(5.0f));
        
        if (key == GLUT_KEY_UP) {
            
            //MoveForward 平移
            cameraFrame.MoveForward(linear);
        }
        
        if (key == GLUT_KEY_DOWN) {
            cameraFrame.MoveForward(-linear);
        }
        
        if (key == GLUT_KEY_LEFT) {
            //RotateWorld 旋转
            cameraFrame.RotateWorld(angular, 0.0f, 1.0f, 0.0f);
        }
        
        if (key == GLUT_KEY_RIGHT) {
            cameraFrame.RotateWorld(-angular, 0.0f, 1.0f, 0.0f);
        } 
    }
    

    按下键盘方向键我们就可以移动观察者的位置与角度

    按下键盘前后左右.gif

    Demo地址: https://github.com/daolelidong/OpenGLUniverse

    相关文章

      网友评论

          本文标题:OpenGL的绘制

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