计算机图形学·5 OpenGL编程2 完整程序
本文是记录专业课“计算机图形学”的部分笔记,参考教材为Angel的第八版交互式计算机图形学——基于WebGL 2.0的自顶向下方法。
1、事实上,大多数OpenGL程序具有相似的结构,由以下函数组成。①main():定义了回调函数、打开一个或多个具有所需属性的窗口、进入事件循环(最后一条可执行语句);②init():设置状态变量如Viewing、Attributes;③callbacks如Display function显示函数、Input functions交互函数、window functions窗口函数。
2、我们先实现和上节最简单程序一样的输出,但是补上其缺省的状态值。先看main.c:

其中,①glutInit允许应用程序获取命令行参数并初始化系统。②gluInitDisplayMode请求了窗口(渲染上下文)的属性,如RGB颜色/RGBA颜色,单缓冲/双缓冲,属性按逻辑或的规则(|)组合在一起。③glutInitWindowSize以像素为单位定义窗口大小。④glutInitWindowPosition让窗口从左上角开始显示。⑤glutCreateWindow创建了一个标题为“simple”的窗口。⑥glutDisplayFunc指定了显示回调函数。⑦glutReshapeFunc则定义了改变窗口大小的时候的回调函数。⑧glutMainLoop使之进入无穷事件循环。
然后再来看init.c:

在这里,①glShadeModel(GL_SMOOTH); 定义了多边形表面的颜色计算方式,可选值为GL_SMOOTH(平滑着色,产生逼真的渐变光照效果)和GL_FLAT(平面着色,产生块状、卡通效果)。②glMatrixMode (GLenum mode); 的参数mode指定哪一个矩阵堆栈是下一个矩阵操作的目标,可选值为GL_MODELVIEW,GL_PROJECTION,GL_TEXTURE。③glLoadIdentity (); 用于重置当前指定的矩阵(在这里是投影矩阵)为单位矩阵。
3、需要特别说明glOrtho(-1.0, 1.0, -1.0, 1.0, -1.0, 1.0); 它定义了一个视见体(一个不可见的立方体区域,只有在这个区域内的物体才会被显示出来,区域外的物体将被“裁剪”掉),其左边界在x轴的-1.0处、右边界在x轴的1.0、下边界在y轴的-1.0、上边界在y轴的1.0、近边界在z轴的-1.0处(注:OpenGL的默认设置中,相机朝向负Z轴)、远边界在z轴的1.0。
也可以认为这个函数的操作是创建一个正投影矩阵,并且用这个矩阵乘以当前矩阵。其中,近裁剪平面是一个矩形,矩形左下角点三维空间坐标是(left,bottom,-near),右上角点是(right,top,-near);远裁剪平面也是一个矩形,左下角点空间坐标是(left,bottom,-far),右上角点是(right,top,-far)。
这生成的正投影矩阵是一个缩放和平移矩阵,其作用是将视见体的中心 `( (left+right)/2, (bottom+top)/2, (-near-far)/2 )`平移至原点 `(0,0,0)`、将视见体在X, Y, Z方向上分别缩放,使其恰好适应 [-1, 1] 的立方体范围。当这个变换完成后,任何不在 [-1, 1] 范围内的顶点(即原视见体之外的顶点)就很容易被识别和裁剪掉


4、glVertex中的units由应用程序确定,被称为世界坐标(object or problem coordinates),观察参数(viewing specifications)同样采用物体坐标系,而视见体的大小决定了图像中会显示什么内容,在这里是右手系统。在内部,OpenGL会将世界坐标转换为相机坐标(camera coordinates),然后切换到屏幕坐标,此外还会使用了一些通常对应用程序不可见的内部表示。
5、从之前的讲解可以看到,OpenGL将一个相机放置在对象空间的原点,并使其指向负z方向;默认的视见体(viewing volume)是一个以原点为中心的立方体,边长为2。而在默认的正交视图中,点沿着z轴向前投影到z=0的平面上,如下图所示:

6、在OpenGL中,投影是通过投影矩阵(变换)来执行的,由于只有一组变换函数,因此我们必须先设置矩阵模式。当调用 glMatrixMode(GL_PROJECTION)后,所有后续的矩阵操作(如 glLoadIdentity()、glOrtho()、gluPerspective())都将作用于投影矩阵堆栈。不过由于变换函数是递增的,因此我们从单位矩阵开始,即glLoadIdentity() 之后再使用正交投影glOrtho或透视投影glFrustum/gluPerspective。
7、需要补充的是,在glOrtho(left, right, bottom, top, near, far)中,近距和远距是从相机测量的。而如果是二维呢?在那里,二维顶点命令将所有顶点放置在z=0的平面上,我们可以使用没有前、后裁剪面的函数 gluOrtho2D(left, right, bottom, top)。其中,视图或裁剪体积会变成一个裁剪窗口。
8、基本图元如下图所示。需要注意的就是GL_TRIANGLES(每3个顶点一个三角形)、GL_TRIANGLE_STRIP(高效绘制相连三角形)、GL_TRIANGLE_FAN(扇形三角形)。

这里我们必须提到多边形问题(Polygon Issues)。OpenGL只会正确显示满足以下条件的多边形:①Simple简单性(边不相交);②Convex凸性(多边形中两点之间线段上的所有点也都在该多边形内);③Flat平面性(所有顶点都在同一平面上)。

9、补充一些PPT略过的内容:①光滑的曲面(如球体)无法被完美表示,需要使用三角形网格来近似曲面的形状,增加多边形的数量可以提高模型平滑度、但也会增加计算开销。②三角化是将一个复杂的简单多边形分解为一组三角形的过程(图形硬件渲染三角形的效率最高)。对于凸多边形,OpenGL会自动进行三角化;对于凹多边形或带孔洞的多边形,需要开发者使用专门的库或工具处理。③OpenGL本身没有直接绘制文本的函数,需要使用位图字体/笔画字体/纹理贴图字体/GLUT工具库来实现。
10、Attributes属性决定了图元的外观,主要包括①颜色(可为点、线、多边形的正背面分别设置);②大小和宽度;③点画模式(如虚线、点线等);④多边形模式(GL_FILL默认模式,显示为实心填充;GL_LINE只显示多边形的边线,用于线框模式显示;GL_POINT只显示多边形的顶点)。
11、Color Model可以先回顾第2节·图像形成的加性基色(RGB)、减性基色(CMY)和补色。在RGB颜色中,每个颜色分量都单独存储在帧缓冲区中,缓冲区中每个组件通常为8位。注意,在glColor3f中,颜色值的范围从0.0(无)到1.0(全部);而在glColor3ub中,值的颜色范围为0到255,这是一个不带符号的8位整数。
也可以采用Indexed Color 查色表颜色的方式。在这里,颜色是RGB值表中的索引,内存需求少、索引通常为8位,如下图所示:

12、在设置颜色属性时,由glColor设置的颜色将成为状态的一部分,并将一直使用直至被更新。注意,颜色和其他属性不是对象的一部分,而是在渲染对象时分配的。我们可以通过代码创建概念性的顶点颜色,例如glColor glVertex … … … glColor glVertex。
而平滑着色和平面着色非常好理解,如下图。默认的是平滑着色glShadeModel(GL_SMOOTH)
,OpenGL在可见多边形之间插值顶点颜色;而平面着色glShadeModel(GL_FLAT)以第一个顶点的颜色确定填充色。

13、最后还有控制函数,比如GLTOOLS、GLEE(GL Easy Extension library)。GLEE是一个免费的跨平台扩展加载库,为最高达3.0版本的OpenGL函数以及近400个扩展提供了无缝支持。常见函数比如:


简单程序的结构:GLUT——Main(Set up our windows、Callback)——Display(Graphics output)——Myinit
