OpenGL基础图形编程
一、OpenGL与3D图形世界
1.1、OpenGL使人们进入三维图形世界
我们生活在一个充满三维物体的三维世界中,为了使计算机能精确地再现这些物体,我们必须能在三维空间描绘这些物体。我们又生活在一个充满信息的世界中,能否尽快地理解并运用这些信息将直接影响事业的成败,所以我们需要用一种最直接的形式来表示这些信息。
最近几年计算机图形学的发展使得三维表现技术得以形成,这些三维表现技术使我们能够再现三维世界中的物体,能够用三维形体来表示复杂的信息,这种技术就是可视化(Visualization)技术。可视化技术使人能够在三维图形世界中直接对具有形体的信息进行操作,和计算机直接交流。这种技术已经把人和机器的力量以一种直觉而自然的方式加以统一,这种革命性的变化无疑将极大地提高人们的工作效率。可视化技术赋予人们一种仿真的、三维的并且具有实时交互的能力,这样人们可以在三维图形世界中用以前不可想象的手段来获取信息或发挥自己创造性的思维。机械工程师可以从二维平面图中得以解放直接进入三维世界,从而很快得到自己设计的三维机械零件模型。医生可以从病人的三维扫描图象分析病人的病灶。军事指挥员可以面对用三维图形技术生成的战场地形,指挥具有真实感的三维飞机、军舰、坦克向目标开进并分析战斗方案的效果。
更令人惊奇的是目前正在发展的虚拟现实技术,它能使人们进入一个三维的、多媒体的虚拟世界,人们可以游历远古时代的城堡,也可以遨游浩翰的太空。所有这些都依赖于计算机图形学、计算机可视化技术的发展。人们对计算机可视化技术的研究已经历了一个很长的历程,而且形成了许多可视化工具,其中SGI公司推出的GL三维图形库表现突出,易于使用而且功能强大。利用GL开发出来的三维应用软件颇受许多专业技术人员的喜爱,这些三维应用软件已涉及建筑、产品设计、医学、地球科学、流体力学等领域。随着计算机技术的继续发展,GL已经进一步发展成为OpenGL,OpenGL已被认为是高性能图形和交互式视景处理的标准,目前包括ATT公司UNIX软件实验室、IBM公司、DEC公司、SUN公司、HP公司、Microsoft公司和
SGI公司在内的几家在计算机市场占领导地位的大公司都采用了OpenGL图形标准。
值得一提的是,由于Microsoft公司在 Windows NT中提供OpenGL图形标准,OpenGL将在微机中广泛应用,尤其是OpenGL三维图形加速卡和微机图形工作站的推出,人们可以在微机上实现三维图形应用,如CAD设计、仿真模拟、三维游戏等,从而更有机会、更方便地使用OpenGL及其应用软件来建立自己的三维图形世界。
1.2、OpenGL提供直观的三维图形开发环境
OpenGL实际上是一种图形与硬件的接口。它包括了120个图形函数,开发者可以用这些函数来建立三维模型和进行三维实时交互。与其他图形程序设计接口不同,OpenGL提供了十分清晰明了的图形函数,因此初学的程序设计员也能利用OpenGL的图形处理能力和1670万种色彩的调色板很快地设计出三维图形以及三维交互软件。
OpenGL强有力的图形函数不要求开发者把三维物体模型的数据写成固定的数据格式,这样开发者不但可以直接使用自己的数据,而且可以利用其他不同格式的数据源。这种灵活性极大地节省了开发者的时间,提高了软件开发效益。
长期以来,从事三维图形开发的技术人员都不得不在自己的程序中编写矩阵变换、外部设备访问等函数,这样为调制这些与自己的软件开发目标关系并不十分密切的函数费脑筋,而OpenGL正是提供一种直观的编程环境,它提供的一系列函数大大地简化了三维图形程序。例如:
- OpenGL提供一系列的三维图形单元供开发者调用。
- OpenGL提供一系列的图形变换函数。
- OpenGL提供一系列的外部设备访问函数,使开发者可以方便地访问鼠标、键盘、空间球、数据手套等这种直观的三维图形开发环境体现了OpenGL的技术优势,这也是许多三维图形开发者热衷于OpenGL的缘由所在。
OpenGL成为目前三维图形开发标准在计算机发展初期,人们就开始从事计算机图形的开发。直到计算机硬软件和计算机图形学高度发达的九十年代,人们发现复杂的数据以视觉的形式表现时是最易理解的,因而三维图形得以迅猛发展,于是各种三维图形工具软件包相继推出,如PHIGS、PEX、 RenderMan等。这些三维图形工具软件包有些侧重于使用方便,有些侧重于渲染效果或与应用软件的连接,但没有一种三维工具软件包在交互式三维图形建模能力、外部设备管理以及编程方便程度上能够OpenGL相比拟。
OpenGL经过对GL的进一步发展,实现二维和三维的高级图形技术,在性能上表现得异常优越,它包括建模、变换、光线处理、色彩处理、动画以及更先进的能力,如纹理影射、物体运动模糊等。OpenGL的这些能力为实现逼真的三维渲染效果、建立交互的三维景观提供了优秀的软件工具。OpenGL在硬件、窗口、操作系统方面是相互独立的。
许多计算机公司已经把 OpenGL集成到各种窗口和操作系统中,其中操作系统包括UNIX、Windows NT、DOS等,窗口系统有X窗口、Windows等。为了实现一个完整功能的图形处理系统,设计一个与OpenGL相关的系统结构为:其最底层是图形硬件,第二层为操作系统,第三层为窗口系统,第四层为OpenGL,第五层为应用软件。OpenGL是网络透明的,在客户 — 服务器(Client-Server)体系结构中,OpenGL允许本地和远程绘图。所以在网络系统中,OpenGL在X窗口、Windows或其它窗口系统下都可以以一个独立的图形窗口出现。
OpenGL作为一个性能优越的图形应用程序设计界面(API)而适合于广泛的计算环境,从个人计算机到工作站和超级计算机,OpenGL都能实现高性能的三维图形功能。由于许多在计算机界具有领导地位的计算机公司纷纷采用OpenGL作为三维图形应用程序设计界面,OpenGL应用程序具有广泛的移植性。因此,OpenGL已成为目前的三维图形开发标准,是从事三维图形开发工作的技术人员所必须掌握的开发工具。
二、OpenGL概念建立
<>function StorePage(){d=document;t=d.selection?(d.selection.type!="None"?d.selection.createRange().text:""):(d.getSelection?d.getSelection():"");void(keyit=window.open("http://www.365key.com/storeit.aspx?t="+escape(d.title)+"&u="+escape(d.location.href)+"&c="+escape(t),"keyit","scrollbars=no,width=475,height=575,left=75,top=20,status=no,resizable=yes"));keyit.focus(); 2.1、OpenGL基本理解
OpenGL是一个与硬件图形发生器的软件接口,它包括了100多个图形操作函数,开发者可以利用这些函数来构造景物模型、进行三维图形交互软件的开发。正如上一章所述,OpenGL是一个高性能的图形开发软件包。OpenGL支持网络,在网络系统中用户可以在不同的图形终端上运行程序显示图形。 OpenGL作为一个与硬件独立的图形接口,它不提供与硬件密切相关的设备操作函数,同时,它也不提供描述类似于飞机、汽车、分子形状等复杂形体的图形操作函数。用户必须从点、线、面等最基本的图形单元开始构造自己的三维模型。当然,象OpenInventor那样更高一级的基于OpenGL的三维图形建模开发软件包将提供方便的工具。因此OpenGL的图形操作函数十分基本、灵活。例如OpenGL中的模型绘制过程就多种多样,内容十分丰富,OpenGL提供了以下的对三维物体的绘制方式:
- 网格线绘图方式(wireframe)
这种方式仅绘制三维物体的网格轮廓线。
- 深度优先网格线绘图方式(depth_cued)
用网格线方式绘图,增加模拟人眼看物体一样,远处的物体比近处的物体要暗些。
- 反走样网格线绘图方式(antialiased)
用网格线方式绘图,绘图时采用反走样技术以减少图形线条的参差不齐。
- 平面消隐绘图方式(flat_shade)
对模型的隐藏面进行消隐,对模型的平面单元按光照程度进行着色但不进行光滑处理。
- 光滑消隐绘图方式(smooth_shade)
对模型进行消隐按光照渲染着色的过程中再进行光滑处理,这种方式更接近于现实。
- 加阴影和纹理的绘图方式(shadows、textures)
在模型表面贴上纹理甚至于加上光照阴影,使得三维景观象照片一样。
- 运动模糊的绘图方式(motion-blured)
模拟物体运动时人眼观察所感觉的动感现象。
- 大气环境效果(atmosphere-effects)
在三维景观中加入如雾等大气环境效果,使人身临其境。
- 深度域效果(depth-of-effects)
类似于照相机镜头效果,模型在聚焦点处清晰,反之则模糊。
2.2、OpenGL工作流程
整个OpenGL的基本工作流程如下图:
其中几何顶点数据包括模型的顶点集、线集、多边形集,这些数据经过流程图的上部,包括运算器、逐个顶点操作等;图像数据包括象素集、影像集、位图集等,图像象素数据的处理方式与几何顶点数据的处理方式是不同的,但它们都经过光栅化、逐个片元(Fragment)处理直至把最后的光栅数据写入帧缓冲器。在OpenGL中的所有数据包括几何顶点数据和象素数据都可以被存储在显示列表中或者立即可以得到处理。OpenGL中,显示列表技术是一项重要的技术。
OpenGL要求把所有的几何图形单元都用顶点来描述,这样运算器和逐个顶点计算操作都可以针对每个顶点进行计算和操作,然后进行光栅化形成图形碎片;对于象素数据,象素操作结果被存储在纹理组装用的内存中,再象几何顶点操作一样光栅化形成图形片元。
整个流程操作的最后,图形片元都要进行一系列的逐个片元操作,这样最后的象素值BZ送入帧缓冲器实现图形的显示。
2.3、OpenGL图形操作步骤
在上一节中说明了OpenGL的基本工作流程,根据这个流程可以归纳出在OpenGL中进行主要的图形操作直至在计算机屏幕上渲染绘制出三维图形景观的基本步骤:
1)根据基本图形单元建立景物模型,并且对所建立的模型进行数学描述(OpenGL中把:点、线、多边形、图像和位图都作为基本图形单元)。
2)把景物模型放在三维空间中的合适的位置,并且设置视点(viewpoint)以观察所感兴趣的景观。
3)计算模型中所有物体的色彩,其中的色彩根据应用要求来确定,同时确定光照条件、纹理粘贴方式等。
4)把景物模型的数学描述及其色彩信息转换至计算机屏幕上的象素,这个过程也就是光栅化(rasterization)。
在这些步骤的执行过程中,OpenGL可能执行其他的一些操作,例如自动消隐处理等。另外,景物光栅化之后被送入帧缓冲器之前还可以根据需要对象素数据进行操作。
三、WindowsNT下的OpenGL
3.1、Windows NT下的OpenGL函数
如前面的章节所述,Windows NT下的OpenGL同样包含100多个库函数,这些函数都按一定的格式来命名,即每个函数都以gl开头。Windows NT下的OpenGL除了具有基本的OpenGL函数外,还支持其他四类函数:
相应函数 | 具体说明 |
OpenGL实用库 | 43个函数,每个函数以glu开头。 |
OpenGL辅助库 | 31个函数,每个函数以aux开头。 |
Windows专用库函数(WGL) | 6个函数,每个函数以wgl开头。 |
Win32 API函数 | 5个函数,函数前面没有专用前缀。 |
在OpenGL中有115个核心函数,这些函数是最基本的,它们可以在任何OpenGL的工作平台上应用。这些函数用于建立各种各样的形体,产生光照效果,进行反走样以及进行纹理映射,进行投影变换等等。由于这些核心函数有许多种形式并能够接受不同类型的参数,实际上这些函数可以派生出300 多个函数。
OpenGL的实用函数是比OpenGL核心函数更高一层的函数,这些函数是通过调用核心函数来起作用的。这些函数提供了十分简单的用法,从而减轻了开发者的编程负担。OpenGL的实用函数包括纹理映射、坐标变换、多边形分化、绘制一些如椭球、圆柱、茶壶等简单多边形实体(本指南将详细讲述这些函数的具体用法)等。这部分函数象核心函数一样在任何OpenGL平台都可以应用。
OpenGL的辅助库是一些特殊的函数,这些函数本来是用于初学者做简单的练习之用,因此这些函数不能在所有的OpenGL平台上使用,在Windows NT环境下可以使用这些函数。这些函数使用简单,它们可以用于窗口管理、输入输出处理以及绘制一些简单的三维形体。为了使OpenGL的应用程序具有良好的移植性,在使用OpenGL辅助库的时候应谨慎。
6个WGL函数是用于连接OpenGL与Windows NT的,这些函数用于在Windows NT环境下的OpenGL窗口能够进行渲染着色,在窗口内绘制位图字体以及把文本放在窗口的某一位置等。这些函数把Windows与OpenGL揉合在一起。最后的5个Win32函数用于处理象素存储格式和双缓冲区,显然这些函数仅仅能够用于Win32系统而不能用于其它OpenGL平台。
3.2、OpenGL基本功能
OpenGL能够对整个三维模型进行渲染着色,从而绘制出与客观世界十分类似的三维景象。另外OpenGL还可以进行三维交互、动作模拟等。具体的功能主要有以下这些内容。
- 模型绘制
OpenGL能够绘制点、线和多边形。应用这些基本的形体,我们可以构造出几乎所有的三维模型。OpenGL通常用模型的多边形的顶点来描述三维模型。如何通过多边形及其顶点来描述三维模型,在指南的在后续章节会有详细的介绍。
- 模型观察
在建立了三维景物模型后,就需要用OpenGL描述如何观察所建立的三维模型。观察三维模型是通过一系列的坐标变换进行的。模型的坐标变换在使观察者能够在视点位置观察与视点相适应的三维模型景观。在整个三维模型的观察过程中,投影变换的类型决定观察三维模型的观察方式,不同的投影变换得到的三维模型的景象也是不同的。最后的视窗变换则对模型的景象进行裁剪缩放,即决定整个三维模型在屏幕上的图象。
- 颜色模式的指定
OpenGL 应用了一些专门的函数来指定三维模型的颜色。程序员可以选择二个颜色模式,即RGBA模式和颜色表模式。在RGBA模式中,颜色直接由RGB值来指定;在颜色表模式中,颜色值则由颜色表中的一个颜色索引值来指定。程序员还可以选择平面着色和光滑着色二种着色方式对整个三维景观进行着色。
- 光照应用
用OpenGL绘制的三维模型必须加上光照才能更加与客观物体相似。OpenGL提供了管理四种光(辐射光、环境光、镜面光和漫反射光)的方法,另外还可以指定模型表面的反射特性。
- 图象效果增强
OpenGL提供了一系列的增强三维景观的图象效果的函数,这些函数通过反走样、混合和雾化来增强图象的效果。反走样用于改善图象中线段图形的锯齿而更平滑,混合用于处理模型的半透明效果,雾使得影像从视点到远处逐渐褪色,更接近于真实。
- 位图和图象处理
OpenGL还提供了专门对位图和图象进行操作的函数。
- 纹理映射
三维景物因缺少景物的具体细节而显得不够真实,为了更加逼真地表现三维景物,OpenGL提供了纹理映射的功能。OpenGL提供的一系列纹理映射函数使得开发者可以十分方便地把真实图象贴到景物的多边形上,从而可以在视窗内绘制逼真的三维景观。
- 实时动画
为了获得平滑的动画效果,需要先在内存中生成下一幅图象,然后把已经生成的图象从内存拷贝到屏幕上,这就是OpenGL的双缓存技术(double buffer)。OpenGL提供了双缓存技术的一系列函数。
- 交互技术
目前有许多图形应用需要人机交互,OpenGL提供了方便的三维图形人机交互接口,用户可以选择修改三维景观中的物体。
OpenGL的作用机制是客户(client)/服务器(sever)机制,即客户(用OpenGL绘制景物的应用程序)向服务器(即OpenGL内核)发布OpenGL命令,服务器则解释这些命令。大多数情况下,客户和服务器在同一机器上运行。正是OpenGL的这种客户/服务器机制,OpenGL可以十分方便地在网络环境下使用。因此Windows NT下的OpenGL是网络透明的。正象Windows的图形设备接口(GDI)把图形函数库封装在一个动态链接库(Windows NT下的GDI32.DLL)内一样,OpenGL图形库也被封装在一个动态链接库内(OPENGL32.DLL)。受客户应用程序调用的OpenGL函数都先在OPENGL32.DLL中处理,然后传给服务器WINSRV.DLL。OpenGL的命令再次得到处理并且直接传给Win32的设备驱动接口(Device Drive Interface,DDI),这样就把经过处理的图形命令送给视频显示驱动程序。下图简要说明这个过程:
图3-1 OpenGL在Windows NT下运行机制
在三维图形加速卡的GLINT图形加速芯片的加速支持下,二个附加的驱动程序被加入这个过程中。一个OpenGL可安装客户驱动程序(Installable Client Driver,ICD)被加在客户这一边,一个硬件指定DDI(Hardware-specific DDI)被加在服务器这边,这个驱动程序与Wind32 DDI是同一级别的。
图3-2 在三维图形加速下OpenGL运行机制
四、OpenGL基础程序结构
用OpenGL编写的程序结构类似于用其他语言编写的程序。实际上,OpenGL是一个丰富的三维图形函数库,编写OpenGL程序并非难事,只需在基本C语言中调用这些函数,用法同Turbo C、Microsoft C等类似,但也有许多不同之处。
本指南所有的程序都是在Windows NT的Microsoft Visual C++集成环境下编译连接的,其中有部分头文件和函数是为这个环境所用的,例如判别操作系统的头文件“glos.h”。此外,为便于各类读者同时快速入门,在短时间内掌握OpenGL编程的基本方法和技巧,指南中例子尽量采用标准ANSI C调用OpenGL函数来编写,而且所有例程都只采用OpenGL附带的辅助库中的窗口系统。此外,这样也便于程序在各平台间移植,尤其往工作站UNIX 操作系统移植时,也只需改动头文件等很少很少的部分。下面列出一个简单的OpenGL程序:
例4-1 OpenGL简单例程(Simple.c)
#include <GL/gl.h>
#include <GL/glaux.h>
#include "glos.h"
void main(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,500,500);
auxInitWindow("simple");
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(1.0,0.0,0.0);
glRectf(-0.5,-0.5,0.5,0.5);
glFlush();
_sleep(1000);
}
这个程序运行结果是在屏幕窗口内画一个红色的方块。
下面具体分析整个程序结构:首先,在程序最开始处是OpenGL头文件:<GL/gl.h>、<GL/glaux.h>。前一个是gl库的头文件,后一个是辅助库的头文件。此外,在以后的几章中还将说明OpenGL的另外两个头文件,一个是<GL/glu.h>实用库的头文件,另一个是<GL/glx.h>X窗口扩充库的头文件(这个常用在工作站上)。接下来是主函数main()的定义:一般的程序结构是先定义一个窗口:
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,500,500);
auxInitWindow("simple");
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA)设置窗口显示模式为RGBA方式,即彩色方式,并且图形缓存为单缓存(SINGLE BUFFER)。 auxInitPosition(0, 0, 500, 500)定义窗口的初始位置,前两个参数(0, 0)为窗口的左上角点的屏幕坐标,后两个参数(500,500)为窗口的宽度和高度。auxInitWindow("simple")是窗口初始化,字符参数是窗口名称。
然后是窗口内清屏:
glClearColor(0.0,0.0,0.0,0.0); glClear(GL_COLOR_BUFFER_BIT);
第一句将窗口清为黑色,第二句将颜色缓冲区清为glClearColor(0.0, 0.0, 0.0, 0.0)命令所设置的颜色,即同窗口背景颜色一致。
再接着是在窗口内画一个物体:
glColor3f(1.0,0.0,0.0);
glRectf(-0.5,-0.5,0.5,0.5);
很明显,第一句设置物体颜色,函数中前三个参数分别为R、G、B值,最后一个参数是Alpha值,范围都从0至1;第二句绘制一个二维矩形。注意:OpenGL是针对三维图形而言,因此用作OpenGL编程绘制物体必须意识到任何一个物体都是三维的,具有空间性,而显示于屏幕上的物体都是三维物体在二维平面上的投影。
从表面上看,上述程序代码很简单,实际上已经用到了缺省的投影形式(正射投影)。再看glFlush()函数,表示强制绘图完成。最后一句_sleep(1000),参数单位为毫秒,整句意思是保持现有状况一秒钟,然后结束程序运行。这个函数是VC++的库函数。
总而言之,OpenGL程序基本结构为定义窗口、清理窗口、绘制物体、结束运行。
五、OpenGL的数据类型和函数名
OpenGL的数据类型定义可以与其它语言一致,但建议在ANSI C下最好使用以下定义的数据类型,例如GLint、GLfloat等。具体类型见表5-1。
前缀 数据类型 相应C语言类型 OpenGL类型
================================================================
b 8-bit integer signed char GLbyte
s 16-bit integer short GLshort
i 32-bit integer long GLint,GLsizei
f 32-bit floating-point float GLfloat,GLclampf
d 64-bit floating-point double GLdouble,GLclampd
ub 8-bit unsigned integer unsigned char GLubyte,GLboolean
us 16-bit unsigned integer unsigned short GLushort
ui 32-bit unsigned integer unsigned long GLuint,GLenum,GLbitfield
表5-1 命令前缀和参数数据类型
OpenGL的库函数命名方式很有规律,了解这种规律后阅读和编写程序都比较容易方便。
首先,每个库函数有前缀gl、glu、glx或aux,表示此函数分属于基本库、实用库、X窗口扩充库或辅助库,其后的函数名头字母大写,后缀是参数类型的简写,取i、f,参见表5-1。例:
glVertex2i(2,4);
glVertex3f(2.0,4.0,5.0);
注意:有的函数参数类型后缀前带有数字2、3、4。2代表二维,3代表三维,4代表alpha值(以后介绍)。
有些OpenGL函数最后带一个字母v,表示函数参数可用一个指针指向一个向量(或数组)来替代一系列单个参数值。下面两种格式都表示设置当前颜色为红色,二者等价。
glColor3f(1.0,0.0,0.0);
float color_array[]={1.0,0.0,0.0};
glColor3fv(color_array);
除了以上基本命名方式外,还有一种带“*”星号的表示方法,例如glColor*(),它表示可以用函数的各种方式来设置当前颜色。同理,glVertex*v()表示用一个指针指向所有类型的向量来定义一系列顶点坐标值。
最后,OpenGL也定义GLvoid类型,如果用C语言编写,可以用它替代void类型。
六、OpenGL辅组库的基本使用
OpenGL是一个开放的系统,它是独立于任何窗口系统或操作系统的。尽管它包含了许多图形函数,但它却没有窗口函数,也没有从键盘和鼠标读取事件的函数,所以要初学者写出一个完整的图形程序是相当困难的。另外,OpenGL图形函数中只提供基本的几何原形:点、线、多边形,因此要创建基本的三维几何体如球、锥体等,也很不容易。而OpenGL辅助库就是为解决这些基本问题专门设计的,它提供了一些基本的窗口管理函数和三维图形绘制函数,能帮助初学者尽快进入OpenGL世界,掌握关键的三维图形技术,体会其中奇妙的乐趣。但是,对于复杂的应用,这些函数远远不够,只能作为参考。
6.1、辅助库函数分类
这一节内容可以作为手册查阅,初学者不必深究。
辅助库函数大致分为六类:
6.1.1 窗口初始化和退出
相关函数有三个,它们在第一章已提到,这里将详细介绍:
void auxInitWindow(GLbyte *titleString)
打开一个由auxInitDisplayMode()和auxInitPosition()指定的窗口。函数参数是窗口标题,窗口背景缺省颜色是RGBA下的黑色或颜色表(color_index)下的0号调色板的颜色。按下Escape键可以完成关掉窗口、结束程序、全部清屏三项功能。
void auxInitDisplayMode(GLbitfield mask)
设置窗口显示模式。基本模式有RGBA或颜色表、单或双缓存,也可指定其他附加模式:深度、模板或累积缓存(depth,stencil,and/or accumulation buffer)。参数mask是一组位标志的联合(取或),AUX_RGBA或AUX_INDEX、AUX_SINGLE或AUX_DOUBLE,以及其它有效标志AUX_DEPTH、AUX_STENCIL或AUX_ACCUM。
void auxInitPosition(GLint x,GLint y,GLsizei width,GLsizei height)
设置窗口位置及大小。参数(x, y)为窗口的左上角点的屏幕坐标,参数(width, height)为窗口的宽度和高度,单位为象素,缺省值为(0, 0, 100, 100)。
6.1.2 窗口处理和事件输入
当窗口创建后,且在进入主函数循环之前,应当登记以下列出的回调函数(callback function):
void auxReshapeFunc(void(*function)(GLsizei,GLsizei))
定义窗口改变时形状重定函数。参数function是一个函数指针,这个函数带有两个参数,即窗口改变后的新宽度和新高度。通常,function是 glViewport(),显示裁减后的新尺寸,重定义投影矩阵,以便使投影后图像的比例与视点匹配,避免比例失调。若不调用 auxReshapeFunc(),缺省重定物体形状的函数功能是调用一个二维的正射投影矩阵。运用辅助库,窗口将在每个事件改变后自动重新绘制。
void auxKeyFunction(GLint key,void(*function)(void))
定义键盘响应函数。参数function就是当按下key键时所调用的函数指针,辅助库为参数key定义了几个常量:AUX_0至AUX_9、 AUX_A至AUX_Z、AUX_a至AUX_z、AUX_LEFT、AUX_RIGHT、AUX_UP、AUX_DOWN(方向键)、 AUX_ESCAPE、AUX_SPACE或AUX_RETURN。
void auxMouseFunc(GLint button,Glint mode,void(*function)(AUX_EVENTREC *))
定义鼠标响应函数。参数function就是当鼠标以mode方式作用于button时所调用的函数。参数button有 AUX_LEFTBUTTON、AUX_MIDDLEBUTTON或AUX_RIGHTBUTTON(以右手为标准)。参数mode代表鼠标触击状态,击中时为AUX_MOUSEDOWN,释放时为AUX_MOUSEUP。参数function必须带一个参数,它是指向结构AUX_EVENNTREC的指针。当函数auxMouseFunc()被调用时将为这个结构分配相应的内存。通常用法类似如下:
void function(AUX_EVENTREC *event)
{
GLint x,y;
x=event->data[AUX_MOUSEX];
y=event->data[AUX_MOUSEY];
...
}
6.1.3 颜色表装入
因为OpenGL本身没有窗口系统,所以依赖于窗口系统的颜色映射就没法装入颜色查找表。如果采用颜色表模式,就要用到辅助库提供的用RGB值定义的单个颜色索引函数:
void auxSetOneColor(GLint index,GLfloat red,GLfloat green,GLfloat blue)
设置自定义颜色的索引。参数index即索引号,参数red、green、blue分别为红、绿、蓝值,范围在(0~1)内。
6.1.4 三维物体绘制
每组三维物体包括两种形式:网状体(wire)和实心体(solid)。网状体没有平面法向,而实心体有,能进行光影计算,有光照时采用实心体模型。下面这些函数的 参数都是定义物体大小的,可以改变。
功能 | 函数 |
绘制球 | void auxWireSphere(GLdouble radius) void auxSolidSphere(GLdouble radius) |
绘制立方体 | void auxWireCube(GLdouble size) void auxSolidCube(GLdouble size) |
绘制长方体 | void auxWireBox(GLdouble width,GLdouble height,GLdouble depth) void auxSolidBox(GLdouble width,GLdouble height,GLdouble depth) |
绘制环形圆纹面 | void auxWireTorus(GLdouble innerRadius,GLdouble outerRadius) void auxSolidTorus(GLdouble innerRadius,GLdouble outerRadius) |
绘制圆柱 | void auxWireCylinder(GLdouble radius,GLdouble height) void auxSolidCylinder(GLdouble radius,GLdouble height) |
绘制二十面体 | void auxWireIcosahedron(GLdouble radius) void auxSolidIcosahedron(GLdouble radius) |
绘制八面体 | void auxWireOctahedron(GLdouble radius) void auxSolidOctahedron(GLdouble radius) |
绘制四面体 | void auxWireTetrahedron(GLdouble radius) void auxSolidTetrahedron(GLdouble radius) |
绘制十二面体 | void auxWireDodecahedron(GLdouble radius) void auxSolidDodecahedron(GLdouble radius) |
绘制圆锥 | void auxWireCone(GLdouble radius,GLdouble height) void auxSolidCone(GLdouble radius,GLdouble height) |
绘制茶壶 | void auxWireTeapot(GLdouble size) void aucSolidTeapot(GLdouble size) |
表6-1 |
以上物体均以各自中心为原点绘制,所有坐标都已单位化,可以缩放。
6.1.5 背景过程管理
void auxIdleFunc(void *func)
定义空闲状态执行函数。参数func是一个指针,指向所要执行的函数功能。当它为零时,func执行无效。
6.1.6 程序运行
void auxMainLoop(void(*displayFunc)(void))
定义场景绘制循环函数。displayFunc指针指向场景绘制函数。当窗口需要更新或场景发生改变时,程序便调用它所指的函数,重新绘制场景。
6.2、辅助库应用示例
下面举一个辅助库的应用例子,testaux.c:
例6-1 辅助库应用例程 testaux.c
#include "glos.h"
#include <GL/gl.h>
#include <GL/glaux.h>
void myinit(void);
void CALLBACK myReshape(GLsizei w,GLsizei h);
void CALLBACK display(void);
void myinit(void)
{
glClearColor(0.0,0.0,0.0,0.0);
glClear(GL_COLOR_BUFFER_BIT);
}
void CALLBACK myReshape(GLsizei w,GLsizei h)
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(-1.5,1.5,-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-10.0,10.0);
else
glOrtho(-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-1.5,1.5,-10.0,10.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void CALLBACK display(void)
{
glColor3f(1.0,1.0,0.0);
auxWireSphere(1.0);
glFlush();
}
void main(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
auxInitPosition(0,0,500,500);
auxInitWindow("AUX_SAMPLE");
myinit();
auxReshapeFunc(myReshape);
auxMainLoop(display);
}
图6-1 网状球体 |
以上程序运行结果是在屏幕窗口内绘制一个黄色的网状球体,这个程序充分体现了辅助库的基本应用方法。
首先,在主函数中用辅助库函数定义一个窗口auxInitWindow(),然后初始化颜色myinit(),这些在第一章中已说明。接下来是两个十分重要的函数 auxReshapeFunc()和auxMainLoop(),参数都是一个函数指针,指向的都是回调函数(回调函数定义用CALLBACK说明)。
前者是窗口形状重定函数,参数指针指向函数myReshape(),它的两个参数就是窗口的新宽度和新高度。然后用glViewport(0, 0, w, h)重定视口,并且在新视口内重新定义投影矩阵,
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(-1.5,1.5,-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-10.0,10.0);
else
glOrtho(-1.5*(GLfloat)h/(GLfloat)w, 1.5*(GLfloat)h/(GLfloat)w,-1.5,1.5,-10.0,10.0);
即先用glMatrixMode()说明当前矩阵操作与投影有关GL_PROJECTION,再用glLoadIdentity()将矩阵清为单位矩阵,避免受其它矩阵操作的干扰;然后调用glOrtho()对物体进行正射投影,并且用判断语句给出了两种情况,使投影后图像的比例与视点匹配,避免比例失调。
再下来调用glMatrixMode()将矩阵操作改为对观察物体有关的方式GL_MODELVIEW,同样用 glLoadIdentity()清矩阵。后者是主函数循环函数,参数指针指向函数display(),即绘制物体。当窗口需要更新或物体发生改变时,程序便调用它重新绘制。以上例子是辅助库的最基本应用,复杂的应用将在后续的章节中详细介绍。
七、OpenGL建模
OpenGL基本库提供了大量绘制各种类型图元的方法,辅助库也提供了不少描述复杂三维图形的函数。这一章主要介绍基本图元,如点、线、多边形,有了这些图元,就可以建立比较复杂的模型了。
7.1、描述图元
OpenGL是三维图形的函数库,它所定义的点、线、多边形等图元与一般的定义不太一样,存在一定的差别。对编程者来说,能否理解二者之间的差别十分重要。一种差别源于基于计算机计算的限制。OpenGL中所有浮点计算精度有限,故点、线、多边形的坐标值存在一定的误差。另一种差别源于位图显示的限制。以这种方式显示图形,最小的显示图元是一个象素,尽管每个象素宽度很小,但它们仍然比数学上所定义的点或线宽要大得多。当用OpenGL 进行计算时,虽然是用一系列浮点值定义点串,但每个点仍然是用单个象素显示,只是近似拟合。
OpenGL图元是抽象的几何概念,不是真实世界中的物体,因此须用相关的数学模型来描述。
7.1.1 齐次坐标(Homogeneous Coordinate)
在空间直角坐标系中,任意一点可用一个三维坐标矩阵[x y z]表示。如果将该点用一个四维坐标的矩阵[Hx Hy Hz H]表示时,则称为齐次坐标表示方法。在齐次坐标中,最后一维坐标H称为比例因子。
在OpenGL中,二维坐标点全看作三维坐标点,所有的点都用齐次坐标来描述,统一作为三维齐次点来处理。每个齐次点用一个向量(x, y, z, w)表示,其中四个元素全不为零。齐次点具有下列几个性质:
1)如果实数a非零,则(x, y, x, w)和(ax, ay, az, aw)表示同一个点,类似于x/y = (ax)/( ay)。
2)三维空间点(x, y, z)的齐次点坐标为(x, y, z, 1.0),二维平面点(x,y)的齐次坐标为(x, y, 0.0, 1.0)。
3)当w不为零时,齐次点坐标(x, y, z, w)即三维空间点坐标(x/w, y/w, z/w);当w为零时,齐次点(x, y, z, 0.0)表示此点位于某方向的无穷远处。
注意:OpenGL中指定w大于或等于0.0。
7.1.2 点(Point)
用浮点值表示的点称为顶点(Vertex)。所有顶点在OpenGL内部计算时都作为三维点处理,用二维坐标(x, y)定义的点在OpenGL中默认z值为0。所有顶点坐标用齐次坐标(x, y, z, w) 表示,如果w不为0.0,这些齐次坐标表示的顶点即为三维空间点(x/w, y/w, z/w)。编程者可以自己指定w值,但很少这样做。一般来说,w缺省为1.0。
7.1.3 线(Line)
在OpenGL中,线代表线段(Line Segment),不是数学意义上的那种沿轴两个方向无限延伸的线。这里的线由一系列顶点顺次连结而成,有闭合和不闭合两种。见图7-1所示。
图7-1 线段的两种连结方式 |
7.1.4 多边形(Polygon)
OpenGL中定义的多边形是由一系列线段依次连结而成的封闭区域。这些线段不能交叉,区域内不能有空洞,多边形必须在凸多边形,否则不能被OpenGL函数接受。合法和非法多边形图示见图7-2。
图7-2 合法和非法多边形 |
OpenGL多边形可以是平面多边形,即所有顶点在一个平面上,也可以是空间多边形。更复杂的多边形将在提高篇中介绍。
7.2、绘制图元
7.2.1 定义顶点
在OpenGL中,所有几何物体最终都由有一定顺序的顶点集来描述。
函数glVertex{234}{sifd}[v](TYPE coords)可以用二维、三维或齐次坐标定义顶点。举例如下:
glVertex2s(2,3);
glVertex3d(0.0,1.0,3.1414926535);
glVertex4f(2.4,1.0,-2.2,2.0);
GLfloat pp[3]={5.0,2.0,10.2};
glVertex3fv(pp);
第一例子表示一个空间顶点(2, 3, 0),第二个例子表示用双精度浮点数定义一个顶点,第三个例子表示用齐次坐标定义一个顶点,其真实坐标为(1.2, 0.5, -1.1),最后一个例子表示用一个指针(或数组)定义顶点。
7.2.2 构造几何图元
在实际应用中,通常用一组相关的顶点序列以一定的方式组织起来定义某个几何图元,而不采用单独定义多个顶点来构造几何图元。在OpenGL中,所有被定义的顶点必须放在glBegain()和glEnd()两个函数之间才能正确表达一个几何图元或物体,否则,glVertex*()不完成任何操作。如:
glBegin(GL_POLYGON);
glVertex2f(0.0,0.0);
glVertex2f(0.0,3.0);
glVertex2f(3.0,3.0);
glVertex2f(4.0,1.5);
glVertex2f(3.0,0.0);
glEnd();
以上这段程序定义了一个多边形,如果将glBegin()中的参数GL_POLYGON改为GL_POINTS,则图形变为一组顶点(5个),见图7-3所示。
图7-3 绘制多边形或一组顶点 |
点函数glBegin(GLenum mode)标志描述一个几何图元的顶点列表的开始,其参数mode表示几何图元的描述类型。所有类型及说明见表7-1所示,相应的图示见图7-4。
类型 | 说明 |
GL_POINTS | 单个顶点集 |
GL_LINES | 多组双顶点线段 |
GL_POLYGON | 单个简单填充凸多边形 |
GL_TRAINGLES | 多组独立填充三角形 |