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. 【OpenGL的绘制】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

    推荐阅读