logo资料库

OpenGL的光照详解.pdf

第1页 / 共8页
第2页 / 共8页
第3页 / 共8页
第4页 / 共8页
第5页 / 共8页
第6页 / 共8页
第7页 / 共8页
第8页 / 共8页
资料共8页,全文预览结束
NEBULA - 2006 NEBULA - 2006 OpenGL的光照详解 2009-04-04 15:42 计算机图形学及OpenGL简介 计算机图形学是计算机科学的重要组成部分,在模拟仿真、虚拟现实、飞行员驾驶员训练、 医疗、教学、演示等各个方面都得到了广泛得应用。其中最火热的应用是在 3D游戏方面,并极大的推动了相关计算机硬件 的高速发展。 OpenGL是一个独立于硬件的高效接口,可在很多硬件平台上实现,在 UNIX、Linux、Mactosh上都可以使用OpenGL开发 。当然在PC上也提供相应的支持,在PC游戏史上上有着划时代意义的电子游戏QUAKE的3D图像在底层就是使用的OpenG L。在OpenGL之上建立了提供高级特性的复杂函数库。OpenGL使用库(GLU)提供学多建模特性,包括二次曲面、NURBS 曲线和曲面。 大多数OpenGL都使用相似的操作步骤,这些处理步骤叫做OpenGL渲染流水线。如下图所示,虽然并非所有的OpenGL实 现都使用这种操作顺序,但它为预测OpenGL将做什么提供了可靠的线索。 光照简介 在现实生活中的物体,要有光照存在才可以被看到。物体通过自身发光以及反射光进入人眼,物体才能在人眼中成像。如果 没有任何的光,人眼将观察不到任何东西,一片漆黑。 在 光照中首先是光源,要有光源才能产生光线,才有以后的一系列反射、折射、散射等效果。不同的物体的表面物理属性 不同,所以相同的光线照射到不同表面属性的 物体表面会产生不同的效果,发生漫反射,镜面反射的比例各不相同,有的 属于半透明的物体还有折射效果。这些不同的物体表面物理属性属于材质的范畴。 除了材质以外,物体表面还有各种图案效果,这就是纹理。光线在空中穿行的时候,还会有更多复杂的效果,比如灰尘会让 光线散射,在空中行成一条光路,直射的阳光产生的光晕效果,炎热的地面温度会让地面附近的光线的行进路线不在是笔直 的,从而产生扭曲的效果。 在我的课程报告中,不涉及复杂的光照效果,只对最基本的情况进行介绍,并说明程序实现方法。
光照中的光源 在 现实中,光源的类型很多,而且有的光源不能简单的用一种模型来描述,而是具有多种不同类型的光源的特点。我们在3 D图形程序中,把现实中的光源分为几种典 型的种类,基本上可以描述现实中的情况,并进行近似的模拟。这几种基本的光 源类型是:点光源、无穷远光源、方向光源和环境光。 点光源:光线从光源点向四面八方发散,发光的恒星(如太阳)、发光的灯泡一般使用该光源模型模拟,是最简单的光源。 无穷远光源:所有的光线都平行的从一个方向过来,当发光体(如太阳)离渲染的场景很远可以认为是无穷远时,一般使用 该光源模型进行模拟。 方向光源:光线沿着一个方向在特定角度范围内逐渐发散开。现实世界中的车灯,手电筒一般使用该光源模型进行模拟。 环境光源:光线从各个地方以各个角度投射到场景中所有物体表面,找不到光源的确切位置。现实世界中不存在这样的光源 ,一般使用该光源模型来模拟点光源、无穷远光源、方向光源在物体表面经过许多次反射后的情况,环境光源照亮所有物体 的所有面。 上 面这四种基本的光源模型,只能近似的描述光源,不可能做到非常逼真(以假乱真)。因为在现实中的光源,并不真正 就是这样的。比如环境光源,一束光线照射到 物体表面发生反射后,再照射到另外的物体的表面,如此循环反复这才是环 境光的真正情况。但由于这个过程是个无限次反射的过程,计算机无法处理无限的问题, 所以采取了简单的近似处理。而 且环境光源在反射过程中,上一次反射所带的颜色会影响下次反射所照物体的颜色,并且无限的重复。光线追踪算法是一种 好得多的 近似描述,但也仅仅是近似描述,只是近似效果比用环境光源模型要好。 除 了以上介绍的四种光源模型外,OpenGL还提供了让物体自发光让自己可以被看见的方式。这就是物体自发光。物体自发 光对于光源十分的重要,比如电灯泡可 以看作是一个点光源,我们把点光源的位置设置到灯泡的中央,这样灯泡周围的物 体将被照亮,但是灯泡的外表面由于相对光源来说是背面,将不能被照亮。这与实 际情况不符合,灯泡照亮其它物体,而 自身却不亮,所以需要通过物体自发光让灯泡的外表面也发亮。 光 源的一般属性包括:镜面反射光线颜色、漫反射光颜色、环境光线颜色、光源位置。镜面反射光颜色:在物体表面将发 生镜面反射的光线的颜色。漫反射光颜色:在 物体表面将发生漫反射的光线的颜色。环境光线颜色:照亮所有物体所有表 面的光线的颜色。光源位置就是光源在场景中所在的位置。 光线的衰减:光源发出的光线的强度会随着传播距离越来越大而变弱(无穷远光源除外)。光线强度会乘以一个衰减因子。 衰减因子 = 1/(K1 + K2 * d + k3 *d^2) 其中d为光源距离 (无穷远光源的衰减因子为1) 方向光源发出的光线会随着偏移中心方向的角度增大而减弱。 推荐 光照中的材质 材质是光照效果中的重要属性。材质描述了物体表面的光学物理属性,决定了光线在该表面光线反射的具体情况。材质决定 了物体的表面特性,决定了光线在物体表面反射的情况。 物体表面的反射分为漫反射和镜面反射。物体反射各种类型光源的情况都可以分为:漫反射和镜面反射两种。 漫反射:光线射到物体表面以后,反射光线的方向是任意方向的。 漫反射 镜面反射:光线射到物体表面以后,反射光线根据照射表面位置的法线方向,发生方向唯一确定的镜面反射。
镜面反射 在OpenGL中漫反射部分的光线与镜面反射部分的光线是分开计算的,然后将分开计算的效果进行叠加。 材质的属性包括:镜面反射颜色、漫反射颜色、环境光颜色、光洁度、自发光颜色。 镜面反射颜色、漫反射色、环境光颜色:分别与光源的镜面反射光颜色、光源的漫反射颜色、光源的环境光颜色共同决定物 体表面的镜面反射颜色、漫反射颜色、环境光颜色。3种类型的结果分别计算,然后叠加共同确定反射表面像素值的颜色。 漫反射项: max{L*n,0}×DIFFUSE_light×DIFFUSE_material 环境光项:AMBIENT_light × AMBIENT_material 在OpenGL中可以使用下面的代码来设置材质属性: GLfloat planet_ambient[] = { 0.01 , 0.01 , 0.01 , 1.0 }; GLfloat planet_diffuse[] = { 0.7 , 0.7 , 0.7 , 1.0 } ; glMaterialfv(GL_FRONT , GL_AMBIENT ,planet_ambient); glMaterialfv(GL_FRONT , GL_DIFFUSE ,planet_diffuse); OpenGL确 定某个顶点颜色可以分为两种情况:开启光照渲染和关闭光照渲染的情况。当关闭光照渲染的时候,顶点的颜色 由OpenGL状态机绘制该物体时的颜色确定;当 开启光照渲染的时候,绘制该物体时OpenGL状态机的颜色将对该物体上顶 点的颜色没有任何影响,此时物体顶点的颜色由物体材质,光照叠加效果以及物体表 面的纹理贴图(如果有纹理贴图的话 )共同决定。 在默认情况下OpenGL状态机的光照渲染是关闭的。下面的代码开启光照渲染处理,设置光源的各种属性并将该光源开启。 //开启光照渲染 glEnable(GL_LIGHTING); //设置光源属性 GLfloat light_ambient[] = { 1.0 , 1.0 , 1.0 , 0.0 }; GLfloat light_diffuse[] = { 1.0 , 1.0 , 1.0 , 1.0 }; //指定光源的位置 GLfloat light _position[] = { 0.0 , 0.0 , 1.0 , 1.0 }; //用定义好的光源属性给指定光源GL_LIGHT0进行设置 glLightfv(GL_LIGHT0 , GL_AMBIENT , light_ambient); glLightfv(GL_LIGHT0 , GL_DIFFUSE , light_diffuse); glLightfv(GL_LIGHT0 , GL_POSITION , light_position); //开启设置的光源GL_LIGHT0 glEnable(GL_LIGHT0); 在设置光源的位置的时候需要注意Z轴的正方向是垂直于屏幕向外,当设置Z的值为负值的时候,光源的位置位于屏幕内部 ;设置Z为正值的时候,光源的位置位于屏幕的外部。如上面的代码设置的位置是屏幕正中央,垂直于屏幕表面外面1个单 位的位置。 我们如果要打开全局光照,也就是在没有指定光源的时候,让场景中的物体也可以被照亮,可以使用下面的代码。 //全局光照系数 GLfloat globel_ambient[] = { 0.0 , 0.0 , 0.0 , 1.0 }; //打开全局光照
glLightModelfv(GL_LIGHT_MODEL_AMBIENT , globel_ambient); 由 于物体有的表面相对于观察点来说,是背向观察者的,即使光源照到了这些表面上的顶点也不能被观察者看到。但是这 些顶点也在渲染场景的时候,消耗了大量的计 算时间,即使对最后的观察的效果没有任何影响。所以可以设置背面的这些 顶点不进行计算,提高程序的运行效率。添加如下的代码: //关闭背面顶点的计算 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE , GL_FALSE); 只有环境光和漫反射情况下的程序运行效果如下图: 镜面反射项:max{s*n,0}^shiness×SPECULAR_light×SPECULAR_material 其中s为所计算点的到光源连线的单位向量与所计算点到视点的单位向量的和;shiness是材质的光洁度属性 下面的代码设置了镜面反射: GLfloat planet_specular[] = { 0.5 , 0.5 , 0.5 , 0.5 }; glMaterialfv(GL_FRONT , GL_SPECULAR ,planet_specular); 加上镜面反射效果的情况后程序运行效果如下图: 一个3D渲染场景中允许有多个光源,3D场景中物体表面的点的最终颜色由各个光源对该点 的效果叠加。 一个光源对场景中3D物体表面一个顶点的颜色的贡献如下: 顶点颜色 = 环境光颜色 + 衰减因子 × (漫反射项 + 镜面反射项) 我们将对光源的颜色改为红色,代码如下: GLfloat planet_diffuse[] = { 0.7 , 0.0, 0.0, 1.0 }; GLfloat planet_specular[] = { 0.5 , 0.0 , 0.0 , 0.5 }; 对于场景中的单个光源,修改光源颜色后,程序运行效果如下图: 考虑材质自发光颜色属性和多个光源的后,3D物体表面点的颜色的最终由以下式子决定: 顶点颜色 = 材质自发光颜色项 + ∑(环境光颜色+衰减因子×(漫反射项 + 镜面反射项)) 在 真实情况下,往往是有众多的光源。比如天上的月亮,路边的街灯,汽车的车灯,商店的霓虹灯等等。OpenGL支持场景 中添加多个光源,支持在场景中至少使 用8个光源(或者8个以上,者取决于OpenGL的具体实现)。由于OpenGL需要计算 机每个顶点从各个光源收到的光线,因此增加光源将增加运算量,降 低程序运行的效率,但是增加渲染场景的真实性。
现在在场景中再加入一个绿色的光源,光照的效果将由多个光源的光照效果进行叠加。两个光源加入场景后,程序运行的效 果如下图所示: 光照中的纹理 当场景中的3D物体表面有纹理贴图时,纹理图像也将对点的最终颜色产生影响。 我们在前面已经提到,在这种情况下,物体表面点的颜色由场景中的所有光源(包括环境光源),物体的材质,物体表面 的纹理共同确定。 渲染过程是先进行光照处理,再进行纹理处理,纹理处理会影响到最终3D物体表面像素点的颜色。 添加下面的代码,就可以加上纹理贴图: //指定纹理图像文件的目录位置 resource_path = "image/earth.bmp"; //从指定目录装载纹理图像文件 textrue_Resource = auxDIBImageLoad(resource_path); //指定该纹理在进行贴图时的属性,GL_LINEAR和GL_NEAREST说明在贴图 //的时候进行线性插值,使用最近颜色进行匹配,这样将得到比较自然平滑 //的纹理贴图效果 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST); glTexImage2D(GL_TEXTURE_2D,0,GL_RGB, textrue_Resource->sizeX,textrue_Resource->sizeY,0 ,GL_RGB,GL_UNSIGNED_BYTE,textrue_Resource->data); 在程序中加入纹理以后,需要将纹理贴到具体的物体表面,这就要指定纹理坐标。也就是说明纹理图片的那个位置贴到物体 的哪个位置,由于本文探讨的主题是光照,所以不对纹理作详细的说明。在程序中使用简单的纹理坐标映射,使用下面的代 码可以简单方便的对球体实现纹理贴图: //绑定我们之前定义好的纹理 glBindTexture(GL_TEXTURE_2D, textrue_Resource); //用于自动纹理映射的二次曲面对象 GLUquadricObj* g_text= gluNewQuadric(); //绘制半径为0.3的球体,同时使用 gluSphere(g_text,0.3,64,64); 第 一条语句将当前纹理设置为我们之前定义好的纹理对象。第二条语句生成一个帮助产生纹理映射坐标的对象,第三条语 句绘制一个半径为0.3大小的球体,同时使 用GLUquadricObj的对象产生纹理坐标,使用产生的纹理坐标进行纹理贴图。这 两个函数时GLUT库里面的函数,在使用的时候需要将GLUT库包 含进来。 //包含GLUT库 #include #pragma comment(lib,"glut32.lib") 当有两个光源的情况下,再加上纹理贴图后,程序运行的效果如下:
三维太阳系模拟程序(Solar System)介绍 在 掌握了光照的基本知识后,可以使用这些知识完成一个简单模拟太阳系的练习程序。加上各种光照效果,纹理贴图以及 相应的交互操作处理。这个程序将真正综合运 用我所介绍的各种光照处理,以及交互操作,坐标系转换,定时器设置,星 球运动轨迹设置,星球运行轨道绘制,公转自转运动实现等内容,可以算一个真正意义的 上的一个完整的三维程序,下面 我将介绍这个程序的实现和要注意的问题。 下面的代码,可以实现Z缓 存检测,这样就会先绘制离屏幕远的物体后绘制离开屏幕近的物体,避免被前面挡住的物体也绘 制出来;只对外面的多边形进行渲染,对内侧的多边不进行渲染处 理,提高程序运行效率;设置投影方式为正交投影,符 合我们现实生活观察物体的真实成像方式;设置视角大小,也就是设置可以观察到的视野范围的角度;开启纹 理处理,为 各个天体贴上纹理贴图增加真实感。如下面的代码: //开启深度Z缓存 glEnable(GL_DEPTH_TEST); //设置只有正面多边形进行光照计算 glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,GL_FALSE); //设置正交投影 glMatrixMode(GL_PROJECTION); //设置视角大小 gluPerspective(45.0,(GLfloat)(SCREEN_WITH)/(GLfloat)(SCREEN_HEIGHT),0.01,10000000000); //开启纹理效果 glEnable(GL_TEXTURE_2D); //开启纹理映射对象的纹理映射开关 gluQuadricTexture(g_text,GL_TRUE); 综合演示程序的运行效果如下图所示:
该演示程序实现了简单的太阳系模拟,几个主要的行星围绕着中心的太阳进行公转,行星自己 也进行着自转,地球的卫星 月亮围绕着地球旋转,同时卫星也进行自转。场景中有一个光源--太阳,同时开启了全局环境光线,所以在关闭唯一的光 源(太阳) 后,这个太阳系统仍然可以看见各个星体,只是非常暗(因为我设置的全局环境光很弱)。程序开始运行的时 候,可以选择是在全屏模式或者在窗口模式下运行。程 序在运行中可以使用键盘控制观察点进行前后左右的平移动,使用 鼠标控制观察点所朝向的方向,操作方式和常见的第一人称射击游戏相同。 演示程序的具体操作如下: S 向后平移 A 向左平移 D 向右平移 L 开启/关闭太阳光源 鼠标控制方向 退出程序 #c/c++ 评论 浏览(3750) 评论(1) 转载
cyberniuniu 好文! 2010-07-26 09:53 帮助中心 | 空间客服 | 投诉中心 | 空间协议 ©2012 Baidu 发布 回复
分享到:
收藏