OpenGL的绘制

OpenGL渲染图像

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

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

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

  • 导入框架

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