一. 現實中的光照模型
1. 顏色是什麼?在沒有學過光學的人的認識中,顏色是物體的自身屬性,很多的物體的顏色是一成不變的,比如雪是白色,樹葉是綠色,血液是紅色。但如果我們試着呆在一 個隔絕任何光線的純黑小屋中,我們會發現所有顏色都消失了,於是我們可以得到這样的結論:顏色並不是這样物體自身的屬性,不同的顏色,是物體表面光學特性 和光線相互作用的結果。
日光是所有可見光的集合,照射與物體表面時,某些波長的光被吸收,某些波長的光線則被反射,反射的光線最終進入人的眼睛,投影在視網膜,形成了顏色。比如紅色就是物體表面反射紅光,吸收其餘光線的結果。
2. 物體表面和光線不同夾角對顏色產生的影響:
我們都有這样的經曆,玻璃杯,瓷器等表面光滑的物件,無論表面原來的顏色是什麼,在日光下總會有一塊純白的“高光”區域,並且這個區域會隨着光照方 向的變化或者人觀察角度的變化,在物體表面移動。說明對於表面光滑的物體而言,顏色不但和表面特性,光線相關,還和光照角度和觀察角度相關。
3. 物體表面光滑度對顏色的影響:
同一束光線,照在同样顏色的光滑表面上和粗糙表面上,視覺效果也是不同的,明顯的區別是,在光滑表面上,通常都有那種前面說的高光效果,而且越光滑 越明顯,在粗糙表面上,那種高光效果消失了,顏色的分布變的很均勻,顏色的變化也變的很平滑, 改變光線照射的角度,依然會對顏色的分布產生影響,但是似乎已經和觀察角度無關了。這種反射,被稱为漫反射,實際上漫反射依然嚴格遵循光線的反射和折射定 律,只不過光線在接觸粗糙表面時,在微觀尺度上表面的不規則形狀之間進行了很多次的鏡面反射,因此出射角度已經隨機分布。 不過在計算機處理漫反射時,如果對微觀尺度上的鏡面反射進行完整的模擬,計算量過於龐大,所以會將其近似为均勻向所有方向散射。
4. 自發光物體在光線中產生的顏色:
如果我們要模擬的物體本身是個光源,那麼它在外部光源下會呈現什麼? 我們可以自己動手實驗 - 比如將發綠光的物體,置於紅色外部光源下,可以觀察到黃色。因此我們可以得到的結論是自發光體的顏色由自發光顏色和外部光源叠加產生。
运行 OpenGL 程序在屏幕上显示的最终颜色,受场景中光线的特性以及物体反射和吸收光的属性(即材质)影响。
在OpenGL 光照模型中光源和光照效果可以细分为红,绿,蓝三个部分,光源由红,绿,蓝强度来定义,而物体表面材料由其反射红,绿,蓝的程度和方向来定义。OpenGL 光照模型使用的计算公式是对于现实世界光照的一个近似但效果非常好并适合快速计算。
OpenGL 光照模型中定义的光源可以分别控制,打开或关闭,OpenGL ES支持最多八个光源。
OpenGL 光照模型中最终的光照效果可以分为四个组成部分:Emitted(光源), ambient(环境光),diffuse(漫射光)和specular(镜面反射光),最终结果由这四种光叠加而成。
1. Emitted (emission)发射光: 一般只发光物体或者光源,这种光不受其它光源的影响。
2. ambient 環境光: 指光线经过多次反射后已经无法得知其方向(可以看作来自所有方向),可以成为环境光,该光源如果射到某个平面,其反射方向为所有方向。 Ambient 不依赖于光源的方向。就是哪些在环境中进行了充分散射的光,而无法分辨其方向的光。光线在物体表面上向各个方向上均匀泛射,场景中的物体都会泛射光,这些泛射光又会照射到其他 物体上继续被泛射,直到光子能量耗尽为止,这样整个场景中散布着这样的泛射光。在编程时,可通过设置一个颜色常量来表示环境光或使用 ambient occlusion map (环境闭包贴图)来处理环境光。在 OpenGL 中,全局环境光的强度为 (0.2, 0.2, 0.2, 1.0),这弱弱的白色全局环境光确保即使没有额外的光源,场景中的物体依然是可见的。在白天非陽光直射,和外部光源很多的情況下,現實環境中會彌散着幾乎一致的亮度,OpenGL將這部分光稱作環境光,它给3D世界整體亮度設定了基線。
3. diffuse 漫反射光:当一束平行的入射光线射到粗糙的表面时,因面上凹凸不平,所以入射线虽然互相平行,由于各点的法线方向不一致,造成反射光线向不同 的方向无规则地反射,这种反射称之为“漫反射”或“漫射”。这个反射的光则称为漫射光。漫射光射到某个平面时,其反射方向也为所有方向。diffuse 只依赖于光源的方向和法线的方向。因此為只和表面與光線入射夾角有關的光線。我们之所以能看到物体,就是因为物体将入射的光然后向各个方向反射(所以称之为漫反射)。物体的漫反射材质属性对物体的颜色起着决定性作用。
4. specular 鏡面反射光: 一般指物体被光源直射的高亮区域,也可以成为镜面反射区,如金属。specular依赖于光源的方向,法线的方向和视角的方向。不但和表面與光線入射夾角有關,而且還和觀察角度有關的光線,並且根據表面光滑程度,呈現不同亮度,越光滑,越容易產生高光區域。在 OpenGL 中,镜面光的强度可通过光泽度(shininess)来调节。
同样的,對應於光線的類型,OpenGL也定義了3種材質屬性(Material):
Ambient,Diffuse, Specular. 每種材質類型的值實際上是代表各自的光線類型在最終合成的亮度上的權重 。也可以看作在物體表面,鏡面反射,漫反射和環境光反射各占多大成分。
尽管光源可能只发送某一频率的光线,但ambient,diffuse和specular可能不同。比如使用白光照射一堵红墙,散射的光线可能为红色。OpenGL允许为光源分别设置红,绿,蓝三个元素的值。
最终决定所看到物体的颜色除了光源的颜色和方向外,还取决于物体本身的颜色,比如红色的光照在红色的物体和蓝色的物体,最终看到的物体一个还是红 色,一个为黑色。OpenGL 中对物体材料(Material)的颜色是通过其反射红,绿,蓝的比例来定义的。 和光源一样,物体的颜色也可以有不同的ambient,diffuse和specular,表现为反射这些光的比例。ambient,diffuse反射 通常为同样的颜色,而specular常常表现为白色或灰色光,如使用白光照射一个红色的球,球的大部分区域显示为红色,而高亮区域为白色。
如何决定入射光和表面的夾角 - 法向量:
OpenGL中的表面,都是由三角形組成的,三角形的三個頂點,可以决定一個平面,凡是平面,就會有法線,所以我們只要知道了這個平面的法向量,那麼
入射光和平面的夾角= 90度-入射光向量和法向量的夾角。
不過不同於前述的一個三角形對應一個法向量,OpenGL中的法線向量是和頂點聯系在一起的(具體原因也許是數據組織的方便性),因此在光照模型 中,需要给每個頂點指定法向量,如不指定,則默認为(0,0,1)。最終三角形內部各個位置的法向量也由3個頂點的法向量線性插值而來。不過通常我們會保 持三角形三個頂點的法向量相同。
二,光照的计算
OpenGL中頂點顏色的最終合成:
我們已經知道了這4種類型的光線,以及它們各自的反射亮度受哪些因素的影響,那麼它們如何互相影響,最終决定一個頂點的顏色呢?
OpenGL给出了一套光線合成的標准公式,不論真實世界的光線是否真由如此組成,但這套公式無疑從經驗上是最接近現實效果的。
以下是OpenGLES1.1的固定渲染管線中,它們的合成公式:
vertex color =
the material emission at that vertex +
the global ambient light scaled by the material's ambient property at that vertex +
the ambient, diffuse, and specular contributions from all the light sources, properly attenuated
最終合成的顏色,即由自發光,全局環境光和所有點光源三部分叠加而成.
Ambient + Diffuse + Specular = Final (圖右)
其中全局環境光的公式为:
global ambient light = ambientlight model * ambientmaterial
點光源部分又由ambient,diffuse,specular三種光源各自的公式相加而成,並且光強按離光源的距離衰減:
contribution = attenuation factor * spotlight effect *
(ambient term + diffuse term + specular term)
另外衰減因子(attenuation factor) 和距離相關,距離越大,衰減因子越小。如不考慮衰減,則可用常量1代替。
光照计算的终极公式
光照颜色 = 发射颜色 + 全局环境颜色 + (环境颜色 + 漫反射颜色 + 镜面反射颜色) × 聚光灯效果 × 衰减因子如果场景中有多个光源(包括环境光),那么分别计算来每个光源的光照颜色,然后把这些光照颜色累加即可。如果物体不发射光,则没有发射颜色这一成分。
以下是個別次項目的計算公式
1. 发射光计算
前面说过,在 OpenGL 中是给物体材质设置发射颜色来模拟发射光的,因此它的计算非常简单:发射颜色 = 物体的发射材质颜色
2. 环境光计算
前面说到,一般我们是设置一个颜色常量来表示环境光(或来自 ambient occlusion map),因此环境光的计算也是很简单的。其中環境光源公式:
ambient term=ambientlight *ambientmaterial
(环境颜色 = 光源的环境光颜色 × 物体的环境材质颜色)
3. 漫反射光计算
散射光源公式:漫反射颜色 = 光源的漫反射光颜色 × 物体的漫反射材质颜色 × DiffuseFactor
其中漫反射因子 DiffuseFactor 是光线与顶点法线向量的点积:
(df)DiffuseFactor = max(0, dot(N, L)) *** 0 <= df <= 1
在图形学中,点积几何意义其实就是表示两个向量之间夹角的 cos 值。因此这个公式直观地揭示了漫反射的规律:顶点法线正对入射光线,漫反射效果最强,顶点法线背对入射光线(角度大于等于90度)就完全没有漫反射效果。
In the diagram, L is the unit length vector pointing to the light source, and N is the surface normal, which is a unit-length vector that's perpendicular to the surface.
注意:在光照计算中,顶点法线必须是经过规范化的(normalize)。
如果將光源設在無限遠處,則可以簡化公式如下
我们可以设置一个颜色常量来表示漫反射材质颜色,来得到平滑颜色的表面。但有些物体表面是很粗糙的,如裂缝,隆起,刮痕等。为了获得这样的粗糙表面 效果,可以使用 displaced polygon 技术,但这需要大量的计算,效率低下。另外一种更为高效的方式就是使用凹凸贴图(bump map)。凹凸贴图就是一种纹理,其内容包含编码在RGB颜色空间中的经过扰动的顶点法线。在使用凹凸贴图进行漫反射计算时时,首先和前面一样用正常法线 计算出 NormalDiffuseFactor,再根据扰动的顶点法线计算出 PerturbedDiffuseFactor,然后将两者相乘作为最终的 DiffuseFactor。如下图所示:
4. 鏡面光源公式:
镜面反射颜色 (Specular Lighting)= 光源的镜面光颜色 × 物体的镜面材质颜色 × SpecularFactorSpecularFactor = power(max(0, dot(N, H)), shininess)
H = normalise(L + E)
當觀眾在無限遠處時,E可以簡化成 [0, 0, 1]
与漫反射不同,镜面反射受观察者的位置影响,这一点在上面的计算公式中可以清楚地看出来。
1. H 向量是视线向量 E 与光线向量 L 的半向量(注意:它经过规划化的)H向量又稱為半角(half-angle),因為它是位於E與L的中間。
2. L代表的是光線入射方向的反向,其几何意义就是视线与光线夹角的平分线。
3. 而 H 和 N 的点积的几何意义就是说这个平分线与法线的夹角的 cos 值,然后将这个 cos 值进行 shininess 次乘方计算得到最终的镜面反射因子 Specularfactor。
这里的关键点在于视线与光线的平分线与法线的点积计算上,这表示,当视线与光线在表面处的反射光线夹角(可看成是N与H的夹角)越少时,镜面反射效果最明显(角度越小,cos 值越大)。
一般可使用常量颜色作为镜面材质颜色,但这样得到的效果有时候并不理想。同漫反射一样,我们也可以使用镜面贴图来获得更好的镜面反射效果。镜面贴图通过控制物体表示上特定像素允许的镜面反射强度来获得较为真实的效果。
普通镜面反射 镜面贴图反射
5. 衰减因子
光源发射的光线在其传播过程中,会与空气中的其他粒子碰撞,其能量会逐渐衰减。在 OpenGL 中,这是通过将光照强度乘以随传播距离变化的衰减因子来模拟实现的。这个衰减因子的计算公式如下:衰减因子 = 1.0/(距离衰减常量 + 线性衰减常量 × 距离 + 二次衰减常量 × 距离的平方)
其中距离衰减常量,线性衰减常量和二次衰减常量均为常量值。
注意:环境光,漫反射光和镜面光的强度都会受随着距离的增大而衰减,只有发射光和全局环境光的强度不会受此影响。
6. 聚光灯因子
在这里,我们讨论聚光灯的发射光照计算(也即位置型光源:如台灯,相对方向性光源:如太阳)。聚光灯就是朝某个特定发射发射光线的光源。你可以想象 下漆黑的夜晚里,一个手电筒给与你光明的这个场景,手电筒就是一个很好的聚光灯示例。聚光灯的计算分为两部分:在光线照射角度范围之外的部分被忽略,只有 在照射角度范围之内的部分需要计算。聚光灯夹角cos值 = power(max(0, dot(单位光源向量, 单位光线向量)), 聚光灯指数)
其中单位光线向量是从光源指向顶点的单位向量,聚光灯指数表示聚光灯的亮度程度。前面说过点积的几何意义就是表示角度的,这里聚光灯因子就表示光源向量与光线向量之间的夹角。
而为了模拟真实聚光灯光环效果,在照射角度范围之内与之外的接壤处,设置一个渐变过渡区域,以避免光照从有到无巨变:
无过渡
|
有过渡
|
内环是完全光照的,而过渡区域(中环)是从完全光照到完全没有光照的渐变过渡,外环是完全没有光照的。
聚光灯因子 = clamp((外环的聚光灯角度cos值 - 当前顶点的聚光灯角度cos值)/(外环的聚光灯角度cos值 - 内环聚光灯角度cos值), 0, 1)
clamp 函数是将聚光灯因子限定在[0, 1]之间,这样,内环是完全光照的,而中环是从完全光照到没有光照的渐变过渡,外环是没有光照的。因此:
聚光灯效果 = 聚光灯光源颜色 × 聚光灯因子
三,高洛德着色(Gouraud Shading)与冯氏着色(Phong Shading)
在图形渲染中有两种着色方式,高洛德着色与冯氏着色。高洛德着色也被称为Per-Vertex着色,它是在顶点着色阶段对顶点进行颜色计算,然后在 光栅化阶段对这些顶点颜色进行线性插值形成片元的颜色;冯氏着色也被称为Per-Pixel像素着色,它是在片元着色阶段对每一个片元(像素)进行颜色计 算。无疑,插值的颜色效果没有针对每一个片元进行颜色计算的效果好(除非你的图元切分到像素近似大小,不过这样 GPU 肯定吃不消,计算量巨大!)。| Gouraud 着色 | Phong 着色 |
參考網頁
http://www.cnblogs.com/kesalin/archive/2012/12/29/light_theroy.html
http://rritw.com/a/bianchengyuyan/C__/20130306/318310.html
沒有留言:
張貼留言