自己做的程序光照上总感觉有点问题,今天花时间弄了弄。把相关的资料记录下来,以备查找!!
OpenGL 编程指南 6章 光照OpenGL如何模拟现实的光照效果 OpenGL对光照计算的部分依据。图6-1是一个球在有光照和无光照情况下的样子。
图6-1 有光照和无光照球面的比较OpenGL,你可以通过调整光照和物体在场景中的关系获得各种各样的效果。这是本章将着重讨论的内容,包括OpenGL的光照模型以及众多的参数。本章末尾将从数学上介绍光照对颜色的影响问题。 看我又说对了吧,没有光照的球面看起来和一个二维的圆盘没有区别,从而可见光照和物体的相互作用在三维影像中是多么重要。 利用 本章可分为如下几部分: § 正如前面所讲的,最终的光照效果是光线与表面共同作用的结果。 在 在场景中, 不同的物理表面对光线有不同的反射特性,那些通常看起来很光泽的表面能够将入射光很好地向某一特定方向反射,而另外一些则将入射光均衡地散射向各个方向。大部分表面的特性是介于这两者之间的。当然有些表面也会自己发光,比如汽车前灯。 § OpenGL对光照效果的计算是由四个独立的部分叠加而成的,它们是:出射光[自发光](emitted)、环境光(ambient)、漫反射光(diffuse)、镜面光(specular)。OpenGL允许对各个反射光分量的RGB值分别作独立的调整。6.1.2 材质颜色
OpenGL中近似认为材质的颜色由它的反射光中的红绿蓝三色所占的百分比决定。比如:一个理想的红色球将反射入射的所有红光并吸收所有绿色和蓝色光。这样的球在白光(含红绿蓝三色光)和纯红光的照射下的表现是一致的--都是红色球,然而在不含红光成份的光(如纯绿光)的照射下则呈现为黑色(因为没有光被反射)。6.1.3 光线与材质的RGB值RGB值定义于材质的RGB值定义有所不同。RGB值的大小对应于该色光强度与其最高强度的比例百分数。比如一束光的RGB都是1.0,那么它就是最强的白光;如果这些值都为0.5,则整束光的颜色还是白色,只不过强度减半,看起来是灰色。如果R=G=1.0而B=0.0,那么就是最强的黄光。RGB值的大小对应于材质对该色光的反射比例百分数。比如某材质的R=1.0,G=0.5,B=0.0,则它反射入射的所有红光,一半的绿光,不反射蓝光。换句话来说,如果一束光的RGB值为(LR, LG, LB),被照材质的RGB值为(MR, MG, MB),则在不考虑其它反射效果的情况下,眼睛所看到的颜色由(LR*MR, LG*MG, LB*MB)决定。RGB成份分别为(R1, G1, B1)和(R2, B2, G2),则OpenGL将相应的成份相加,得到(R1+R2, G1+G2, B1+B2)。如果任何一个相加结果大于1(表明这样的光强是显示设备不能实现的),则被认为是1。6.2 简例:渲染一个球体
1. 定义所有物体每个顶点的法向量方向。这些法向量决定了物体表面与光源的相对取向。
2. 建立、选择并放置一个或多个光源。
3. 建立并选择一个光照模型,它决定了全局环境光照的程度以及视点的有效位置(用于光照计算)。6-1所示。 :光照下的球体: 4. 定义场景中物体的材质。 例6-1将显示一个在独立光源照射下的球体,如前面的图 例6-1 #include <GL/gl.h> #include <GL/glu.h> #include "aux.h" void myinit(void) { GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat mat_shininess[] = { 50.0 }; GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; 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_LEQUAL); glEnable(GL_DEPTH_TEST); } void display(void) { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); auxSolidSphere(1.0); glFlush(); } void 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)w/(GLfloat)h, 1.5*(GLfloat)w/(GLfloat)h, -1.5, 1.5, -10.0, 10.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } int main(int argc, char** argv) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH); auxInitPosition (0, 0, 500, 500); auxInitWindow (argv[0]); myinit(); auxReshapeFunc (myReshape); auxMainLoop(display); } 与光照有关的调用写在函数 场景中物体的材质特性决定了光线是如何被反射的从而表现出它是用什么材料做的。因为入射光线与物体材质表面的作用相当复杂,所以通过设置物体的材质特性而使它“看起来象那么会事”就是一件很技术的活了。你需要制定一种材质的环境光反射色、漫射光反射色、镜面光反射色(这里说XX反射色的意思是指对某种光的某个颜色分量的反射能力)以及它的光泽度。在这个例子中,只有最后两个特性--镜面光反射颜色和光泽度被明确地指定(通过使用glMaterialfv()函数)。在“定义材质特性”一节中会描述所有的材质特性参数并给出例子。重点注意: 在写你自己的光照程序时,记住可以修改一些参数值,而另一些参数使用其缺省值。当然,不要忘记使你定义的光源生效(enable之),同时允许光照计算。最后,记住你可以使用显示列表以提高在改变光源位置时的计算效率(参阅“显示列表设计”一节)。 § 光源有如下属性:颜色、位置和方向。下面将讨论如何控制这些属性一达到期望的效果。我们用来设置光源的所有属性的函数是 void glLight{if}[v](GLenum light, GLenum pname, TYPE param); light是光源的标识,例如:GL_LIGHT0,GL_LIGHT1...GL_LIGHT7。一般地,GL_LIGHTi代表第i号光源。pname代表需设置的属性,其枚举值和对应的属性意义见表6-1。 表6-1 (本表系从 表6-1中相对GL_DIFFUSE和GL_SPECULAR的缺省值只对GL_LIGHT0有效,对于其它的光源,它们的缺省值都是 (0.0, 0.0, 0.0, 1.0)。 下面是使用glLight*()的一个例子: GLfloat light_ambient[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat light_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); glLightfv(GL_LIGHT0, GL_DIFFUSE, light_diffuse); glLightfv(GL_LIGHT0, GL_SPECULAR, light_specular); glLightfv(GL_LIGHT0, GL_POSITION, light_position); 数组被用来给参数赋值,通过反复调用glLightfv()给不同的参数赋值。在这个例子里,前三条语句是多余的,因为它们设置的分别是那三个参数的缺省值。 记住要用glEnable打开每个光源。要了解更多的相关操作,请参阅“使光源生效”一节。 在下面的部分中将解释glLight*()的所有参数和它们可能的取值。这些参数和定义全局光照模型以及物体材质特性的参数息息相关。请参阅“选择光照模型”和“定义材质特性”以获取更多的知识。在“光照的数学”中,将从数学的角度解释这些参数是如何相互作用的。 § OpenGL GL_AMBIENT GLfloat light_ambient[] = { 0.0, 0.0, 1.0, 1.0}; glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient); 那么产生的结果如附录 GL_DIFFUSE GL_SPECULAR § 光源的位置有两种。一种是离场景无限远,另一种是在附近。前者被称为方向光源,它射到物体上的光可以认为是平行的。现实生活中,太阳就是这样的光源。后者被称为位置光源,因为它的确切位置决定了它对场景的作用效果,尤其是决定了光线的投射方向。台灯就是位置光源。从附录 在例 GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 }; glLightfv(GL_LIGHT0, GL_POSITION, light_position); 如上所示,你给 如果 请注意,具有光滑阴影效果的多边形表面各部分的颜色是由其各顶点的颜色计算决定的。正因为如此,你需要避免在局部光源附近使用大多边形--如果光源位置在多边形的中部,各顶点会因为距离光源太远而接收不到足够的光照,从而整个多边形看起来要比你相象的要暗。要解决这个问题,就得把大多边形分解成许多小块。 在现实世界中,光线的强度会随着传播距离的增加而减弱。方向光源是从无限远处射来的,对它谈距离衰减意义不大,所以衰减计算对方向光源是无效的。然而,对位置光源则必须考虑这个问题。 衰减因子 其中, d kc kl kq 缺省情况下, glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0); glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0); glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5); 注意,环境光、漫反射光和镜面光都会被衰减,只有出射光和全局环境光不被衰减。 § 前面曾提到,你可以将一个位置光源定义成一个聚光灯,也就是说可以将光的发射形状调整为圆锥形。创建聚光灯,你需要定义需要的锥光的跨度。 图 注意光线不能超越圆锥的边界。缺省时没有聚光灯效果,因为GL_SPOT_CUTOFF参数被默认为180度,也就意味着光线是超各个方向发射的(此时圆锥的顶角为360,已经不成“锥”了)。GL_SPOT_CUTOFF一般的取值范围是[0.0, 90.0](或者就是取那个180)。下面的语句将散射角置为45度。 glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0); 你还需要给出聚光灯的方向,即圆锥中轴的方向。 GLfloat spot_direction[] = { -1.0, -1.0, 0.0 }; glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction); 方向是用齐次坐标表示的,缺省情况下为(0, 0, 1),所以如果你不显式设置GL_SPOT_DIRECTION的值,聚光灯光线将射向z轴负向。同时,这个方向将被透视模型矩阵按其标准化后的值转换到视点坐标系中 除了聚光灯的散射角和方向外,你还可以控制光线在圆锥内的强度分布。有两个办法,首先,你可以先设置光线衰减因子,我们前面提到过,它将与光强相乘;你也可以设置聚光指数,即GL_SPOT_EXPONENT参数,它决定了光线的集中性,缺省值为0。光线在圆锥的中心最强,越靠边越弱,其衰减与该光线与圆锥中轴的夹角的COS值的GL_SPOT_EXPONENT参数次方成正比。因此聚光指数越大,锥光的集中性越好。请参阅“光照的数学”一节以了解光强计算的细节。 § 如前所述,你可以在你的场景中至少使用 GLfloat light1_ambient[] = { 0.2, 0.2, 0.2, 1.0 }; GLfloat light1_diffuse[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light1_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat light1_position[] = { -2.0, 2.0, 1.0, 1.0 }; GLfloat spot_direction[] = { -1.0, -1.0, 0.0 }; glLightfv(GL_LIGHT1, GL_AMBIENT, light1_ambient); glLightfv(GL_LIGHT1, GL_DIFFUSE, light1_diffuse); glLightfv(GL_LIGHT1, GL_SPECULAR, light1_specular); glLightfv(GL_LIGHT1, GL_POSITION, light1_position); glLightf(GL_LIGHT1, GL_CONSTANT_ATTENUATION, 1.5); glLightf(GL_LIGHT1, GL_LINEAR_ATTENUATION, 0.5); glLightf(GL_LIGHT1, GL_QUADRATIC_ATTENUATION, 0.2); glLightf(GL_LIGHT1, GL_SPOT_CUTOFF, 45.0); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction); glLightf(GL_LIGHT1, GL_SPOT_EXPONENT, 2.0); glEnable(GL_LIGHT1); 如果将这些代码加入例6-1中,那么其中的球体将被两个光源照射,一个是方向型的,另一个是位置型的。 可以尝试一下,修改例6-1, § OpenGL 例6-1作为最简单的例子,演示了固定位置光源。要达到这个效果,你只需要在使用了透视或(和)模型变换后设置光源位置。下面是myinit()和myReshape()函数中的相关语句: glMatrixMode (GL_PROJECTION); glLoadIdentity(); if (w <= h) glOrtho (-1.5, 1.5, -1.5*h/w, 1.5*h/w, -10.0, 10.0); else glOrtho (-1.5*w/h, 1.5*w/h, -1.5, 1.5, -10.0, 10.0); glMatrixMode (GL_MODELVIEW); glLoadIdentity(); /* later in myInit() */ GLfloat light_position[] = { 1.0, 1.0, 1.0, 1.0 }; glLightfv(GL_LIGHT0, GL_POSITION, position); 视窗和投影矩阵被首先确定。然后,单位阵被调入作为透视模型矩阵,接下来设置光源位置。因为使用了单位阵,因此光源的初始位置(1.0, 1.0, 1.0)和它相乘并没有变化。在这之后,光源位置和透视模型矩阵都没有改变,所以光源还放在(1.0, 1.0, 1.0)的位置。 现在假设你要旋转或移动光源的位置使其相对一个静态的物体运动。一个办法是在模型变换后设置光源位置,光源位置改变的效果是通过模型变换阵的改变来实现的。你仍可以在程序开头使用上面的init()函数。然后,一般是在程序消息循环里加入你想要的模型变换(利用透视模型栈)从而确定光源的新位置。下面是一段可能的代码: void display(GLint spin) { GLfloat light_position[] = { 0.0, 0.0, 1.5, 1.0 }; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0.0, 0.0, -5.0); glPushMatrix(); glRotated((GLdouble) spin, 1.0, 0.0, 0.0); glLightfv(GL_LIGHT0, GL_POSITION, light_position); glPopMatrix(); auxSolidTorus(0.275, 0.85); glPopMatrix(); glFlush(); } display()函数重画场景,重画时光源好象是绕静态圆环面转过了spin代表的角度。请注意两对glPushMatrix()和glPopMatrix()函数,它们是用来隔离透视和模型变换的,之所以要隔离是因为需要画的圆环面是不旋转的,即画它时只受到glTranslatef(0.0, 0.0, -5.0); 这条语句的影响。在本例中视点保持不变,所以当前的变换阵被压栈,然后由glTranslatef()完成透视变换(实际就是位移),将现在的变换阵压栈,再由glRotated()完成模型变换(其实就是旋转变换)。此时设置新的光源位置,由于此位置是在旋转后的坐标系中定义的,所以变换回原坐标系中后就好象光源被旋转了一样(注:原也就是视点坐标系,与之对应的变换阵一直被压在栈里)。在光源位置定义完毕后,与透视变换对应的矩阵被弹出,此时再画圆环面,就象前面所说的那样,转换到原坐标系中,圆环面只有位置变换而无旋转变换,所以看起来就有了光源环绕圆环面旋转的效果。 为了创建和视点一起移动的光源,你需要在透视模型变换之前设置光源位置,这样,变换就会按相同的方式一起变换视点和光源。让我们在myinit()函数中,作一些小改动来达到这个效果: GLfloat light_position[] = { 0.0, 0.0, 1.0, 1.0 }; // 这样光源好象是放在视点的位置上 glViewport(0, 0, w-1, h-1); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(40.0, (GLfloat) w/(GLfloat) h, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); glLightfv(GL_LIGHT0, GL_POSITION, light_position); 然后对display()函数修改如下: void display(GLint spin) { glClear(GL_COLOR_BUFFER_MASK | GL_DEPTH_BUFFER_MASK); glPushMatrix(); glTranslatef (0.0, 0.0, -5.0); glRotatef ((GLfloat) spin, 1.0, 0.0, 0.0); auxSolidTorus(0.275, 0.85); glPopMatrix(); glFlush(); } 当被照的圆环面重画时,光源和视点都被移动了spin角度。因为我们是在视点的坐标系中,所以看到的效果是一个旋转的圆环面被固定的视点的光源照射的效果。 可以尝试一下,修改下面的例子6-1: 例6-1: 通过模型变换移动光源: movelight.c #include <GL/gl.h> #include <GL/glu.h> #include "aux.h" static int spin = 0; void movelight (AUX_EVENTREC *event) { spin = (spin + 30) % 360; } void myinit (void) { glEnable(GL_LIGHTING); glEnable(GL_LIGHT0); glDepthFunc(GL_LEQUAL); glEnable(GL_DEPTH_TEST); } void display(void) { GLfloat position[] = { 0.0, 0.0, 1.5, 1.0 }; glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix (); glTranslatef (0.0, 0.0, -5.0); glPushMatrix (); glRotated ((GLdouble) spin, 1.0, 0.0, 0.0); glRotated (0.0, 1.0, 0.0, 0.0); glLightfv (GL_LIGHT0, GL_POSITION, position); glTranslated (0.0, 0.0, 1.5); glDisable (GL_LIGHTING); glColor3f (0.0, 1.0, 1.0); auxWireCube (0.1); glEnable (GL_LIGHTING); glPopMatrix (); auxSolidTorus (0.275, 0.85); glPopMatrix (); glFlush (); } void myReshape(GLsizei w, GLsizei h) { glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(40.0, (GLfloat) w/(GLfloat) h, 1.0, 20.0); glMatrixMode(GL_MODELVIEW); } int main(int argc, char** argv) { auxInitDisplayMode (AUX_SINGLE | AUX_RGBA | AUX_DEPTH); auxInitPosition (0, 0, 500, 500); auxInitWindow (argv[0]); myinit(); auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, movelight); auxReshapeFunc (myReshape); auxMainLoop(display); } § OpenGL 本节介绍如何设置光源,同时介绍如何起用之--即如何告诉OpenGL你需要对相应光照进行计算。 § 如前所述,每个光源都对场景的环境光有贡献。同时,还有一种环境光不是由光源提供的,这就是全局环境光。 设置全局环境光的RGBA强度,需要按下面的方式使用GL_LIGHT_MODEL_AMBIENT参数: GLfloat lmodel_ambient[] = { 0.2, 0.2, 0.2, 1.0 }; glLightModelfv(GL_LIGHT_MODEL_AMBIENT, lmodel_ambient); 本例中lmodel_ambient的参数值都是GL_LIGHT_MODEL_AMBIENT的缺省值。因为这些数值会产生少量的环境光,所以即使不定义其它光源你仍可以看到场景中的物体。附录J中图J-18演示了设置不同强度全局环境光的效果。 § 视点位置影响到由于镜面反射导致的高光点计算。更具体地讲,高光点的强度由三个因素决定:被照顶点的法向量、从光源到顶点的光线方向、视点和顶点的连线方向。有一点要明白,使用各种光照命令后,实际上移动的并不是视点,你所做的是改变了投影方式,使视点看起来被移动了 对于无穷远视点来说,它和某顶点的连线方向是个常数。局部视点的效果更趋于真实,但由于它要对每个顶点的方向分别计算,就大大增加了程序的负担。缺省状态下,视点都是在无穷远处的。下面的语句可以把它改成局部的: glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE); 这条语句把视点放在视点坐标系的 § 光照计算是对所有多边形表面进行的,不管是正面还是反面。由于通常设置的都是正面的光照性质,背面的光照效果往往都是不正确的。在例 glLightModeli(LIGHT_MODEL_TWO_SIDE, GL_TRUE); OpenGL 关闭双侧光照,只需把前面语句中的 § 在 glEnable(GL_LIGHTING); 取消光照的命令为 当你设置了你定义的每个光源后,你必须显式地打开它们。在例 glEnable(GL_LIGHT0); § 本节将介绍如何定义物体的材质:环境反射色、漫反射色和镜面反射色,光泽度,以及出射光色。在光照和材质计算中用到的公式会在“光照中的数学”一节中介绍。大部分的材质特性参数与在建立光源时用过的参数相似,设置它们的机制也差不多,只不过这回使用的命令是 void glMaterial{if}[v](Glenum face, GLenum pname, TYPE param); face参数可以是GL_FRONT, GL_BACK, 或 GL_FRONT_AND_BACK,分别代表材质应该贴到物体的哪个表面。材质特性的名称通过pname设置,特性值由 表6-2 glMaterial*()的pname参数及其缺省值 在“选择光照模型”一节中讨论过,你可以为物体的正反表面设置不同的光照效果。如果反面的确可以被看到的话,你可以通过glMaterial*()的face参数来使正反表面具有不同的材质特性。附录J的图J-19展示了一个内外材质不同的物体的光照效果。 想知道通过材质特性的设置会引起什么样的效果,请见附录图J-21,图中给出了同一个物体设置了不同材质特性的效果。整幅图使用的是相同的光照模型。下面的部分将详细讨论这些球体的效果是如何画出来的。 请注意,大部分材质特性的设置值是(R,G,B,A)颜色。不管在其它参数中alpha(也就是A)的含义,相对某个特定的顶点,它表示该顶点的漫反射光反射色的alpha,也就是给GL_DIFFUSE参数设置值的alpha值。(参阅“混合”一节关于alpha值的讨论)。同时,任何RGBA模式的材质特性使用的都不是颜色索引模式。参阅“颜色索引模式下的光照”一节以了解更多关于颜色索引模式的知识。 § 通过 环境光影响物体的总体色彩。因为当物体被光源直接照射时眼睛看到的主要的漫射光,所以只有当没有直接照明时环境光在物体上的效果才会被明显的看到。物体的环境反射光由全局环境光和各光源的环境光成分决定。与漫射光类似,环境光也不受视点位置的影响。 对于现实的物体来说,环境光和漫射光的颜色基本是一致的。因此, GLfloat mat_amb_diff[] = { 0.1, 0.5, 0.8, 1.0 }; glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, mat_amb_diff); 在本例中,RGBA色为(0.1, 0.5, 0.8, 1.0)--一种深蓝色,这句话将物体正反表面的环境和漫射光色都设置为这种颜色。 在附录图J-21第一行的球体没有环境光反射--(0,0,0,0),第二行的环境光反射就比较大--(0.7, 0.7, 0.7, 1.0)。 § 物体上的镜面反射导致高光点。与环境光和漫射光不同,镜面反射的光强与视点的位置关系密切--从反射角的方向看它最亮。想理解这一点,可以想象在阳光下看一个金属球的情形。当你移动脑袋的时候,高光点也会跟着移动一些。而移动超过一定限度时,你就完全看不到高光点了。 OpenGL允许你设置镜面反射高光点的RGBA颜色(通过GL_SPECULAR参数),并控制高光点的大小和明亮程度(通过GL_SHININESS参数)。GL_SHININESS的取值范围是[0.0, 128.0]--值越高,高光点越小且越亮(聚焦越好)。想了解高光点的计算请参阅“光照中的数学”一节。 在附录图J-21中,第一列的球体没有镜面反射,第二列的GL_SPECULAR 的GL_SHININESS值设置如下: GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat low_shininess[] = { 5.0 }; glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); 在第三列中,GL_SHININESS值都被增加到了100.0。 § 通过给GL_EMISSION参数设置一个RGBA值,你可以使物体看起来好象在发射那种颜色的光。因为大部分现实世界的物体(除了光源)都不发射光,所以你你最可能使用这个功能来模拟一盏灯以及场景中的其它光源。在图J-21中,第四列的球体都有绿色的GL_EMISSION: GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0}; glMaterialfv(GL_FRONT, GL_EMISSION, mat_emission); 你会注意到这些球体微微有些荧光灯泡的感觉,不过它们在场景中不会起到光源的作用。要想真正有灯泡照亮别人的效果,你必须在球体的位置再定义一个位置型光源才行。 § 例 例 GLfloat no_mat[] = { 0.0, 0.0, 0.0, 1.0 }; GLfloat mat_ambient[] = { 0.7, 0.7, 0.7, 1.0 }; GLfloat mat_ambient_color[] = { 0.8, 0.8, 0.2, 1.0 }; GLfloat mat_diffuse[] = { 0.1, 0.5, 0.8, 1.0 }; GLfloat mat_specular[] = { 1.0, 1.0, 1.0, 1.0 }; GLfloat no_shininess[] = { 0.0 }; GLfloat low_shininess[] = { 5.0 }; GLfloat high_shininess[] = { 100.0 }; GLfloat mat_emission[] = {0.3, 0.2, 0.2, 0.0}; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // glPushMatrix(); glTranslatef (-3.75, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, no_mat); glMaterialfv(GL_FRONT, GL_SHININESS, no_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); auxSolidSphere(); glPopMatrix(); // glPushMatrix(); glTranslatef (-1.25, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse); glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular); glMaterialfv(GL_FRONT, GL_SHININESS, low_shininess); glMaterialfv(GL_FRONT, GL_EMISSION, no_mat); auxSolidSphere(); glPopMatrix(); // glPushMatrix(); glTranslatef (1.25, 3.0, 0.0); glMaterialfv(GL_FRONT, GL_AMBIENT, no_mat); glMaterialfv(GL_FRONT, GL_DIFFUSE,
6.3.1 颜色
pname
缺省值
说明
GL_AMBIENT
0,0,0,1
RGBA模式的环境光
GL_DIFFUSE
1,1,1,1
RGBA模式的漫反射光
GL_SPECULAR
1,1,1,1
RGBA模式的镜面光
GL_POSTION
1,0,1,0
光源位置齐次坐标(x,y,z,w)
GL_SPOT_DIRECTION
0,0,-1
聚光灯聚光方向矢量
GL_SPOT_EXPONENT
0
聚光灯聚光指数
GL_SPOT_CUTOFF
180
聚光灯聚光发散半角
GL_CONSTANT_ATTENUATION
1
常数衰减因子
GL_LINER_ATTENUATION
0
线性衰减因子
GL_QUADRATIC_ATTENUATION
0
平方衰减因子
6.5.1 漫射和环境反射glMaterial*()设置GL_DIFFUSE和GL_AMBIENT参数会影响到被物体反射的漫射光和环境光的颜色。漫射光是影响你所看到的物体颜色的最重要的因素,它取决与入射光的颜色以及入射光和顶点法向量的夹角。(当入射光垂直于表面是漫射光最强)。视点位置对漫射光无影响。OpenGL提供了一个方法可以方便地使用glMaterial*()一次将漫射光和环境光参数设置为相同的值:
pname
缺省值
说明
GL_AMBIENT
(0.2, 0.2, 0.2, 1.0)
材料环境光反射色
GL_DIFFUSE
(0.8, 0.8, 0.8, 1.0)
材料漫反射光反射色
GL_AMBIENT_AND_DIFFUSE
设置上两项为相同值
GL_SPECULAR
(0.0, 0.0, 0.0, 1.0)
材料镜面光反射色
GL_SHININESS
0.0
材料反射指数
GL_EMISSION
(0.0, 0.0, 0.0, 1.0)
材料发射光色
GL_COLOR_INDEXES
(0,1,1)
环境、漫射、镜面光反射颜色索引
与光线对应,材质有独立的环境反射、漫反射和镜面反射颜色成份,分别决定了材质对环境光、漫反射光和镜面光的反射能力。环境反射和漫反射决定了物体的颜色,这两种成份很相象,甚至可以当作一种来处理。镜面反射色通常为白色或灰色,因此镜面反射的高光(highlights)通常是光源的镜面光成份的颜色和强度决定。想象一束白光照射在一个有光泽的红色金属球上,球体的大部分将表现为红色,而高光处则为白色。
§
光线颜色的
对光线来说,
对材质来说,
类似地,如果到达眼睛的两束光的
§
向场景中加入光照的步骤如下:
出射光是最简单的,它从物体发出且不受任何其它光源的影响。
环境光从光源发出并经过多次反射形成的,它均匀从周围环境入射至物体表面并朝各个方向等量反射。
漫反射光是直接从特定光源入射并朝各个方向等量反射的光,入射角越小它看起来越亮,同时从各个角度来看它的亮度是一样的。
镜面光也是直接从特定光源入射的,但在反射时只朝特定的方向反射(遵从镜面反射定理)。一束平行激光在高质量的镜面上可以几乎被100%地反射。表面光泽的金属或塑料都有很高的镜面反射成份,而象粉笔或地毯之类的光泽度差的物品则几乎没有。
尽管同一光源出射的光的频率分布是一定的,但照在不同的表面上产生的环境光、漫反射光、镜面光分量却可能不同。比如白光照在房间的红色墙壁上,被散射的光就会趋于红色。
§
第
学习目的:
我们看到的任何物体的样子是由其自身特性和光照共同决定的。想想在风和日丽的晴天和在乌云密布的阴天看到的大海是多么的不同,一个宛如明丽的蓝宝石,另一个却只是晦暗的灰色。实际上如果没有光照,大部分物体看起来根本不是三维的。入射光和物体对光的吸收和反射决定了你所看到的景色,这也就是