这是我的第一篇学习OpenGL的笔记,也是博主的第一篇博客,希望能够在这里和大家一起成长。
下面的代码是《OpenGL超级宝典(第五版)》中的示例代码,基本程序如下:
1 #include <GLtools.h> //OpenGL toolkit:工具箱库——包含一个用于操作矩阵和向量的3D数学库,并依靠GLEW获得OpenGL 3.3中用来产生和渲染一些简单3D对象的函数,以及对平截头体、相机类和变换矩阵进行管理的函数的充分支持。 2 #include <GLShaderManager.h> //Shader Manager Class: 3 //GLEW:自动初始化所有新函数指针并包含所需类型定义、常量和枚举值的扩展加载库。 4 //#include <GL/glut.h> 5 #define FREEGLUT_STATIC //因为我们使用的是freeglut的静态库版本,所以需要在它前面添加FREEGLUT_STATIC处理器宏 6 #include <GL/glut.h> //Windows FreeGlut equivalent:一种实用工具箱 7 8 9 GLBatch triangleBatch; //声明一个简单的GLTool封装类,用于将三角形顶点批次进行封装 10 GLShaderManager shaderManager; //简单的存储着色器实例 11 12 //////////////////////////////////////////////////////////////////////////////////////////// 13 //62 CHAPTER 2 Getting Started 14 //04_0321712617_ch02.qxd 6/21/10 11:35 AM Page 62 15 //Windows has changed size, or has just been created. In either case, we need to use the 16 //windows dimensions to set the viewport and the projection matrix. 17 void ChangeSize(int w,int h) //在窗口大小改变时接受新的宽度和高度 18 { 19 glViewport(0,0,w,h); //修改从目的坐标系到屏幕坐标系上的映射,x,y参数代表窗口中视口的左下角坐标,而宽度和高度参数实用像素表示的。 20 } 21 /////////////////////////////////////////////////////////////////////////////////////////// 22 //this fuction does any needed initialization on the rendering context. 23 //This is the first opportunity to do any OpenGL related tasks. 24 void SetupRC() 25 { 26 //Blue background 27 glClearColor(0.0f,0.0f,1.0f,1.0f); //这个函数设置用来进行窗口清除的颜色 28 shaderManager.InitializeStockShaders(); //由于着色器管理器需要编译和链接它自己的着色器,所以我们必须在OpenGL开始的时候调用InitializeStockShaders方法。 29 30 //Load up a triangle 31 GLfloat vVerts[]={-0.5f,0.0f,0.0f, 32 0.5f,0.0f,0.0f, 33 0.0f,0.5f,0.0f }; //创建三角形图元,三角形图元就是组成三角形的一系列点或者顶点;值得一提的是,所有这些点的Z坐标均设置为0 34 //下面代码建立了一个三角形的批次,仅包含3个顶点。 35 triangleBatch.Begin(GL_TRIANGLES,3); //批次容器的初始化,告诉这个类它代表哪种图元,其中包括顶点数,以及(可选)一组或两组纹理坐标。 36 triangleBatch.CopyVertexData3f(vVerts); //然后至少复制一个由三分量顶点组成的数组。 37 triangleBatch.End(); //表示已经完成了数据复制工作,并且将设置内部标记,以通知这个类包含哪些属性。 38 } 39 /////////////////////////////////////////////////////////////////////////////////////////// 40 //called to draw scene 41 //窗口发生改变的时候,调用该函数用以重绘窗口 42 void RenderScene(void) 43 { 44 //Clear the window with current clearing color 45 //上面的函数将清除颜色设置为蓝色,现在我们需要执行一个函数真正进行清除。 46 glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT); //glClear函数清除一个或一组特定的缓冲区。颜色缓冲区、深度缓冲区、模板缓冲区。深度缓冲区用于进行深度测试,其中存储了屏幕上每个像素的深度值。 47 48 //下面的代码设置一组浮点数来表示红色(其alpha值设为1.0),并将它传递到存储着色器,即GLT_SHADER_IDENTITY着色器。这个着色器是使用指定颜色以默认笛卡尔坐标系在屏幕上渲染几何图形。 49 GLfloat vRed[]={1.0f,0.0f,0.0f,1.0f}; 50 shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed); //UseStockShader函数会选择一个存储着色器并提供这个着色器的Uniform值。 51 triangleBatch.Draw(); //指示将几何图形提交到着色器,绘制批次容器所描述的内容。 52 53 //Perform the buffer swap to display the back buffer 54 //当设置OpenGL时,我们指定要一个双缓冲渲染环境。这就意味着将在后台缓冲区进行渲染,然后在结束时交换到前台。这种形式能够防止观察者看到可能伴随着动画帧与动画帧之间闪烁的渲染过程。 55 //缓冲区交换将以平台特定的方式进行,但是GLUT有一个单独的函数调用可以完成这项工作,即glutSwapBuffers()。 56 glutSwapBuffers(); 57 } 58 /////////////////////////////////////////////////////////////////////////////////////////// 59 //Main entry point for GLUT based programs 60 int main(int argc,char* argv[]) 61 { 62 gltSetWorkingDirectory(argv[0]); //设置当前工作目录 63 64 //接下来,进行一些基于GLUT的标准设置 65 glutInit(&argc,argv); //传输命令行参数,并初始化GLUT库 66 glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL); //告诉GLUT库,在创建窗口时要使用哪种显示模式。GLUT_DOUBLE:双缓冲窗口 GLUT_RGBA:RGBA颜色模式 GLUT_DEPTH:将一个深度缓冲区分配为显示的一部分 GLUT_STENCIL:确保我们也会有一个可用的模板缓冲区。 67 //其中深度缓冲区可用于进行深度测试,存储了屏幕上每个像素的深度值。 68 glutInitWindowSize(800,600); //设置窗口的大小 69 glutCreateWindow("Triangle"); //创建以“Triangle”为标题的窗口 70 //回调函数 71 glutReshapeFunc(ChangeSize); //为窗口改变大小而设置一个回调函数,以便能够设置视点 72 glutDisplayFunc(RenderScene); //注册一个函数以包含OpenGL渲染代码 73 74 //在开始运行主消息循环之前,还要解决两个问题 75 GLenum err=glewInit(); //初始化GLEW库。重新调用GLEW库初始化OpenGL驱动程序中所有丢失的入口点,以确保OpenGL API对我们来说完全可用。 76 if(GLEW_OK!=err) //在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。 77 { 78 fprintf(stderr,"GLEW Error:%s\n",glewGetErrorString(err)); 79 return 1; 80 } 81 SetupRC(); //调用SetupRC。这里RC代表渲染环境(Rendering Context),这是一个运行中的OpenGL状态机的句柄。在任何OpenGL函数起作用之前必须创建一个渲染环境。 82 83 //开始主消息循环并结束main函数 84 glutMainLoop(); 85 return 0; 86 }
头文件分析
1 #include <GLtools.h> //OpenGL toolkit:工具箱库——包含一个用于操作矩阵和向量的3D数学库,并依靠GLEW获得OpenGL 3.3中用来产生和渲染一些简单3D对象的函数,以及对平截头体、相机类和变换矩阵进行管理的函数的充分支持。 2 #include <GLShaderManager.h> //Shader Manager Class 3 #define FREEGLUT_STATIC //因为我们使用的是freeglut的静态库版本,所以需要在它前面添加FREEGLUT_STATIC处理器宏 4 #include <GL/glut.h> //Windows FreeGlut equivalent:一种实用工具箱
这里插入了三个库函数:
1.GLtools.h:这个库是随着《OpenGL超级宝典》出现的,是一个工具箱库函数。GLTools中包含了一个用于操作矩阵和向量和3D数学库,并依靠GLEW获得OpenGL3.3中用来产生和渲染一些简单3D对象的函数,以及对视觉平截头体、相机类和变换矩阵进行管理的函数的充分支持。
2.GLShaderManager.h:这个也是《OpenGL超级宝典》中的头文件,其中包含了着色器管理器。没有着色器我们就不能再OpenGL(核心框架)中进行着色。着色器管理器不仅允许我们创建并管理着色器,还提供一组“存储着色器”(Stock Shader),它们能够进行一些初步和基本的渲染操作。
3.GL/glut.h:这是一个静态库,首先在前面添加FREEGLUT_STATI处理器宏。GLUT是OpenGL的实用工具箱,目的是帮助人们学习和编写OpenGL程序,而不必为任何平台特定环境的细枝末节而分神。
主函数分析
1 int main(int argc,char* argv[]) 2 { 3 gltSetWorkingDirectory(argv[0]); //设置当前工作目录 4 5 //接下来,进行一些基于GLUT的标准设置 6 glutInit(&argc,argv); //传输命令行参数,并初始化GLUT库 7 glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL); //告诉GLUT库,在创建窗口时要使用哪种显示模式。GLUT_DOUBLE:双缓冲窗口 GLUT_RGBA:RGBA颜色模式 GLUT_DEPTH:将一个深度缓冲区分配为显示的一部分 GLUT_STENCIL:确保我们也会有一个可用的模板缓冲区。 8 //其中深度缓冲区可用于进行深度测试,存储了屏幕上每个像素的深度值。 9 glutInitWindowSize(800,600); //设置窗口的大小 10 glutCreateWindow("Triangle"); //创建以“Triangle”为标题的窗口 11 //回调函数 12 glutReshapeFunc(ChangeSize); //为窗口改变大小而设置一个回调函数,以便能够设置视点 13 glutDisplayFunc(RenderScene); //注册一个函数以包含OpenGL渲染代码 14 15 //在开始运行主消息循环之前,还要解决两个问题 16 GLenum err=glewInit(); //初始化GLEW库。重新调用GLEW库初始化OpenGL驱动程序中所有丢失的入口点,以确保OpenGL API对我们来说完全可用。 17 if(GLEW_OK!=err) //在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。 18 { 19 fprintf(stderr,"GLEW Error:%s\n",glewGetErrorString(err)); 20 return 1; 21 } 22 SetupRC(); //调用SetupRC。这里RC代表渲染环境(Rendering Context),这是一个运行中的OpenGL状态机的句柄。在任何OpenGL函数起作用之前必须创建一个渲染环境。 23 24 //开始主消息循环并结束main函数 25 glutMainLoop(); 26 return 0; 27 }
和基本的win32程序一样,OpenGL的程序是从main函数开始运行的。
1.设置程序的默认工作目录
首先设置程序默认的工作目录,在Windows当中,默认的工作目录实际上就是与程序的可执行执行程序相同的目录。
2.GLUT的标准设置
GLUT的初始化操作
向GLUT传递命令行参数,并初始化GLUT库:glutInit(&argc,argv);
显示模式设置
告诉GLUT库,在创建窗口时要使用哪种类型的显示模式:glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGBA|GLUT_DEPTH|GLUT_STENCIL);
创建窗口
首先调用glutInitWindowSize(800,600);函数初始化一个大小为800*600的显示窗体;
然后调用glutCreateWindow("Triangle");来创建一个名为Triangle的显示窗体。
定义不同事件的回调函数
GLUT内部运行着一个本地消息循环,拦截适当的消息,然后调用我们为不同事件注册的回调函数。与使用真正的系统特定框架相比有一定的局限性,但是大大简化了组织并运行一个程序的过程,并且支持一个演示框架的最低限度的事件。
在这里,我们必须为窗体改变大小而设置一个回调函数,以便能够设置视点:glutReshapeRunc(ChangeSize);
此后,我们再为渲染窗体设置一个回调函数:glut(RenderScene);
3.初始化GLEW库
重新调用OpenGL库初始化OpenGL驱动程序中所有丢失的入口点,以确保OpenGL API对我们来说完全可用。调用glewInit一次就能完成这一步,在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。
这一步在调用主消息循环之前必须完成。
1 GLenum err=glewInit(); //初始化GLEW库。重新调用GLEW库初始化OpenGL驱动程序中所有丢失的入口点,以确保OpenGL API对我们来说完全可用。 2 if(GLEW_OK!=err) //在试图做任何渲染之前,还要检查确定驱动程序的初始化过程中没有出现任何问题。 3 { 4 fprintf(stderr,"GLEW Error:%s\n",glewGetErrorString(err)); 5 return 1; 6 }
4.OpenGL渲染环境的初始化
最后一项准备工作就是调用SetupRC,在实际开始渲染之前,我们在这里进行任何OpenGL初始化都非常方便。这里的RC代表渲染环境(Rendering Context),这是一个运行中的OpenGL状态机的句柄。在任何OpenGL函数起作用之前必须创建一个渲染环境,而GLUT在我们第一次创建窗口时完成了这项工作。我们实际上在这里进行预加载纹理,建立几何图形、渲染器等工作。
5.开启主消息循环
调用glutMainLoop()函数,开启主消息循环。
GlutMainLoop函数被调用之后,在主窗口被关闭之前都不会返回,并且一个应用程序中只需调用一次。这个函数负责处理所有操作系统特定的消息、按键动作等,直到我们关闭程序为止。它还能确保我们注册的这些函数被正确地调用。
子函数分析
1. 全局变量
这里声明了两个全局变量,一个用于构建三角形顶点的批次,一个用于存储着色器。
1 GLBatch triangleBatch; //声明一个简单的GLTool封装类,用于将三角形顶点批次进行封装
2 GLShaderManager shaderManager; //简单的存储着色器实例
2. ChangeSize函数
该函数在窗口大小改变的同时,修改坐标系间的映射关系。
ChangeSize函数在窗口大小改变时接受新的宽度和高度。我们可以使用这个信息,在OpenGL函数glViewport的帮助下修改从目的坐标系到屏幕坐标系上的映射。
3. RenderScene函数
当窗口发生变化时,该函数用以重绘窗口。
首先glClear清除一个或一组特定的缓冲区。颜色缓冲区、深度缓冲区、模板缓冲区。glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT|GL_STENCIL_BUFFER_BIT);
然后设置着色器。
这里使用着色器管理器申请了一个单位(Identity)着色器。使用红色背景来填充这个单位着色器。
1 GLfloat vRed[]={1.0f,0.0f,0.0f,1.0f}; 2 shaderManager.UseStockShader(GLT_SHADER_IDENTITY,vRed); //UseStockShader函数会选择一个存储着色器并提供这个着色器的Uniform值。 3 triangleBatch.Draw(); //指示将几何图形提交到着色器,绘制批次容器所描述的内容。
单位着色器只是简单地使用默认的笛卡尔坐标系(在所有坐标轴的坐标范围都是-1.0~1.0)。所有片段都应用同一颜色,几何图形为实心和未渲染的。这种着色器只能使用一个GLT_ATTRIBUTE_VERTEX属性。vColor参数包含了要求的颜色。
最后,调用glutSwapBuffers函数指定双缓冲渲染环境。这就意味着将在后台缓冲区进行渲染,然后在结束时交换到前台。这种形式能够防止观察者看到可能伴随着动画帧与动画帧之间闪烁的渲染过程。缓冲区交换将以平台特定的方式进行,但是GLUT的这个函数可以完成这项工作。
glutSwapBuffers();
4. SetupRC函数
首先设置用来进行窗口清除的颜色,即几何图形的颜色。glClearColor(0.0f,0.0f,1.0f,1.0f);
然后绘制顶点三角形,首先是顶点数组GLfloat[]。
紧接着使用批次管理器来构建三角形批次,
1 //下面代码建立了一个三角形的批次,仅包含3个顶点。 2 triangleBatch.Begin(GL_TRIANGLES,3); //批次容器的初始化,告诉这个类它代表哪种图元,其中包括顶点数,以及(可选)一组或两组纹理坐标。 3 triangleBatch.CopyVertexData3f(vVerts); //然后至少复制一个由三分量顶点组成的数组。 4 triangleBatch.End(); //表示已经完成了数据复制工作,并且将设置内部标记,以通知这个类包含哪些属性。