Quantcast
Channel: 入门 – Delbert's Blog
Viewing all articles
Browse latest Browse all 10

[OpenGL](7)使用光照来表现立体感

$
0
0

OpenGL Logo

Delbert 按:本系列文章开始写作于 2006 年,终稿(未完成)于 2008 年,已经与当前的 OpenGL 版本有较大差异,仅供入门参考,如需应用,请自行参考手册。 另:Delbert 已经取得原作者授权,转载请以超链接形式注明出处。

从生理学的角度上讲,眼睛之所以看见各种物体,是因为光线直接或间接的从它们那里到达了眼睛。人类对于光线强弱的变化的反应,比对于颜色变化的反应来得灵敏。因此对于人类而言,光线很大程度上表现了物体的立体感。

请看图1,图中绘制了两个大小相同的白色球体。其中右边的一个是没有使用任何光照效果的,它看起来就像是一个二维的圆盘,没有立体的感觉。左边的一个是使用了简单的光照效果的,我们通过光照的层次,很容易的认为它是一个三维的物体。

OpenGL_7_1

OpenGL对于光照效果提供了直接的支持,只需要调用某些函数,便可以实现简单的光照效果。但是在这之前,我们有必要了解一些基础知识。

一、建立光照模型

在现实生活中,某些物体本身就会发光,例如太阳、电灯等,而其它物体虽然不会发光,但可以反射来自其它物体的光。这些光通过各种方式传播,最后进入我们的眼睛——于是一幅画面就在我们的眼中形成了。

就目前的计算机而言,要准确模拟各种光线的传播,这是无法做到的事情。比如一个四面都是粗糙墙壁的房间,一盏电灯所发出的光线在很短的时间内就会经过非常多次的反射,最终几乎布满了房间的每一个角落,这一过程即使使用目前运算速度最快的计算机,也无法精确模拟。不过,我们并不需要精确的模拟各种光线,只需要找到一种近似的计算方式,使它的最终结果让我们的眼睛认为它是真实的,这就可以了。

OpenGL在处理光照时采用这样一种近似:把光照系统分为三部分,分别是光源、材质和光照环境。光源就是光的来源,可以是前面所说的太阳或者电灯等。材质是指接受光照的各种物体的表面,由于物体如何反射光线只由物体表面决定(OpenGL中没有考虑光的折射),材质特点就决定了物体反射光线的特点。光照环境是指一些额外的参数,它们将影响最终的光照画面,比如一些光线经过多次反射后,已经无法分清它究竟是由哪个光源发出,这时,指定一个“环境亮度”参数,可以使最后形成的画面更接近于真实情况。

在物理学中,光线如果射入理想的光滑平面,则反射后的光线是很规则的(这样的反射称为镜面反射)。光线如果射入粗糙的、不光滑的平面,则反射后的光线是杂乱的(这样的反射称为漫反射)。现实生活中的物体在反射光线时,并不是绝对的镜面反射或漫反射,但可以看成是这两种反射的叠加。对于光源发出的光线,可以分别设置其经过镜面反射和漫反射后的光线强度。对于被光线照射的材质,也可以分别设置光线经过镜面反射和漫反射后的光线强度。这些因素综合起来,就形成了最终的光照效果。

二、法线向量

根据光的反射定律,由光的入射方向和入射点的法线就可以得到光的出射方向。因此,对于指定的物体,在指定了光源后,即可计算出光的反射方向,进而计算出光照效果的画面。在OpenGL中,法线的方向是用一个向量来表示。

不幸的是,OpenGL并不会根据你所指定的多边形各个顶点来计算出这些多边形所构成的物体的表面的每个点的法线(这话听着有些迷糊),通常,为了实现光照效果,需要在代码中为每一个顶点指定其法线向量。

指定法线向量的方式与指定颜色的方式有雷同之处。在指定颜色时,只需要指定每一个顶点的颜色,OpenGL就可以自行计算顶点之间的其它点的颜色。并且,颜色一旦被指定,除非再指定新的颜色,否则以后指定的所有顶点都将以这一向量作为自己的颜色。在指定法线向量时,只需要指定每一个顶点的法线向量,OpenGL会自行计算顶点之间的其它点的法线向量。并且,法线向量一旦被指定,除非再指定新的法线向量,否则以后指定的所有顶点都将以这一向量作为自己的法线向量。使用glColor*函数可以指定颜色,而使用glNormal*函数则可以指定法线向量。

注意:使用glTranslate* 函数或者glRotate*函数可以改变物体的外观,但法线向量并不会随之改变。然而,使用glScale*函数,对每一坐标轴进行不同程度的缩放,很有可能导致法线向量的不正确,虽然OpenGL提供了一些措施来修正这一问题,但由此也带来了各种开销。因此,在使用了法线向量的场合,应尽量避免使用 glScale*函数。即使使用,也最好保证各坐标轴进行等比例缩放。

三、控制光源

在OpenGL中,仅仅支持有限数量的光源。使用GL_LIGHT0表示第0号光源,GL_LIGHT1表示第1号光源,依次类推,OpenGL至少会支持8个光源,即GL_LIGHT0到GL_LIGHT7。使用 glEnable函数可以开启它们。例如,glEnable(GL_LIGHT0);可以开启第0号光源。使用glDisable函数则可以关闭光源。一些OpenGL实现可能支持更多数量的光源,但总的来说,开启过多的光源将会导致程序运行速度的严重下降,玩过3D Mark的朋友可能多少也有些体会。一些场景中可能有成百上千的电灯,这时可能需要采取一些近似的手段来进行编程,否则以目前的计算机而言,是无法运行这样的程序的。

每一个光源都可以设置其属性,这一动作是通过glLight*函数完成的。glLight*函数具有三个参数,第一个参数指明是设置哪一个光源的属性,第二个参数指明是设置该光源的哪一个属性,第三个参数则是指明把该属性值设置成多少。光源的属性众多,下面将分别介绍。

  1. GL_AMBIENT、 GL_DIFFUSE、GL_SPECULAR属性。这三个属性表示了光源所发出的光的反射特性(以及颜色)。每个属性由四个值表示,分别代表了颜色的 R, G, B, A值。GL_AMBIENT表示该光源所发出的光,经过非常多次的反射后,最终遗留在整个光照环境中的强度(颜色)。 GL_DIFFUSE表示该光源所发出的光,照射到粗糙表面时经过漫反射,所得到的光的强度(颜色)。GL_SPECULAR表示该光源所发出的光,照射到光滑表面时经过镜面反射,所得到的光的强度(颜色)。
  2. GL_POSITION属性。表示光源所在的位置。由四个值(X, Y, Z, W)表示。如果第四个值W为零,则表示该光源位于无限远处,前三个值表示了它所在的方向。这种光源称为方向性光源,通常,太阳可以近似的被认为是方向性光源。如果第四个值W不为零,则X/W, Y/W, Z/W表示了光源的位置。这种光源称为位置性光源。对于位置性光源,设置其位置与设置多边形顶点的方式相似,各种矩阵变换函数例如:glTranslate*、glRotate*等在这里也同样有效。方向性光源在计算时比位置性光源快了不少,因此,在视觉效果允许的情况下,应该尽可能的使用方向性光源。
  3. GL_SPOT_DIRECTION、 GL_SPOT_EXPONENT、GL_SPOT_CUTOFF属性。表示将光源作为聚光灯使用(这些属性只对位置性光源有效)。很多光源都是向四面八方发射光线,但有时候一些光源则是只向某个方向发射,比如手电筒,只向一个较小的角度发射光线。GL_SPOT_DIRECTION属性有三个值,表示一个向量,即光源发射的方向。GL_SPOT_EXPONENT属性只有一个值,表示聚光的程度,为零时表示光照范围内向各方向发射的光线强度相同,为正数时表示光照向中央集中,正对发射方向的位置受到更多光照,其它位置受到较少光照。数值越大,聚光效果就越明显。GL_SPOT_CUTOFF属性也只有一个值,表示一个角度,它是光源发射光线所覆盖角度的一半(见图2),其取值范围在0到90之间,也可以取180这个特殊值。取值为180时表示光源发射光线覆盖360度,即不使用聚光灯,向全周围发射。
  4. GL_CONSTANT_ATTENUATION、GL_LINEAR_ATTENUATION、 GL_QUADRATIC_ATTENUATION属性。这三个属性表示了光源所发出的光线的直线传播特性(这些属性只对位置性光源有效)。现实生活中,光线的强度随着距离的增加而减弱,OpenGL把这个减弱的趋势抽象成函数:
    衰减因子 = 1 / (k1 + k2 * d + k3 * k3 * d)
    其中d表示距离,光线的初始强度乘以衰减因子,就得到对应距离的光线强度。k1, k2, k3分别就是 GL_CONSTANT_ATTENUATION, GL_LINEAR_ATTENUATION, GL_QUADRATIC_ATTENUATION。通过设置这三个常数,就可以控制光线在传播过程中的减弱趋势。

OpenGL_7_2

属性还真是不少。当然了,如果是使用方向性光源,(3)(4)这两类属性就不会用到了,问题就变得简单明了。

四、控制材质

材质与光源相似,也需要设置众多的属性。不同的是,光源是通过glLight*函数来设置的,而材质则是通过glMaterial*函数来设置的。

glMaterial* 函数有三个参数。第一个参数表示指定哪一面的属性。可以是GL_FRONT、GL_BACK或者GL_FRONT_AND_BACK。分别表示设置“正面”“背面”的材质,或者两面同时设置。(关于“正面”“背面”的内容需要参看前些课程的内容)第二、第三个参数与glLight*函数的第二、三个参数作用类似。下面分别说明glMaterial*函数可以指定的材质属性。

  1. GL_AMBIENT、GL_DIFFUSE、 GL_SPECULAR属性。这三个属性与光源的三个对应属性类似,每一属性都由四个值组成。GL_AMBIENT表示各种光线照射到该材质上,经过很多次反射后最终遗留在环境中的光线强度(颜色)。GL_DIFFUSE表示光线照射到该材质上,经过漫反射后形成的光线强度(颜色)。 GL_SPECULAR表示光线照射到该材质上,经过镜面反射后形成的光线强度(颜色)。通常,GL_AMBIENT和GL_DIFFUSE都取相同的值,可以达到比较真实的效果。使用GL_AMBIENT_AND_DIFFUSE可以同时设置GL_AMBIENT和GL_DIFFUSE属性。
  2. GL_SHININESS属性。该属性只有一个值,称为“镜面指数”,取值范围是0到128。该值越小,表示材质越粗糙,点光源发射的光线照射到上面,也可以产生较大的亮点。该值越大,表示材质越类似于镜面,光源照射到上面后,产生较小的亮点。
  3. GL_EMISSION属性。该属性由四个值组成,表示一种颜色。OpenGL认为该材质本身就微微的向外发射光线,以至于眼睛感觉到它有这样的颜色,但这光线又比较微弱,以至于不会影响到其它物体的颜色。
  4. GL_COLOR_INDEXES属性。该属性仅在颜色索引模式下使用,由于颜色索引模式下的光照比RGBA模式要复杂,并且使用范围较小,这里不做讨论。


 除非特别注明,均为 Delbert 原创文章,转载请注明: 转载自 Delbert's Blog

 [OpenGL](7)使用光照来表现立体感链接地址: http://delbert.me/archives/541.html

 本作品采用知识共享署名-非商业性使用-相同方式共享 3.0 Unported许可协议进行许可。


Viewing all articles
Browse latest Browse all 10

Trending Articles