OpenGL編程指南第五章:光照

來源:互聯網
上載者:User

1、隱藏面移除

opengl在渲染的時候,需要繪出離視點近的物體,移除被遮擋的遠處的物體。2D繪圖裡面一般通過控制繪製的順序是來達到目的。3D情境要複雜得多,可能存在相互部分遮擋的情況,改變視角也會改變遮擋的情況,opengl通過Depth Buffer來達到消除隱藏部分的目的。

Depth Buffer儲存每個像素的深度值(一般為片段離近剪下面的距離)。初始時或glClear(GL_DEPTH_BUFFER_BIT),depth buffer被初始化成最大值(一般為遠剪下面的深度);在fragment操作視,會對比其深度值和buffer中對應位置的深度值,如果小於,才繼續操作並替換該深度值,否則直接丟棄。要啟用這個功能,調用glEnable(GL_DEPTH_TEST)。

2、關照系統

在真實世界中,物體表面所呈現的顏色,取決於其反射的光線顏色;光源(或多個光源)照射到物體表面的光線,有些被物體吸收,有些被反射;有些物體表面比較光滑,會把入射光線往特定的方向反射,某些則往各個方向散射,大部分物體基於兩者之間。OpenGL通過把光分解為RGB三個分量來類比光照系統:光源的顏色取決於其RBG三個分量的強度,物體表面的材質定義為其往各個方向所反射的RBG三個分量的百分比。

OpenGL中,光來自於相互獨立的若干光源。物體所受的光或者來自某個特定的方向或位置,或者均勻分佈於環境當中。比如房間內有一盞燈,那麼房間內物體所受的大部分光來自於這盞燈;但是還有一些光經過了多次的反射已經無法分別其方向了,但是這些光還是來自這盞燈的,如果這個光源關閉,這些光會消失。還有,環境本身有一些光,這些光無法確定其來源和方向。

環境光線(ambient),漫射光(diffuse),反射光(specular),放射光(emisive)
物體所受的光可以分成以上四個部分,這個四個部分會被單獨計算然後再結合在一起。
環境光線:均勻散佈於環境當中,無法區分方向;環境光線與物體面相接觸會往各個方向均勻反射;
漫射光:來自某個方向,如果直射的話會顯得更加明亮,與物體面接觸後會往各個方向均勻放射;PS:可以看出,這種光對物體的
作用與入射角度是相關的,但與觀察角度無關。
反射光:來自某個方向,往某個方向反射,起高光作用;
放射光:物體本省釋放的光,影響物體自身,不與光源相互作用,也不會給環境增加光。

材料顏色(Material Color)

材料顏色實際上是材料對光的反射比,同樣分成ambient,diffuse,specular三種,每種又按RGB分別取值。ambient值與光照ambient值相結合,同樣diffuse值於光照diffuse值相結合,specular值與光照specular值相結合。材料的ambient和diffuse一般來說是一樣的,這兩個分量的反射(漫射)決定了物體的顏色;而specular值一般就是白色或灰色,也即反射後的顏色即為光照的顏色,用於產生高光效果。打個比方,一個紅球在白色光照下,主要是呈紅色,高光處程白色。

顏色計算

光的顏色RGB和一般顏色RGB值意義相同,而材料的顏色RGB值代表了對光的反射比,前者可用(LR,LG,LB)表示,而後者可用(MR,MG,MB)表示,那麼物體反射的顏色就是(LR*MR,LG*MG,LB*MB)。如果有兩個光源,分別給物體產生(R1,G1,B1)和(R2,G2,B2)兩種顏色,那麼最終物體的顏色是(R1+R2,G1+G2,B1+B2),如果某個值大於1.0,那麼被限制為1.0。

法線

表面法線定義了表面和光源的相方向,會影響到光照的效果;這裡要求法線是正常化的,你需要調用glEnable(GL_NORMALIZE)和glEnable(GL_RESCALE_NORMAL)來開啟法線自動正常化。

3、建立光源

首先要glEnable(GL_LIGHTING)啟用光照系統;再glEnable(GL_LIGHTi)啟用第i個光源。然後就是定義光源的屬性,通過glLight*(GLenum light, GLenum pname, TYPE param)和glLight*v(GLenum light, GLenum
pname, const TYPE *param)兩個API來設定屬性值:light是光源身份也即GL_LIGHT0,GL_LIGHT1...,pname是屬性名稱;param是屬性值。
屬性列表及其預設值請參考原書,值得注意的是GL_DIFFUSE和GL_SPECULAR兩個屬性的預設值對GL_LIGHT0和其他光源不一樣。

Color

給可以給光源的GL_AMBIENT,GL_DIFFUSE,GL_SPECULAR三個分量設定顏色值,比如:
GLfloat light_ambient[] = { 0.0, 0.0, 1.0, 1.0};
glLightfv(GL_LIGHT0, GL_AMBIENT, light_ambient);

位置和方向

從位置的角度講,有兩種光源,一種是directional光源,它位於無窮遠處,所有光線的方向是一致的,也不會衰減,比如“太陽光”。另一種是positional光源,它處於情境中,它的具體位置會影響光照效果。
給光源設定位置的API如下:
GLfloat light_position[] = { 1.0, 1.0, 1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
position的第四個分量如果是0,那麼光源就是directional的,postion值實際是一個向量;否則這個光源就是postional,position就是他的位置。光源的位置和方向都會受ModelView Matrix的影響。

衰減

物體離光源越遠,光的強度會衰減,對於directional光源來說衰減是沒有意義的,所以衰減是針對positional光源來說的。衰減就是用光源的強度乘以一個衰減因子,衰減因子的公式如下:

d = 頂點和光源的距離
Kc = GL_CONSTANT_ATTENUATION
Kl = GL_LINEAR_ATTENUATION
Kq = GL_QUADRATIC_ATTENUATION
Kc的預設值是1.0,Kl和Kq的預設值是0,可以通過API設定:
glLightf(GL_LIGHT0, GL_CONSTANT_ATTENUATION, 2.0);
glLightf(GL_LIGHT0, GL_LINEAR_ATTENUATION, 1.0);
glLightf(GL_LIGHT0, GL_QUADRATIC_ATTENUATION, 0.5);

聚光
可以給positional光源添加聚光效果,聚光就是指定光源照射的方向和範圍(一個椎體):
GLfloat spot_direction[] = { -1.0, -1.0, 0.0 };
glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, spot_direction);
上面指定了光源照射的方向,也即椎體的軸線方向;
glLightf(GL_LIGHT0, GL_SPOT_CUTOFF, 45.0);
上面制定了椎體邊界和周線之間的夾角,如果角度為180的話,相當於沒有聚光效果。

還可以給聚光增加中軸到邊緣的衰減效果,通過GL_SPOT_EXPONENT屬性。

控制光源位置和方向
OpenGL對待光源位置和方向與對待頂點位置和法線是一樣的,都會被ModelView Matrix轉換成眼座標系座標,這一節講述如何控制光源位置以滿足幾種常見的要求:
1)固定光源位置:把光源放到某個固定位置,不受其他動作的影響;再設定好視點之後,立即指定光源位置。
2)獨立地移動光源:一般在display中設定光源位置,只要使用glPushMatrix和glPopMatrix不難做到。
3)隨視點移動光源:在指定視點之前,指定光源位置,此後動態調整視點時,光源相對位置不變。註:指定視點之前指定的位置,從全局座標系轉換到眼座標系時不會發生變化,因此其相對視點的位置也就沒有變化。

設定光照模式

設定光照模式的API是glLightModel{if}(GLenum pname, TYPE param)或void glLightModel{if}v(GLenum pname, const TYPE *param),pname是屬性名稱字。

全域環境光線(global ambient):與光源無關的環境光線,名字是GL_LIGHT_MODEL_AMBIENT

本地或無限遠的視點(local or Infinit viewport):在計算高光效果時,頂點的高光強度取決於三個方向:光源和頂點的相對方向、法線方向、視點和頂點的相對方向;不同的頂點和視點的相對方向是不一樣的,使用無限遠的視點就是忽略這種不一致,使用一個固定值,以提升效率。這裡的設定並不會真的改變視點位置,只是針對光照而已。Opengl預設是使用Infinit
Viewport的,改變使用glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE)。

背面光照:Opengl預設只會對多邊形的front-face做關照處理,某些情況下可能需要對back-face做關照,比如一個球體被剪下一部分,此時需要使用下面的API:glLightModeli(GL_LIGHT_MODEL_TWO_SIDE,
GL_TRUE);

延遲高光處理:OpenGL流水線中,光照處理髮生在紋理處理之後,這樣高光效果就會被極大弱化。為了是高光效果明顯一些,就要延遲一下高光處理,開啟方法:glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,GL_SEPARATE_SPECULAR_COLOR),
這樣在做光照處理時,先合成ambient,diffuse,emissve分量至頂點顏色,這個顏色角primary color,specular分量產生的顏色角secondary color,暫存起來;等到texture完成後,再將specular值secondary加至頂點顏色。關閉該功能:glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL,
GL_SINGLE_COLOR)。


4、定義材料屬性

定義材料屬性使用API:
void glMaterial{if}(GLenum face, GLenum pname, TYPE param);
void glMaterial{if}v(GLenum face, GLenum pname, const TYPE *param);
face指物體正面或背面有,pname是屬性名稱,可能的值有:GL_AMBIENT,GL_DIFFUSE,GL_AMBIENT_AND_DIFFUSE,GL_SPECULAR,GL_EMISSION,GL_SHININESS(高光度)。
Diffuse和Ambient很大程度上決定了物體的最終顏色;
Sepcular效果與視點的位置有極大關係,如果視角方向與反射的方向一致,則高光效果最明顯;
Shiniess用來控制高光的地區和明亮度,值範圍是[0.0,128.0],值越大,則高光地區越小,顏色越亮;
emission會使物體有一種發光的效果,但並不能起到光源的作用。

5、光照數學計算

第一行:物體自身釋放的光顏色;
第二行:全域環境光線對物體產生的顏色;
第三行:衰減和聚光效果,衰減前面已經講過,聚光效果的值分一下幾種情況:1)放射角度為180,此時沒有聚光效果,值為1;2)頂點在聚光椎體之外,值為0;3)為max(v*d,0)對GL_SPOT_EXPONENT求冥,v是從光源位置到頂點的單位向量,d是聚光源的中軸方向;
第四行:光源i的環境光線效果
第五行:光源i散射光產生的效果,L是從頂點到光源的單位向量,n是頂點法線向量;
第六行:光源i產生的高光效果,S是(從頂點到光源的單位向量+從頂點到視點的單位向量)結果的正常化,n是頂點法線向量,從“頂點到視點的單位向量”這一項如果GL_LIGHT_MODEL_LOCAL_VIEWER沒有開啟的話取值為(0,0,1)。

如果啟用GL_LIGHT_SECONDARY_COLOR的話,只需要把specular部分提取出來作為secondary Color,剩下的是primary Color。


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.