OpenGL点光源.doc_第1页
OpenGL点光源.doc_第2页
OpenGL点光源.doc_第3页
OpenGL点光源.doc_第4页
OpenGL点光源.doc_第5页
已阅读5页,还剩31页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

OpenGL点光源 在OpenGL场景描述中可以包含多个点光源,光源的各种属性设置使用函数: void glLightif (GLenum light, GLenum pname, TYPE param); void glLightifv (GLenum light, GLenum pname, TYPE *param);指定。其中,参数light指定进行参数设置的光源,其取值可以是符号常量GL_LIGHT0,GL_LIGHT1,GL_LIGHT7;参数pname指定对光源设置何种属性,其取值参见表10-1;参数param指定对于光源light的pname属性设置何值,非矢量版本中,它是一个数值,矢量版本中,它是一个指针,指向一个保存了属性值的数组。表10-1 参数pname的取值及其含义 (1)点光源的颜色 点光源的颜色由环境光、漫反射光和镜面光分量组合而成,在OpenGL中分别使用GL_AMBIENT、GL_DIFFUSE和GL_SPECULAR指定。其中,漫反射光成分对物体的影响最大。 (2)点光源的位置和类型 点光源的位置使用属性GL_POSITION指定,该属性的值是一个由4个值组成的矢量(x,y,z,w)。其中,如果w值为0,表示指定的是一个离场景无穷远的光源,(x,y,z)指定了光源的方向,这种光源被称为方向光源,发出的是平行光;如果w值为1,表示指定的是一个离场景较近的光源,(x,y,z)指定了光源的位置,这种光源称为定位光源。 (3)聚光灯 当点光源定义为定位光源时,默认情况下,光源向所有的方向发光。但通过将发射光限定在圆锥体内,可以使定位光源变成聚光灯。属性GL_SPOT_CUTOFF用于定义聚光截止角,即光锥体轴线与母线之间的夹角,它的值只有锥体顶角值的1/2。聚光截止角的默认值为180.0,意味着沿所有方向发射光线。除默认值外,聚光截止角的取值范围为0.0,90.0。GL_SPOT_DIRECTION属性指定聚光灯光锥轴线的方向,其默认值是(0.0,0.0,-1.0),即光线指向z轴负向。而GL_SPOT_EXPONENT属性可以指定聚光灯光锥体内的光线聚集程度,其默认值为0。在光锥的轴线处,光强最大,从轴线向母线移动时,光强会不断衰减,衰减的系数是:轴线与照射到顶点的光线之间夹角余弦值的聚光指数次方。 (4)光强度衰减 属性GL_CONSTANT_ATTENUATION、GL_LINEAR_ATTENUATION、GL_QUADRATIC_ATTENUATION分别指定了衰减系数c0,c1和c2,用于指定光强度的衰减。 在OpenGL中,必须明确启用或禁用光照。默认情况下,不启用光照,此时使用当前颜色绘制图形,不进行法线矢量、光源、光照模型、材质属性的相关的计算。要启用光照,可以使用函数: glEnable(GL_LIGHTING);指定了光源的参数后,需要使用函数: glEnable(light);启用light指定的光源。当然也可以用light参数调用glDisable函数,禁用light指定的光源。需要特别说明的是,点光源的位置和方向是定义在场景中的,与景物一起通过几何变换和观察变换变换到观察坐标系中,因此光源既可以与场景中对象的相对位置保持不变,也可以使光源随观察点一起移动。OpenGL全局光照 在OpenGL中,还需要设定全局光照(相当于背景光)。OpenGL提供了下面的函数对全局光照的属性进行定义。 void glLightModeif (GLenum pname,TYPE param); void glLightModeifv (GLenum pname,TYPE *param);其中,参数pname指定全局光照的属性,其取值参见表10-2;参数param指定进行设置的属性的值。表10-2 参数pname的取值及含义 属性GL_LIGHT_MODEL_AMBIENT指定OpenGL场景中的背景光,如果不指定,系统使用低强度的白色(0.2,0.2,0.2,1.0)光。 镜面反射时需要几个矢量参数,包括从物体表面到观察位置的矢量V,它指出表面位置与观察位置的关系。矢量V的默认方向为正z方向(0.0,0.0,1.0),如果不希望用默认值而使用位于观察坐标原点的实际观察位置来计算V,则将GL_LIGHT_MODEL_LOCAL_VIEWER属性值指定为GL_TRUE。 在有些应用中,需要看到物体的后向面,例如实体的内部剖视图。此时需要打开双面光照,即对物体的前向面和后向面都进行光照计算。 在光照计算中,通常是分别计算环境光、表面散射光、漫反射光和镜面反射光的贡献,然后将其叠加。默认情况下,纹理映射在光照处理之后进行。但这样镜面高光区的纹理图案会变得不太理想。为此,可以将GL_LIGHT_MODEL_COLOR_CONTROL指定为GL_SEPARATE_SPECULAR_COLOR,在纹理映射之后应用镜面颜色。这样,对于光照计算将生成两个颜色:镜面反射颜色和非镜面反射颜色。纹理图案先和非镜面反射颜色混合,然后再和镜面反射颜色混合。OpenGL表面材质 在启用了光照后,物体表面的颜色将由照射在其上的光的颜色以及物体的材质属性决定。所谓物体的材质属性,就是物体表面对各种光的反射系数。在OpenGL中使用下面的函数设定: void glMaterialif (GLenum face, GLenum pname, TYPE param); void glMaterialifv (GLenum face, GLenum pname, TYPE *param);其中,face的取值可以是符号常量GL_FRONT,GL_BACK,GL_FRONT_AND_BACK,指定当前设定的材质属性应用于物体表面的前向面、后向面还是前后向面,这使得可以对物体内外表面设置不同的材质属性,在打开双面光照的情况下产生特殊的效果。参数pname指定设置的材质属性,其取值参见表10-3;参数param设置属性的值。表10-3 参数pname的取值及含义 属性GL_AMBIENT和GL_DIFFUSE的值定义了物体表面对环境光和漫射光中R,G,B颜色分量的反射系数。如果使用属性GL_AMBIENT_AND_DIFFUSE,那么物体表面的环境光和漫射光将使用相同的反射系数。 镜面反射可以在物体表面形成高光区域。OpenGL中通过改变属性GL_SPECULAR的值改变物体表面对镜面反射光的反射率,还可以通过属性GL_SHININESS的值改变高光区域的形状和大小。GL_SHININESS属性值的取值范围为0.0,128.0,值越大,高光区域越小、光线集中程度越高。 在很多的应用中,有时希望物体亮一些,特别是对于一些表示光源的物体,此时可以通过GL_EMISSION属性使物体表面看起来有点发光。 在设定了材质属性之后,物体的最终颜色是由其材质属性的RGB值和光照属性的RGB值共同决定的。例如,如果当前环境光源的RGB值为(0.5,1.0,0.5),而物体材质的环境反射系数为(0.5,0.5,0.5),那么物体表面的环境光颜色为: (0.50.5,1.00.5,0.50.5)=(0.25,0.5,0.25)即将每个环境光源的成分与材质的环境反射率相乘。这样,物体表面的颜色为多项RGB值的叠加:包括材质对环境光的反射率与环境光结合的RGB值,材质对漫反射光的反射率与漫反射光结合的RGB值,材质对镜面光的反射率与镜面反射光结合的RGB值等。当叠加的RGB中任何一个颜色分量的值大于1.0,那么就用1.0计算。但是,在这种设定下,有时很难判断出物体在光照环境中的颜色,为此OpenGL提供了另一种材质模式,即颜色材质模式,可以通过函数: void glColorMaterial (GLenum face, GLenum mode);设置。其中,参数face可以取GL_FRONT,GL_BACK,GL_FRONT_AND_BACK,指定物体的哪个面的材质属性使用颜色材质模式;而参数mode允许的取值是GL_AMBIENT、GL_DIFFUSE、GL_SPECULAR、GL_AMBIENT_AND_DIFFUSE或GL_EMISSION,指定将更新哪种材质属性。 在使用了颜色材质模式后,需要调用: glEnable(GL_COLOR_MATERIAL);这样,可以通过glColor函数来指定物体表面的颜色,而相应的材质属性将通过颜色值和光源的RGB值计算出来。光照效果是光线与表面共同作用的结果。在OpenGL中,光线被假定成红绿蓝三种原色光的组合,因此,光源的颜色就由它所发出的这三种原色光的含量决定,而物理表面的颜色则由其反射的这三种光的含量决定。OpenGL的光照模型尽管只是一个近似,但它很管用且计算速度很快。如果你需要一个更精确的模型,那么只好自己编程计算了,不过模型越精确则计算速度越慢。在场景中,OpenGL可以放置若干个光源,每个光源都可以被单独地打开或关闭。照在物体上的光线有些是直接来自光源,而另一些是光源的光经过若干次反射而来。这些多次反射的光被称为环境光(Ambient Light),它们已经变得很“散”,以致于无法确定其初始方向,不过当某个光源(产生它们的光源)被关闭后,它们也就消失了。不同的物理表面对光线有不同的反射特性,那些通常看起来很光泽的表面能够将入射光很好地向某一特定方向反射,而另外一些则将入射光均衡地散射向各个方向。大部分表面的特性是介于这两者之间的。当然有些表面也会自己发光,比如汽车前灯。6.1.1 光照分量OpenGL对光照效果的计算是由四个独立的部分叠加而成的,它们是:出射光自发光(emitted)、环境光(ambient)、漫反射光(diffuse)、镜面光(specular)。出射光是最简单的,它从物体发出且不受任何其它光源的影响。环境光从光源发出并经过多次反射形成的,它均匀从周围环境入射至物体表面并朝各个方向等量反射。漫反射光是直接从特定光源入射并朝各个方向等量反射的光,入射角越小它看起来越亮,同时从各个角度来看它的亮度是一样的。镜面光也是直接从特定光源入射的,但在反射时只朝特定的方向反射(遵从镜面反射定理)。一束平行激光在高质量的镜面上可以几乎被100%地反射。表面光泽的金属或塑料都有很高的镜面反射成份,而象粉笔或地毯之类的光泽度差的物品则几乎没有。尽管同一光源出射的光的频率分布是一定的,但照在不同的表面上产生的环境光、漫反射光、镜面光分量却可能不同。比如白光照在房间的红色墙壁上,被散射的光就会趋于红色。OpenGL允许对各个反射光分量的RGB值分别作独立的调整。6.1.2 材质颜色OpenGL中近似认为材质的颜色由它的反射光中的红绿蓝三色所占的百分比决定。比如:一个理想的红色球将反射入射的所有红光并吸收所有绿色和蓝色光。这样的球在白光(含红绿蓝三色光)和纯红光的照射下的表现是一致的都是红色球,然而在不含红光成份的光(如纯绿光)的照射下则呈现为黑色(因为没有光被反射)。与光线对应,材质有独立的环境反射、漫反射和镜面反射颜色成份,分别决定了材质对环境光、漫反射光和镜面光的反射能力。环境反射和漫反射决定了物体的颜色,这两种成份很相象,甚至可以当作一种来处理。镜面反射色通常为白色或灰色,因此镜面反射的高光(highlights)通常是光源的镜面光成份的颜色和强度决定。想象一束白光照射在一个有光泽的红色金属球上,球体的大部分将表现为红色,而高光处则为白色。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. 建立并选择一个光照模型,它决定了全局环境光照的程度以及视点的有效位置(用于光照计算)。4. 定义场景中物体的材质。例6-1将显示一个在独立光源照射下的球体,如前面的图6-1所示。例6-1 :光照下的球体: #include #include #include aux.hvoid 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 (argv0);myinit();auxReshapeFunc (myReshape);auxMainLoop(display);与光照有关的调用写在函数myinit()中,下面我们将会简单地讨论它们的用法,在后面的章节中还会仔细研究。在例6-1中需要注意的一点是,它使用了RGBA颜色模式,而不是颜色索引模式(color-index)。OpenGL对这两种模型的光照计算是不同的,而且实际上颜色索引模式的光照表现力是很有限的,因此RGBA是比较受推崇的光照模式,本章中的所有例子都使用该模式。需要对颜色索引模式作进一步了解的请参阅“颜色索引模式中的光照”一节。场景中物体的材质特性决定了光线是如何被反射的从而表现出它是用什么材料做的。因为入射光线与物体材质表面的作用相当复杂,所以通过设置物体的材质特性而使它“看起来象那么会事”就是一件很技术的活了。你需要制定一种材质的环境光反射色、漫射光反射色、镜面光反射色(这里说XX反射色的意思是指对某种光的某个颜色分量的反射能力)以及它的光泽度。在这个例子中,只有最后两个特性镜面光反射颜色和光泽度被明确地指定(通过使用glMaterialfv()函数)。在“定义材质特性”一节中会描述所有的材质特性参数并给出例子。重点注意:在写你自己的光照程序时,记住可以修改一些参数值,而另一些参数使用其缺省值。当然,不要忘记使你定义的光源生效(enable之),同时允许光照计算。最后,记住你可以使用显示列表以提高在改变光源位置时的计算效率(参阅“显示列表设计”一节)。6.3 创建光源光源有如下属性:颜色、位置和方向。下面将讨论如何控制这些属性一达到期望的效果。我们用来设置光源的所有属性的函数是glLight*(),它有三个参数,分别用来代表光源标识、需设置的属性及期望的属性值。函数形式为:void glLightifv(GLenum light, GLenum pname, TYPE param);light是光源的标识,例如:GL_LIGHT0,GL_LIGHT1.GL_LIGHT7。一般地,GL_LIGHTi代表第i号光源。pname代表需设置的属性,其枚举值和对应的属性意义见表6-1。param代表需要给pname代表的属性设置的值。注意,TYPE是param的类型,它由glLight函数名后面跟的字母决定,i就是int,f就是float,v(就是vectro)可选,选了说明param代表指向一组参数的指针,不选则说明是单一参数。表6-1 glLight*()的pname参数及其缺省值(本表系从yxy (田丝丝#冬眠的小肥鹰)的教程中拷贝而来,改了一点点,呵呵)pname缺省值说明GL_AMBIENT0,0,0,1RGBA模式的环境光GL_DIFFUSE1,1,1,1RGBA模式的漫反射光GL_SPECULAR1,1,1,1RGBA模式的镜面光GL_POSTION1,0,1,0光源位置齐次坐标(x,y,z,w)GL_SPOT_DIRECTION0,0,-1聚光灯聚光方向矢量GL_SPOT_EXPONENT0聚光灯聚光指数GL_SPOT_CUTOFF180聚光灯聚光发散半角GL_CONSTANT_ATTENUATION1常数衰减因子GL_LINER_ATTENUATION0线性衰减因子GL_QUADRATIC_ATTENUATION0平方衰减因子表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*()的所有参数和它们可能的取值。这些参数和定义全局光照模型以及物体材质特性的参数息息相关。请参阅“选择光照模型”和“定义材质特性”以获取更多的知识。在“光照的数学”中,将从数学的角度解释这些参数是如何相互作用的。6.3.1 颜色OpenGL允许对光源的三个不同的与颜色相关的参数:GL_AMBIENT, GL_DIFFUSE和GL_SPECULAR进行调整。GL_AMBIENT参数代表场景中某光源的环境光的RGBA各个分量强度。象表6-1中列的那样,缺省情况下是没有环境光的,因为GL_AMBIENT是(0.0, 0.0, 0.0, 1.0)。例6-1中使用的正是这样的值。如果程序将环境光设为蓝光,应该这样GLfloat light_ambient = 0.0, 0.0, 1.0, 1.0;glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);那么产生的结果如附录J中图J-14所示。GL_DIFFUSE可能是与“光的颜色”联系最大的参数了,它定义了特定光源提供的漫反射光的RGBA颜色值。缺省情况下,光源GL_LIGHT0的GL_DIFFUSE值是 (1.0,1.0, 1.0, 1.0),即产生明亮的白光,如附录J中图J-14。而其它光源(GL_LIGHT1到GL_LIGHT7)的缺省值都是 (0.0, 0.0, 0.0, 0.0)。GL_SPECULAR参数影响物体上的高光点颜色。现实世界中的典型例子如玻璃瓶子上的高光点显示的就是照在上面的光的颜色。因此,如果你想获得逼真的效果,应该将GL_SPECULAR的值设得和GL_DIFFUSE一样(为什么? 我想是获得逼真的金属光泽效果吧。) 所有的光源的GL_SPECULAR参数缺省值和GL_DIFFUSE缺省值也是一样的。6.3.2 位置与衰减光源的位置有两种。一种是离场景无限远,另一种是在附近。前者被称为方向光源,它射到物体上的光可以认为是平行的。现实生活中,太阳就是这样的光源。后者被称为位置光源,因为它的确切位置决定了它对场景的作用效果,尤其是决定了光线的投射方向。台灯就是位置光源。从附录J图J-16中你可以看到这两种光源的不同作用效果。在例6-1中使用的是方向光源:GLfloat light_position = 1.0, 1.0, 1.0, 0.0 ;glLightfv(GL_LIGHT0, GL_POSITION, light_position);如上所示,你给GL_POSITION参数提供了一个四个值的向量(x, y, z, w)。如果最后一个值w是0,则相应的光源为方向光源,且(x, y, z)决定其方向。这个方向将被透视模型(modelview,这个词俺不知该怎么翻,根据后面的描述,模型变换是指旋转变换,透视变换是只位移变换)矩阵转化到视点坐标系中,就好象它是一个被标准化的向量一样。GL_POSITION的缺省值为(0, 0, 1, 0),指向Z轴的负方向。(注:你当然可以定义诸如(0,0,0)的方向,但这对处理光照是没有意义的)如果w是非零值,光源就是位置型的。而(x,y,z)代表光源位置的齐次坐标(参见附录G)。该位置将被透视模型矩阵转换到视点坐标系中(参见“控制光源的位置和方向”一节以了解更多的有关位置转换的知识)。在缺省意义下,光源的光是向各个方向发出的,不过你也可以定义一个聚光灯使其发射锥光,这将在下一节“聚光灯”中解释。请注意,具有光滑阴影效果的多边形表面各部分的颜色是由其各顶点的颜色计算决定的。正因为如此,你需要避免在局部光源附近使用大多边形如果光源位置在多边形的中部,各顶点会因为距离光源太远而接收不到足够的光照,从而整个多边形看起来要比你相象的要暗。要解决这个问题,就得把大多边形分解成许多小块。在现实世界中,光线的强度会随着传播距离的增加而减弱。方向光源是从无限远处射来的,对它谈距离衰减意义不大,所以衰减计算对方向光源是无效的。然而,对位置光源则必须考虑这个问题。OpenGL通过给光源的光乘上一个因子来达到衰减的目的。衰减因子 其中,d为光源到顶点的距离kc为GL_CONSTANT_ATTENUATIONkl为GL_LINEAR_ATTENUATIONkq为GL_QUADRATIC_ATTENUATION缺省情况下,kc为1.0,kl和kq都为0,当然也可以设置其它值,比如glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0);glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0);glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);注意,环境光、漫反射光和镜面光都会被衰减,只有出射光和全局环境光不被衰减。6.3.3 聚光灯前面曾提到,你可以将一个位置光源定义成一个聚光灯,也就是说可以将光的发射形状调整为圆锥形。创建聚光灯,你需要定义需要的锥光的跨度。(记住,因为聚光灯是位置光源,你还是需要为它定义一个位置。你当然也可以方向光源定义为聚光灯,从语法上没有错,但是无意义)。定义锥光的散射角(即圆锥中轴和边的夹角),需要使用GL_SPOT_CUTOFF参数,圆锥的顶角是该值的两倍,见图6-2。图6-2 GL_SPOT_CUTOFF参数示意注意光线不能超越圆锥的边界。缺省时没有聚光灯效果,因为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参数次方成正比。因此聚光指数越大,锥光的集中性越好。请参阅“光照的数学”一节以了解光强计算的细节。6.3.4 多光源如前所述,你可以在你的场景中至少使用8个光源(当然可以更多,这取决于你的需要)。因为OpenGL需要对每个顶点从所有光源接收的光进行计算,所以增加光源的数目无疑会使系统处理效率下降。用来代表个光源的常量为GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, GL_LIGHT3等等。在前面的讨论中,已经对与GL_LIGHT0相关的参数进行了设置。如果要增加光源,则必须对其参数进行设置。不过要记住,其他光源的缺省参数与GL_LIGHT0的是不同的,如表6-2所示。下面的代码定义了一个衰减的白色聚光灯。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, 将原来的白色方向光源修改为一个位置型有色光源 加入一个有色聚光灯。提示:可以参考上面的代码 感觉一下这两种改动对整体效果的影响 6.3.5 控制光源位置和方向OpenGL对光源的位置和方向的处理与它对基本的几何形体的处理是类似的。换句话说,光源与基本形体有着相同的矩阵变换。进一步讲,当使用glLight*()来指定光源的位置和方向时,这些位置和方向就通过当前的透视模型矩阵转换到视点坐标系中。这意味着你可以通过修改当前透视模型矩阵栈中的内容来控制光源的位置和方向(注:投影矩阵不会对光源的位置和方向产生影响)。本节将介绍如何通过修改程序中对光源位置的设置来获得三种不同的效果,这是与模型和透视变换相关的。这些效果是: 固定位置光源 环绕固定物体运动的光源 和视点一起运动的光源 例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: 让光源只是相对物体移动而不是环绕其转动。提示:在display()中用glTranslated()代替glRotated(),同时找一个合适的参数代替spin 改变光源衰减模式,使光源远离物体时物体上的光照会减弱。提示:加入glLight*()设置相应参数 例6-1: 通过模型变换移动光源: movelight.c#include #include #include aux.hstatic 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 (argv0);myinit();auxMouseFunc (AUX_LEFTBUTTON, AUX_MOUSEDOWN, movelight);auxReshapeFunc (myReshape);auxMainLoop(display);6.4 选择光照模型OpenGL的光照模型概念有如下三个方面: 全局环境光强度 视点位置在场景内还是可以被认为是无限远处 对物体表面的正反面光照计算是否不同 本节介绍如何设置光源,同时介绍如何起用之即如何告诉OpenGL你需要对相应光照进行计算。6.4.1 全局环境光如前所述,每个光源都对场景的环境光有贡献。同时,还有一种环境光不是由光源提供的,这就是全局环境光。 设置全局环境光的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演示了设置不同强度全局环境光的效果。6.4.2 局部和无穷远视点视点位置影响到由于镜面反射导致的高光点计算。更具体地讲,高光点的强度由三个因素决定:被照顶点的法向量、从光源到顶点的光线方向、视点和顶点的连线方向。有一点要明白,使用各种光照命令后,实际上移动的并不是视点,你所做的是改变了投影方式,使视点看起来被移动了(请参阅“投影变换”一节)。不过,在光照计算中,很多地方是假设光源位置改变的。对于无穷远视点来说,它和某顶点的连线方向

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

最新文档

评论

0/150

提交评论