第10章 OpenGL
简介
OpenGL基本程序结构
OpenGL程序设计入门
简介
什么是OpenGL?
OpenGL是一个功能强大的开放图形库(Open Graphics Library)。其前身是SGI公司为其图形工作站开发的IRIS GL。为使其能够更加容易地移植到不同的硬件和操作系统,SGI开发了OpenGL。
目前, OpenGL已成为开放的国际图形标准。
OpenGL的特点
从程序开发人员的角度来看,OpenGL是一组绘图命令的API集合。利用这些API能够方便地描述二维和三维几何物体,并控制这些物体按某种方式绘制到显示缓冲区中。OpenGL的API集提供了物体描述、平移、旋转、缩放、光照、纹理、材质、像素、位图、文字、交互以及提高显示性能等方面的功能,基本涵盖了开发二、三维图形程序所需的各个方面。与一般的图形开发工具相比,OpenGL具有以下几个突出特点:
(1)跨平台特性
OpenGL与硬件、窗口和操作系统是相互独立的。为了构成一个完整功能的图形处理系统,其设计实现共分 5 层:图形硬件、操作系统、窗口系统、 OpenGL和应用软件。
因而, OpenGL可以集成到各种标准窗口和操作系统中。例如,操作系统包括UNIX,Windows NT, Windows 95/98, DOS等;窗口系统包括X Windows, Microsoft Windows等。
(2)应用的广泛性
OpenGL是目前最主要的二、三维交互式图形应用程序开发环境,已成为业界最受推荐的图形应用编程接口。自从1992年发表以来,OpenGL已被广泛地应用于CAD/CAM、三维动画、数字图像处理以及虚拟现实等领域,Kinetix公司的3D Studio Max就是突出的代表。无论是在PC机上,还是在工作站甚至是大型机和超级计算机上,OpenGL都能表现出它的高性能和强大威力。
(3)网络透明性
建立在客户/服务器模型上的网络透明性是OpenGL的固有特性,它允许一个运行在工作站上的进程在本机或通过网络在远程工作站上显示图形。利用这种性质能够均衡各工作站的工作负荷,共同承担图形应用任务。
(4)高质量和高性能
无论是在CAD/CAM、三维动画还是可视化仿真等领域,OpenGL高质量和高效率的图形生成能力都能得到充分的体现。在这些领域中,开发人员可以利用OpenGL制作出效果逼真的二、三维图像来。
(5)出色的编程特性
OpenGL在各种平台上已有多年的应用实践,加上严格的规范控制,因此OpenGL具有良好的稳定性。
OpenGL具有充分的独立性与易使用性等。
OpenGL基本程序结构
Windows 95/98以及Windows NT 以上的操作系统中提供了OpenGL的动态库,在VC++ 以上的版本中提供了OpenGL的静态库,所以,使用OpenGL编程,在微机上使用时,最好是在上述软件环境中编写OpenGL程序。
在微机版本中,OpenGL 提供了三个函数库,它们是基本库、实用库和辅助库。
Windows系统下的OpenGL函数
OpenGL的基本库是OpenGL的核心函数库,在这个函数库中,提供了115个函数,这些函数都是以“gl”为前缀。这类API的主要功能包括物体描述、平移、旋转、缩放、光照、纹理、材质、像素、位图、文字处理等。
所有OpenGL 提供的操作都可以使用这些函数来实现,而且,对于不同的软件和硬件平台,这些函数的使用是完全相同的,这个特性注定了OpenGL程序完美的可移植性。
OpenGL的实用库是OpenGL基本库的一套子程序,它提供了43个函数,这些函数都是以”glu”为前缀。基本的OpenGL不支持传统上同图形标准相关的一些几何对象,为了减少一些编程负担,OpenGL提供了实用库。主要功能包括绘制二次曲面、NURBS曲线曲面、复杂多边形以及纹理、矩阵管理等。
实用库中的所有函数全都是由OpenGL基本库函数来编写的,所以,在使用上和OpenGL基本库的使用是完全相同的,而且,用户也可以使用基本函数库来实现实用库的函数功能。
OpenGL的辅助库是为了方便用户用标准C编写OpenGL程序而编写的。OpenGL是一个图形标准,所以,在OpenGL中没有提供窗口管理和消息事件响应的函数,这样使用标准C 编写OpenGL程序是很不方便的,所以提供了辅助库。它提供了31个函数,这些函数都是以“aux”为前缀。OpenGL辅助库提供了一些基本的窗口管理函数、事件处理函数和一些简单模型的制作函数等,例如,定义窗口的大小、处理键盘时间、鼠标击键事件、绘制多面体等等。
一个简单的OpenGL程序
下面将通过一个简单的OpenGL 程序来说明OpenGL头文件的使用、语法规则、程序的基本结构、程序的运行环境配置。
例 OpenGL 例程
#include <>
#include <GL/>
#include <GL/>
#include <GL/>
#include <>
void myinit(void);
void CALLBACK myReshape(int w, int h);
void CALLBACK display(void);
void myinit(void) //初始化
{glClearColor(,,,); //将窗口清为黑色
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色,即背景色
glColor4f(,,,); //选颜色(R,G,B)
glRotatef(30,,,); //做旋转变换
auxWireCube(); //绘制六面体的虚线图
glFlush(); //强制绘图,不驻留缓存
}
void CALLBACK myReshape (int w, int h) //用于窗口大小改变时的处理,与绘图无关
{glViewport(0,0,w,h);
}
void main(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA); //窗口显示单缓存和RGB(彩色)模式
auxInitPosition(0,0,200,200); //大小 x=200 y=200 (0,0)是屏幕左上点
auxInitWindow(“”); //初始化窗口,参数是标题
myinit();
auxReshapeFunc(myReshape);
auxMainLoop(display);
}
1. 头文件使用
若应用程序使用OpenGL核心函数,应包括头文件<GL/> ;
使用GLU库函数,应包括头文件<GL/> ;
使用AUX库函数,应包括头文件<GL/> ;
使用WGL和Win32应包括头文件<>。
2. 回调(CALLBACK)函数
CALLBACK函数是一些用来让系统调用的函数,系统调用它们来实现显示、接受输入事件功能。
3. 语法规则
OpenGL基本库的所有操作函数都是以“gl”为前缀的。实用库的所有操作函数都是以“glu”为前缀。辅助库的所有操作函数都是以“aux”为前缀的。OpenGL命令带有后缀。
以中的glColor4f为例,前缀“gl”指这个函数是OpenGL的核心库函数,组成命令的单词首字母大写,如“Color”;后缀“4”表示颜色值是由4个变量来表示的;“f ”表示所表示颜色的每个分量的类型为32位浮点数。
OpenGL核心库函数常量是以“GL_”开头,均用大写字母,并用下划线将每个关键词分开,如GL_COLOR_BUFFER_BIT。
4. 程序的基本结构
一个OpenGL程序的基本结构是很简单的,无论多么复杂的OpenGL程序,可以大致分解成以下部分:
(1)定义窗口
Windows 系统下的OpenGL实现提供了一个辅助函数库aux,用于解决开窗口和处理输入事件等问题。
窗口管理函数:
首先,调用void auxInitDisplayMode(Glbitfield mask) 函数定义窗口的特性,如颜色和缓存区的性质。例如:
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
//窗口显示单缓存和RGB(彩色)模式
其次,调用void auxInitPosition(Glint x,Glint y, Glint width, Glint height)定义窗口在屏幕上的位置和大小。其中,x,y为窗口左上角的坐标,width,height 分别为窗口的宽和高(像素个数)。默认值为(0,0,100,100)。例如:
auxInitPosition(0,0,200,200);
//大小 x=200 y=200 (0,0)是屏幕左上点
最后,完成上述两个函数调用后,用函数void auxInitWindow(Glbyte *titleString)打开窗口。窗口的标题为字符串titleString。窗口把ESC键与退出函数联系起来,可以用来关闭窗口,退出程序。例如:
auxInitWindow(“”);
//初始化窗口,参数是标题
处理输入事件:
当改变窗口尺寸、移动窗口、重新显示窗口时,由auxReshapeFunc(myReshape)调用函数myReshape重新定义窗口属性。
通常myReshape函数调用glViewPort函数,对当前图形进行裁剪,重新定义投影矩阵等。
OpenGL辅助函数库中还包括处理键盘和鼠标输入事件的函数。
(2)初始化操作
由于OpenGL的绘图方式是由一系列的状态确定的,因而在绘制图形前需要做一些准备工作,包括清缓存区(buffer)、定义光照模型、定义纹理映射等基本操作的初始化状态、设置三维视景体、定义视口。
例如:
glClearColor(,,,); //将窗口清为黑色
glClear(GL_COLOR_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色,即背景色
(3)设置观察坐标系下的取景模式和取景框位置及大小
主要利用了三个函数:
函数void glViewport(GLint x, Glint y, Glsizei width, Glsizei height):设置在屏幕上的视口大小,四个参数描述屏幕视口四个角上的坐标(以像素表示)。
参数(x,y)用于指定视口的左下角在窗口坐标系中的位置,参数width和height分别确定矩形视口的宽和高,均以像素为单位 。
注意:视口的大小和尺寸是在窗口坐标系中进行度量的,默认状态下其坐标原点位于窗口的左下角,其尺寸与窗口的大小相同。
视点方向
观察体积
函数void glOrtho(left,right,bottom,top,near,far):设置投影方式为正交投影(平行投影),其取景体积是一个各面均为矩形的六面体。
函数void gluPerspective(fovy,aspect,zNear,zFar):设置投影方式为透视投影,其取景体积是一个截头锥体,在这个体积内的物体投影到锥的顶点。
它通过指定x-z平面内的视角大小及宽高比来确定沿视线方向的棱锥,并通过指定远、近剪切面与视点间的距离来截断棱锥,得到观察体。
注: 在默认状态下投影方式为平行正交投影。
(4)使用OpenGL的库函数构造几何物体对象的数学描述。包括点线面的位置和拓扑关系、几何变换、光照处理等。这是OpenGL程序的主要部分。
在例中,在函数void CALLBACK display(void)中写好要绘制的三维图形,然后,在主程序中调用auxMainLoop(display)就可让该图形一直显示。
(5)程序的微机运行环境配置
• 软件与硬件环境
操作系统:Windows 95/98, Windows NT
软件开发环境:Microsoft Visual C++及以上版本
硬件:奔腾级微机,最好配有支持OpenGL硬加速的图形卡
• 连接三个静态库
程序中除了包含必需的头文件如<> <GL/> <GL/> <GL/>外,在创建执行文件时,在VC环境设置中要连接, 和 三个函数库。运行已创建的执行文件时,在windows\system目录下要有,两个动态连接库。
OpenGL程序设计入门
OpenGL的状态机制
OpenGL中的图元绘制
坐标变换及其OpenGL实现
应用变换的一个实例
光照处理
OpenGL的状态机制
OpenGL的绘图方式是由一系列的状态决定的,如果设置了一种状态或模式而不改变它,OpenGL在绘图过程中将一直保持这种状态或模式。
例如,当前绘图颜色就是OpenGL 的一个状态,当选定颜色后,OpenGL就用这个颜色绘图。在例中,以下语句
void myinit(void)
{
glClearColor(,,,);
}
中的函数glClearColor(,,,)将视口背景色清为黑色,如果不改变这种状态,视口背景色将一直保持为黑色。
再如:
glColor3f(,,); //设置当前颜色为红色
glColor3f(,,); //设置当前颜色为蓝色
glRectf(,,,); //绘制一个矩形
glColor3f(,,); //设置当前颜色为绿色
glRectf(,,,);
glRectf(,,,);
执行结果是:一个蓝色的矩形和两个绿色的矩形。
OpenGL中的图元绘制
任何复杂的图形都是由基本的图元点、线和多边形组成的。
程序格式如下:
要绘制某个几何对象,首先必须指明究竟是哪种类型的几何对象(例如点、线和多边形),否则系统在执行绘图操作时无法判断究竟是画什么。OpenGL提供了一对用于指定顶点序列操作的函数,确定基本几何对象的类型。
glBegin(…);
……//描述一组顶点,用于建构某种几何对象
glEnd();
1. 点的绘制
OpenGL中点定义为一个方块,在默认状态下,点是屏幕上的一个像素。在OpenGL中,一个点是当作一个 n(2,3,4)维向量来处理的。
OpenGL 中的点是三维的,如果用户设定二维坐标(x,y),则OpenGL在实际计算时处理的点为(x,y,0);对于由四维齐次坐标定义的顶点(x,y,z,w), 在w非零时,齐次顶点(x,y,z,w)对应于三维坐标中的点(x/w,y/w,z/w);若w =,则对应于无穷远处的点。
glVertex {2,3,4}{dfis}{v}(TYPE coords);
例如:
glVertex2i(0,1);
glVertex3d(,,);
glVertex4d(40,, 0,2);
Glfloat v[3]={,,}; glVertex3fv(v);
以下操作的结果是在屏幕上绘制三个点 :
glBegin(GL_POINTS)
glVertex3f(,,);
glVertex3f(,,);
glVertex3f(,,);
glEnd();
2. 线的绘制
与数学意义上两端无限延伸的直线不同,OpenGL的线是数学定义中的线段,用成对的端点来描述。如:
glBegin(GL_LINES)
glVertex2f(,);
glVertex2f(,);
glEnd();
上述操作描述了一条由坐标原点到点(,)的线段。
3. 多边形的绘制
多边形指封闭线段围成的区域。但OpenGL中可以描述的多边形有两点限制:多边形的边除了多边形的顶点外不允许相交,即确保多边形为简单多边形;多边形为凸多边形,即任给多边形的两个内部点,其连线完全在多边形内。
多边形的描述方式如下:
glBegin(GL_POLYGON);
glVertex*(v0);
glVertex*(v1);
……..
glVertex*(vn);
glEnd();
其中,*表示glVertex函数的上述任一种组合形式,由多边形顶点v0,v1,…,vn的表示形式而定。
注意:多边形顶点应按一定顺序排列(如逆时针)。
4. 矩形的绘制
由于矩形在几何体构造中出现得比较频繁,所以OpenGL提供了专门的矩形函数:
void glRect{dfis}(TYPE x1, TYPE y1, TYPE x2, TYPE y2);
void glRect{dfis}v (TYPE *v1, TYPE *v2);
矩阵的左上、右下角点坐标分别为(x1,y1)和(x2,y2),或者用数组指针v1、v2表示。用上述函数描述的矩形位于z=0平面内,并且各边分别平行于x、y轴。但注意:经过坐标变换之后,这些特性可能改变。
例 基本的图元绘制程序
为简单起见,我们仅改变例中的绘制函数void CALLBACK display(void)如下:
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色,即背景色
glColor4f(,,,); //选颜色(R,G,B)
glPointSize(); //设置点的大小
glBegin(GL_POINTS); //在屏幕上绘制三个点
glVertex3f(,,);
glVertex3f(,,);
glVertex3f(,,);
glEnd();
glBegin(GL_LINES); //在屏幕上绘制一条线段
glVertex2f(,);
glVertex2f(,);
glEnd();
glBegin(GL_POLYGON); //在屏幕上绘制一个四边形
glVertex2f(,);
glVertex2f(,);
glVertex2f(,);
glVertex2f(,);
glEnd();
glColor3f(,,); //设置当前颜色为红色
glColor3f(,,); //设置当前颜色为蓝色
glRectf(,,,); //绘制一个矩形
glColor3f(,,); //设置当前颜色为绿色
glRectf(,,,); //绘制一个矩形
glRectf(,,,); //绘制一个矩形
glFlush(); //强制绘图,不驻留缓存
}
用该函数替换例中相应的函数后,运行的结果如下图所示。
坐标变换及其OpenGL实现
在二维平面上创建三维物体的过程:
在三维空间中创建所绘制物体的模型,由计算机经过适当的变换,将三维坐标系中的点转换为屏幕上的相应位置,以得到理想的视觉效果。
OpenGL就是实现将物体的各个顶点通过各种变换矩阵的作用映射到屏幕的过程 。下图是顶点变换过程。注意: 在模式观察变换过程中,顶点的法向量也自动地进行变换。
在OpenGL编程过程中,程序员必须在头脑中有整个坐标变换过程的清晰的图像,才能将所建的场景模型正确地显示在屏幕上。
OpenGL的坐标变换过程类似于用照相机拍摄照片的过程。使用照相机与坐标变换的步骤比较如下:
(1)竖起三角架,将照相机对准场景(视图变换,取景变换,视点变换)。
(2) 将要拍的场景置于所要求的位置上(造型变换)。
(3) 选择照相机透镜或调整焦距(投影变换)。
(4) 确定最终的照片需要多大,例如放大照片(视口变换)。
其中(1)、(2)顺序可看成照相馆中的照相过程;而在室外摄影时,由于先有景物,再选择照相机的位置与方向,因此照相顺序可看成(2)、(1)。
OpenGL中的多种变换(几何变换、投影变换等)是由矩阵的乘积实现的。 OpenGL提供了一系列矩阵操作函数。
(1) 通用的矩阵操作命令
void glMatrixMode(Glenum mode);
参数取值:GL_MODELVIEW、GL_PROJECTION或GL_TEXTURE 。默认的选定矩阵为造型-观察变换矩阵。
void glLoadIdentity(void);
OpenGL中的变换命令都是对当前矩阵(当前矩阵为以后图形变换所要使用的矩阵)进行操作,因此在选定可修改矩阵后,应首先用上述命令设置当前操作矩阵为单位矩阵。
(2)造型-观察变换
造型-观察变换过程就是一个将顶点坐标从世界坐标变换到视觉坐标的过程。这里很重要的是对两个坐标系的认识。
世界坐标系也称为全局坐标系。它是一个右手坐标系,可以认为该坐标系是固定不变的,在初始态下,其x轴为沿屏幕水平向右,y轴为沿屏幕垂直向上,z轴则为垂直屏幕面向外指向用户。
视觉坐标系(即观察坐标系)也称为局部坐标系。它是一个左手坐标系,该坐标系是可以活动的。在初始态下,其原点及x、y轴分别与世界坐标系的原点及x、y轴重合,而z轴则正好相反,即为垂直屏幕面向内。
在初始状态下,相机在观察坐标系的原点且指向z轴正向,即为垂直屏幕面向内。
• 平移变换
void glTanslate{fd}(TYPE x, TYPE y, TYPE z);
• 旋转变换
void glRotate{fd}(TYPE angle, TYPE x, TYPE y, TYPE z);
绕矢量v=(x,y,z)T逆时针方向旋转angle指定的角度。旋转角度的范围是0~360度。当angle=0时,glRotate()不起作用。
• 缩放变换
void glScale{fd}( TYPE x, TYPE y, TYPE z);
变换的顺序:
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glMultMatrixf(N); /* apply transformation N */
glMultMatrixf(M); /* apply transformation M */
glMultMatrixf(L); /* apply transformation L */
glBegin(GL_POINTS);
glVertex3f(v); /* draw transformed vertex v */
glEnd();
在这个过程中,在GL_MODELVIEW状态下,相继引入了I(单位阵),N,M,L矩阵。变换后的顶点为NMLv(顶点取列向量)。因此,顶点的变换为N(M(Lv)),即是先作变换L,然后是变换M,最后才是N。这里,顶点v 的实际变换顺序正好与指定的顺序相反。
(3)投影变换
在调用投影变换命令前必须先在程序中加入下述语句:
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
这两条命令一方面指定接下来的变换命令只影响投影矩阵,同时也将当前投影矩阵设置为单位阵。
• 透视投影
void gluPerspective (Gldouble fovy, Gldouble aspect, Gldouble zNear, Gldouble zFar) ;
• 正交投影
void glOrtho (Gldouble left, Gldouble right, Gldouble bottom, Gldouble top, Gldouble near, Gldouble far) ;
对于二维图形向二维屏幕的投影,则应使用实用库中的如下函数:
void gluOrtho2D(Gldouble left, Gldouble right, Gldouble bottom, Gldouble top);
前面提到过,用二维顶点命令绘制的二维物体的z坐标均为零,而gluOrtho2D()命令假定场景中的 z 坐标介于和 之间。
(4)视口变换
void glViewport(GLint x, Glint y, Glsizei width, Glsizei height);
窗口和视口是两个不同的概念 。
注意:应该使视口的长宽比与取景体积的长宽比相等,否则会使图像变形。
应用变换的一个实例
本节将通过一个简单的程序进一步阐述OpenGL的基本变换命令及其使用方法。
例 三维空间绘制立方体的程序
#include <>
#include <GL/>
#include <GL/>
#include <GL/>
#include <>
void myinit(void);
void CALLBACK myReshape(int w, int h);
void CALLBACK display(void);
//初始化
void myinit(void)
{
glClearColor(,,,); //将窗口清为黑色
} glShadeModel(GL_FLAT); //常量明暗处理方式
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色,即背景色
glColor3f(,,); //选当前颜色(R,G,B)为白色
glLoadIdentity(); //设置当前矩阵为单位矩阵
glTranslatef(, , ); //平移变换
glRotatef(45,,,); //旋转变换
glScalef(,,); //缩放变换
auxWireCube(); //绘制立方体
glFlush(); //强制绘图,不驻留缓存
}
void CALLBACK myReshape (int w, int h) //用于窗口改变大小时的处理,与绘图无关
{
glMatrixMode(GL_PROJECTION); //指明当前矩阵操作是针对投影矩阵进行的
glLoadIdentity(); //设置当前矩阵为单位矩阵
gluPerspective(, (GLfloat)w/ (GLfloat)h, , ); // 投影变换
glMatrixMode(GL_MODELVIEW); // 返回视点-模型矩阵
glViewport(0,0,w,h); //定义视口变换
}
void main(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA);
//窗口显示单缓存和RGB(彩色)模式
auxInitPosition(0,0,200,200); //大小 x=200、y=200 , (0,0)是屏幕左上点
auxInitWindow(“Perspective 3-D Cubes”); //初始化窗口,参数是标题
myinit();
auxReshapeFunc(myReshape);
auxMainLoop(display);
}
光照处理
光照处理是OpenGL中绘制逼真的三维图形的一个重要步骤。否则,就无法使最终显示在屏幕上的物体表现出立体感。
在RGBA模式下,如果不启用光照处理,则顶点的颜色由当前颜色值所确定;启用光照处理后,则另行计算。
场景中物体的光照计算不仅取决于光源的属性,而且与材料的特性直接相关。即光照计算要反映出在场景中使用的光照特性,以及场景中的物体对这种光的反射和吸收特性。
1. OpenGL光照的基本概念
OpenGL中将光视为由红、绿、蓝三种成分所组成,光源的特性由所发出的三种颜色的光的比例来确定。
说明:光源的RGB值代表各种颜色占最大光强的比例,通过调整各数值可以使光源呈现出各种不同的颜色。例如:R=G=B=1时得到最强的白光,而R=G=B=时,由于光强减弱,得到灰白光。
OpenGL在光照模拟中将光线分为辐射光、环境光、漫反射光和镜面反射光 4 种独立的成分。
辐射光:源自发光体,并且不受其他光源的影响。
环境光(泛光):它经过环境的多次散射,已不可能确定其方向,而是好像来自各个方向。它作用于物体的表面时,将沿各个方向均匀反射。
漫反射光:它来自一个方向,但作用于物体表面后将沿各个方向均匀散射。
镜面反射光:来自一个方向,并沿一特定方向离开。
顺便指出,可以在场景中设置多个独立的光,通过调整各光源的参数,得到满意的光照效果。
物体表面的材质特性:
OpenGL中认为,材料的颜色决定于其对入射光中的红、绿、蓝各成分的反射比例。例如,对于理想的红色物体来说,它完全反射红色光,完全吸收绿色光和蓝色光。因而,白光照射下物体呈红色;纯绿光照射下物体则呈黑色。
说明:材料的设置参数中也包括对R、G、B值的设定,但与光照的参数设置相比,两者的含义是不同的。对材料来说,上述各值分别对应材料对各种颜色光的反射比例。如上述理想的红色物体对应的参数为R=1,G=0,B=0。一般地,如果光源和材料的参数分别为(Rl,Gl,Bl)和(Rm,Gm,Bm),若不考虑其它影响因素,则观察到的光线的参数为(Rl*Rm,Gl*Gm, Bl*Bm)。
与光线的情况相似,材料也具有辐射色、环境色、漫反射色和镜面反射色。
为了模拟场景中的发光体,可以设定材料的辐射光特性;而环境色、漫反射色和镜面反射色则反映材料对环境光、漫反射光和镜面反射光的反射系数。
材料的泛光与每个入射光源的泛光组分相结合;漫反射与入射光源的漫反射光组分相结合;镜面反射与入射光源的镜面反射光组分相结合。
2. 光照步骤的处理
以下程序绘制一个有光照的球体:
#include <>
#include <GL/>
#include <GL/>
#include <GL/>
void myinit(void);
void CALLBACK myReshape(int w, int h);
void CALLBACK display(void);
void myinit(void) //初始化
{
glClearColor(,,,); //将窗口清为黑色
Glfloat mat_specular[]={, , , };
Glfloat mat_shininess={};
Glfloat light_position[] = {, ,,};//无穷远光源
glMaterialfv(GL_FRONT,GL_SPECULAR,mat_specular);
glMaterialfv(GL_FRONT,GL_SHININESS,mat_shininess);
glLightfv(GL_LIGHT0,GL_POSITION,light_position);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glDepthFunc(GL_LESS);
glEnable(GL_DEPTH_TEST);
}
void CALLBACK display(void)
{
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//将颜色缓存清为glClearColor命令所设置的颜色,即背景色
glColor4f(,,,); //选颜色(R,G,B)
auxSolidSphere();
glFlush(); //强制绘图,不驻留缓存
}
void CALLBACK myReshape (int w, int h) //定义视口变换和投影变换
{
glViewport(0,0,w,h);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
if(w<=h)
glOrtho(, , *(GLfoalt)h/(GLfloat)w, *(GLfoalt)h/(GLfloat)w,,);
else
glOrtho(*(GLfoalt)h/(GLfloat)w, *(GLfoalt)h/(GLfloat)w, , ,,);
glMatrixMode(GL_MODELVIEW);
glIdentity();
}
/* Mainp Loop
* Open window with initial window size, title bar,
* RGBA display mode, and handle input events.
*/
void main(void)
{
auxInitDisplayMode(AUX_SINGLE|AUX_RGBA|AUX_DEPTH);
auxInitPosition(0,0,500,500);
auxInitWindow(“Lighting”);
myinit();
auxReshapeFunc(myReshape);
auxMainLoop(display);
}
从上面的程序可以看出,对物体光照处理的步骤是:
• 定义物体各顶点的法向量,由此确定物体与光源的相对方向。OpenGL通过法向量来确定指定顶点从每个光源所得到的受光量,并用于像素点颜色的计算中。
• 光源的创建、定位和启用。
由glLight()函数创建光源,指定光源的位置。可创建8个。
必须用命令glEnable(GL_LIGHTING)启用所定义的光源。用glEnable(GL_LIGHTi)打开第i个光源。
• 选择光照模型。
光照模型由glModel()函数进行选择。OpenGL的光照模型由三部分组成:全景泛光强度、观察点的位置、对前、后面光照计算的处理。
• 定义场景中物体的材料属性。
通过函数glMaterial()指定材料的环境色、漫反射色、镜面反射色及其光亮度。
3. 光照计算
在RGBA模式下,在光照条件下屏幕上某一像素点的颜色计算公式为 :
顶点颜色=像素点处的材料辐射+经顶点处材料环境光属性缩放后的全场环境光+来自各光源经衰减处理后的各种成分(环境光、漫反射光和镜面反射光)
其中emission表示物体发射光,如果物体本身不是光源,则这一项为0;ambient,diffuse,specular分别表示泛光、漫反射光、镜面反射光,其中每一项都是物体与光源的组合(相乘);shininess表示物体的光洁度;符号n 表示顶点的法线方向,l 表示视线方向,s 表示入射光方向。
上述计算对R、G、B分量分别进行,计算所得的结果被调整到0~1的范围之内。
思考题
在微机环境下, 在VC++运行环境中调试并运行例,与程序, 以初步了解OpenGL程序的基本结构和编程方法。