1、OpenGL与3D图形世界
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.二、OpenGL提供直观的三维图形开发环境 OpenGL其实是一种图形与硬件的接口。它包括了120个图形函数,开发者能够用这些函数来创建三维模型和进行三维实时交互。与其余图形程序设计接口不一样,OpenGL提供了十分清晰明了的图形函数,所以初学的程序设计员也能利用OpenGL的图形处理能力和1670万种色彩的调色板很快地设计出三维图形以及三维交互软件。 OpenGL强有力的图形函数不要求开发者把三维物体模型的数据写成固定的数据格式,这样开发者不但能够直接使用本身的数据,并且能够利用其余不一样格式的数据源。这种灵活性极大地节省了开发者的时间,提升了软件开发效益。 长期以来,从事三维图形开发的技术人员都不得不在本身的程序中编写矩阵变换、外部设备访问等函数,这样为调制这些与本身的软件开发目标关系并不十分密切的函数费脑筋,而OpenGL正是提供一种直观的编程环境,它提供的一系列函数大大地简化了三维图形程序。例如:程序员
1.三、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已成为目前的三维图形开发标准,是从事三维图形开发工做的技术人员所必须掌握的开发工具。
2、OpenGL概念创建 算法
相应函数 | 具体说明 |
OpenGL实用库 | 43个函数,每一个函数以glu开头。 |
OpenGL辅助库 | 31个函数,每一个函数以aux开头。 |
Windows专用库函数(WGL) | 6个函数,每一个函数以wgl开头。 |
Win32 API函数 | 5个函数,函数前面没有专用前缀。 |
4、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程序基本结构为定义窗口、清理窗口、绘制物体、结束运行。
5、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 编程
6、OpenGL辅组库的基本使用
OpenGL是一个开放的系统,它是独立于任何窗口系统或操做系统的。尽管它包含了许多图形函数,但它却没有窗口函数,也没有从键盘和鼠标读取事件的函数,因此要初学者写出一个完整的图形程序是至关困难的。另外,OpenGL图形函数中只提供基本的几何原形:点、线、多边形,所以要建立基本的三维几何体如球、锥体等,也很不容易。而OpenGL辅助库就是为解决这些基本问题专门设计的,它提供了一些基本的窗口管理函数和三维图形绘制函数,能帮助初学者尽快进入OpenGL世界,掌握关键的三维图形技术,体会其中奇妙的乐趣。可是,对于复杂的应用,这些函数远远不够,只能做为参考。
6.一、辅助库函数分类
这一节内容能够做为手册查阅,初学者没必要深究。
辅助库函数大体分为六类:
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_九、 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.二、辅助库应用示例
下面举一个辅助库的应用例子,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(),即绘制物体。当窗口须要更新或物体发生改变时,程序便调用它从新绘制。以上例子是辅助库的最基本应用,复杂的应用将在后续的章节中详细介绍。
7、OpenGL建模
OpenGL基本库提供了大量绘制各类类型图元的方法,辅助库也提供了很多描述复杂三维图形的函数。这一章主要介绍基本图元,如点、线、多边形,有了这些图元,就能够创建比较复杂的模型了。
7.一、描述图元
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.二、绘制图元
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所示。
app
![]() |
|
图7-3 绘制多边形或一组顶点 |
点函数glBegin(GLenum mode)标志描述一个几何图元的顶点列表的开始,其参数mode表示几何图元的描述类型。全部类型及说明见表7-1所示,相应的图示见图7-4。
函数
类型 | 说明 |
GL_POINTS | 单个顶点集 |
GL_LINES | 多组双顶点线段 |
GL_POLYGON | 单个简单填充凸多边形 |
GL_TRAINGLES | 多组独立填充三角形 |
GL_QUADS | 多组独立填充四边形 |
GL_LINE_STRIP | 不闭合折线 |
GL_LINE_LOOP | 闭合折线 |
GL_TRAINGLE_STRIP | 线型连续填充三角形串 |
GL_TRAINGLE_FAN | 扇形连续填充三角形串 |
GL_QUAD_STRIP | 连续填充四边形串 |
表7-1 几何图元类型和说明
|
![]() |
|
图7-4 几何图元类型 |
函数glEnd()标志顶点列表的结束。
从图7-4中可看出,能够采用许多方法构造几何图元,这些方法仅仅依赖于所给的顶点数据。
在glBegin()和glEnd()之间最重要的信息就是由函数glVertex*()定义的顶点,必要时也可为每一个顶点指定颜色、法向、纹理坐标或其余,即调用相关的函数,见表7-2所示,具体用法之后会逐步介绍。
工具
函数 | 函数意义 |
glVertex*() | 设置顶点坐标 |
glColor*() | 设置当前颜色 |
glIndex*() | 设置当前颜色表 |
glNormal*() | 设置法向坐标 |
glEvalCoord*() | 产生坐标 |
glCallList(),glCallLists() | 执行显示列表 |
glTexCoord*() | 设置纹理坐标 |
glEdgeFlag*() | 控制边界绘制 |
glMaterial*() | 设置材质 |
表7-2 在glBegin()和glEnd()之间可调用的函数
|
看以下几句:
glBegin(GL_POINTS); glColor3f(1.0,0.0,0.0); /* red color */ glVertex(...); glColor3f(0.0,1.0,0.0); /* green color */ glColor3f(0.0,0.0,1.0); /* blue color */ glVertex(...); glVertex(...); glEnd();
颜色等的设置只对当前点或后续点有效。上一例中第一个点是红色,第二个点和第三个点都是蓝色。其中设置绿色时,以后没有顶点操做,而是设置蓝色,故只有当前蓝色对紧跟其后的两个顶点有效。
为了更好地理解构造几何图元函数的用法,下面举一个简单的例子:
例7-3 几何图元构造例程(drawgeom.c)
#include "glos.h" #include<GL/gl.h> #include<GL/glaux.h>
void myinit(void); void DrawMyObjects(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); glShadeModel(GL_FLAT); }
void CALLBACK myReshape(GLsizei w,GLsizei h) { glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity();
if(w<=h) glOrtho(-20.0,20.0,-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-50.0,50.0); else glOrtho(-20.0*(GLfloat)h/(GLfloat)w, 20.0*(GLfloat)h/(GLfloat)w,-20.0,20.0,-50.0,50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
void CALLBACK display(void) { glColor3f(1.0,1.0,0.0); DrawMyObjects(); glFlush(); }
void DrawMyObjects(void) { /* draw some points */ glBegin(GL_POINTS); glColor3f(1.0,0.0,0.0); glVertex2f(-10.0,11.0); glColor3f(1.0,1.0,0.0); glVertex2f(-9.0,10.0); glColor3f(0.0,1.0,1.0); glVertex2f(-8.0,12.0); glEnd();
/* draw some line_segments */ glBegin(GL_LINES); glColor3f(1.0,1.0,0.0); glVertex2f(-11.0,8.0); glVertex2f(-7.0,7.0); glColor3f(1.0,0.0,1.0); glVertex2f(-11.0,9.0); glVertex2f(-8.0,6.0); glEnd();
/* draw one opened_line */ glBegin(GL_LINE_STRIP); glColor3f(0.0,1.0,0.0); glVertex2f(-3.0,9.0); glVertex2f(2.0,6.0); glVertex2f(3.0,8.0); glVertex2f(-2.5,6.5); glEnd();
/* draw one closed_line */ glBegin(GL_LINE_LOOP); glColor3f(0.0,1.0,1.0); glVertex2f(7.0,7.0); glVertex2f(8.0,8.0); glVertex2f(9.0,6.5); glVertex2f(10.3,7.5); glVertex2f(11.5,6.0); glVertex2f(7.5,6.0); glEnd();
/* draw one filled_polygon */ glBegin(GL_POLYGON); glColor3f(0.5,0.3,0.7); glVertex2f(-7.0,2.0); glVertex2f(-8.0,3.0); glVertex2f(-10.3,0.5); glVertex2f(-7.5,-2.0); glVertex2f(-6.0,-1.0); glEnd();
/* draw some filled_quandrangles */ glBegin(GL_QUADS); glColor3f(0.7,0.5,0.2); glVertex2f(0.0,2.0); glVertex2f(-1.0,3.0); glVertex2f(-3.3,0.5); glVertex2f(-0.5,-1.0); glColor3f(0.5,0.7,0.2); glVertex2f(3.0,2.0); glVertex2f(2.0,3.0); glVertex2f(0.0,0.5); glVertex2f(2.5,-1.0); glEnd();
/* draw some filled_strip_quandrangles */ glBegin(GL_QUAD_STRIP); glVertex2f(6.0,-2.0); glVertex2f(5.5,1.0); glVertex2f(8.0,-1.0); glColor3f(0.8,0.0,0.0); glVertex2f(9.0,2.0); glVertex2f(11.0,-2.0); glColor3f(0.0,0.0,0.8); glVertex2f(11.0,2.0); glVertex2f(13.0,-1.0); glColor3f(0.0,0.8,0.0); glVertex2f(14.0,1.0); glEnd();
/* draw some filled_triangles */ glBegin(GL_TRIANGLES); glColor3f(0.2,0.5,0.7); glVertex2f(-10.0,-5.0); glVertex2f(-12.3,-7.5); glVertex2f(-8.5,-6.0); glColor3f(0.2,0.7,0.5); glVertex2f(-8.0,-7.0); glVertex2f(-7.0,-4.5); glVertex2f(-5.5,-9.0); glEnd();
/* draw some filled_strip_triangles */ glBegin(GL_TRIANGLE_STRIP); glVertex2f(-1.0,-8.0); glVertex2f(-2.5,-5.0); glColor3f(0.8,0.8,0.0); glVertex2f(1.0,-7.0); glColor3f(0.0,0.8,0.8); glVertex2f(2.0,-4.0); glColor3f(0.8,0.0,0.8); glVertex2f(4.0,-6.0); glEnd();
/* draw some filled_fan_triangles */ glBegin(GL_TRIANGLE_FAN); glVertex2f(8.0,-6.0); glVertex2f(10.0,-3.0); glColor3f(0.8,0.2,0.5); glVertex2f(12.5,-4.5); glColor3f(0.2,0.5,0.8); glVertex2f(13.0,-7.5); glColor3f(0.8,0.5,0.2); glVertex2f(10.5,-9.0); glEnd(); }
void main(void) { auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); auxInitPosition(0,0,500,500); auxInitWindow("Geometric Primitive Types"); myinit(); auxReshapeFunc(myReshape); auxMainLoop(display); }
以上程序运行结果就是图7-4所示的内容,这个例子很好地说明了几何图元的类型及颜色等函数的用法。但愿读者本身仔细分析每一个物体的绘制方法,体会其中的关键之处,达到触类旁通的效果。固然,还可利用上一章辅助库中提供的基本三维图元构造比较复杂的物体,你不妨也试一试。
8、OpenGL变换
OpenGL变换是本篇的重点内容,它包括计算机图形学中最基本的三维变换,即几何变换、投影变换、裁剪变换、视口变换,以及针对OpenGL的特殊变换概念理解和用法,如相机模拟、矩阵堆栈等。学好了这章,才开始真正走进三维世界。
8.一、从三维空间到二维平面
8.1.1 相机模拟
在真实世界里,全部的物体都是三维的。可是,这些三维物体在计算机世界中却必须以二维平面物体的形式表现出来。那么,这些物体是怎样从三维变换到二维的呢?下面咱们采用相机(Camera)模拟的方式来说述这个概念,如图8-1所示。