write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
最近跟風(fēng),看了《思維導(dǎo)圖》,用XMind為此節(jié)畫了個(gè)思維導(dǎo)圖,事實(shí)上感覺這種書說起來不一定完全沒有用,但是為了顯示有用似乎說的太過夸張了,基本上,我認(rèn)可一圖勝千言吧。。。。另外,XMind還算比較好用,特別是上傳然后共享的方式比較方便:)基礎(chǔ)版還開源。
上面這種圖用于解釋概念就略顯簡單,但是用于梳理脈絡(luò),復(fù)習(xí)時(shí)回憶概念還算是比較有用的。
上帝說,要有光,就有了光。---- 《舊約?創(chuàng)世紀(jì)》
使用OpenGL時(shí),程序員可以暫時(shí)充當(dāng)一下上帝的角色(其實(shí)犯了妄自稱上帝的戒律),你對(duì)電腦說要有光,于是,顯示的時(shí)候就有了光,呵呵。當(dāng)然,說的時(shí)候需要用電腦的語言。
相對(duì)來說,光照在OpenGL中是屬于那種概念理解比較簡單,但是使用卻比較復(fù)雜的功能之一,因?yàn)楣庹諏儆诒容^容易影響顯示效果的因素,所以光照的用法太多,導(dǎo)致可以使用的定制手段相當(dāng)多,這也就加深了使用的復(fù)雜度,基本上,遵循理解核心概念,需要時(shí)再去查文檔理解每個(gè)函數(shù)的具體參數(shù)使用和意義是種不錯(cuò)的學(xué)習(xí)方式,當(dāng)然,要想精通,恐怕對(duì)各種光照應(yīng)用的情況都得有個(gè)了解才行。上述思維導(dǎo)圖就是核心概念的一個(gè)簡要列表,以下以此圖展開來討論。
為啥需要光
上帝為啥創(chuàng)造光?上帝沒有光看不見東西嗎?。。。。。。。。或者因?yàn)樯系壅J(rèn)為黑暗是不好的,不想呆在黑暗之中。。。。。只有上帝知道
那我們?yōu)樯缎枰饽兀恳驗(yàn)槲覀兊氖澜缬猩系蹌?chuàng)造的光,而OpenGL本質(zhì)上是對(duì)現(xiàn)實(shí)世界的一種建模,既然現(xiàn)實(shí)世界有太陽,有光,那么模擬現(xiàn)實(shí)世界顯示的OpenGL怎么能沒有光呢?(以上純屬無聊的廢話)
以前畫3D圖形的時(shí)候我有意的將物體的每個(gè)面都設(shè)置成不同的顏色(比如以前那個(gè)三角錐),這樣我們才能夠較為清楚的看出來這是個(gè)3D的圖形,為什么會(huì)這樣說呢?那我們先看看事實(shí)上,假如我沒有那樣做,我們看到的是啥。這里以《 Win32 OpenGL編程(6) 踏入3D世界 》一文的glSmoothColorPyramid一例為修改藍(lán)本,(原來的源代碼見/2009-10-21/glSmoothColorPyramid/),將其4面顏色全部設(shè)置為紅色。
即將其源代碼中表示顏色的數(shù)組改為如下形式:
static GLfloat fPyramidColors [] = { 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0, 1.0, 0.0, 0.0};
我們看看效果。
為節(jié)省篇幅僅貼出關(guān)鍵片段,完整源代碼見我博客源代碼的 2009-11-9/glSimpleColorPyramid/目錄,獲取方式見文章最后關(guān)于獲取博客完整源代碼的說明。
事實(shí)上,即便是整個(gè)三角錐正以3D的方式不停的旋轉(zhuǎn),我們看到的效果也類似于一張三角形的紅紙片不停的晃悠而已。3D效果蕩然無存,很顯然,這是有問題的,不能說一個(gè)純色的物體的3D效果會(huì)受到影響吧?這算是我們需要光的最簡單的一層了。一個(gè)三角錐還好說一點(diǎn),我們看看一個(gè)球。此例利用了glut的具體畫實(shí)心體的函數(shù),以后不再全部利用自己的復(fù)雜手工代碼來創(chuàng)造那簡陋的三角錐了,作為一些概念的講解,那樣的三角錐已經(jīng)有點(diǎn)過于簡單了。(當(dāng)然,可以用的時(shí)候還是會(huì)用)因?yàn)槔昧薵lut,畫球就很簡單了(事實(shí)上不用也不難),只需要設(shè)置好紅色,然后用
glutSolidSphere
(0.5, 30, 16);
就能畫出下面這種半徑為窗口寬度1/4的球。
有人能看出來上面的那個(gè)是球?我們看到的僅僅是一個(gè)圓而已,數(shù)學(xué)中我們也常常會(huì)將某個(gè)方向的球的圖形抽象成一個(gè)圓,但是現(xiàn)實(shí)中我們還是很容易看出一個(gè)球就是一個(gè)球而不是一個(gè)圓的面,為啥呢?光啊,因?yàn)楣庹盏拇嬖冢粋€(gè)球在我們眼里事實(shí)上不會(huì)是如上面那樣純紅色的就整個(gè)球都是純紅色,會(huì)根據(jù)光照的方向,強(qiáng)度等因素的不同導(dǎo)致各個(gè)部位顯示不同。下面是一個(gè)加上了光照的紅色球體的例子。(上傳U酷后顏色變了-_-!可能是因?yàn)閁酷在上傳后進(jìn)行了2次壓縮編碼導(dǎo)致的顏色混亂,光源移動(dòng)效果基本一致,需要真實(shí)體驗(yàn)的可以下載源代碼編譯后運(yùn)行)
為節(jié)省篇幅僅貼出關(guān)鍵片段,完整源代碼見我博客源代碼的2009-11-9/LightSimple/ 目錄,獲取方式見文章最后關(guān)于獲取博客完整源代碼的說明。
主要源代碼如下:
//OpenGL初始化開始
void SceneInit( int w, int h)
{
GLenum err = glewInit();
if (err != GLEW_OK)
{
MessageBox(NULL, _T( "Error" ), _T( "Glew init failed." ), MB_OK);
exit(-1);
}
GLfloat mat_specular[] = { 1.0, 0.0, 0.0, 1.0 };
GLfloat mat_shininess[] = { 50.0 };
GLfloat mat_ambient[] = { 1.0, 0.0, 0.0, 1.0 };
GLfloat mat_diffuse[] = { 1.0, 0.0, 0.0, 1.0 };
glMaterialfv(GL_FRONT, GL_SPECULAR, mat_specular);
glMaterialfv(GL_FRONT, GL_SHININESS, mat_shininess);
glMaterialfv(GL_FRONT, GL_AMBIENT, mat_ambient);
glMaterialfv(GL_FRONT, GL_DIFFUSE, mat_diffuse);
glEnable(GL_LIGHTING);
glEnable(GL_LIGHT0);
glColor3f(0.0, 0.0, 0.0);
}
//這里進(jìn)行所有的繪圖工作
void SceneShow(GLvoid)
{
static GLfloat light_position[] = { 1.0, 0.0, -3.0, 0.0 };
static GLfloat angle = 1.0;
glClear(GL_COLOR_BUFFER_BIT); // 清空顏色緩沖區(qū)
glPushMatrix();
glRotatef(angle, 0.0, 1.0, 0.0);
glLightfv(GL_LIGHT0, GL_POSITION, light_position);
glPopMatrix();
angle += 1.0;
glutSolidSphere(0.5, 30, 16);
glFlush();
}
這里啟用光照的順序就像是上面那個(gè)思維導(dǎo)圖中表示的一樣,一下解釋一下各步驟:
1.定義物體的法線
這一步其實(shí)很重要,但是我們?cè)谶@個(gè)例子中看不到,原因在于glutSolidSphere函數(shù)已經(jīng)完成了這樣的定義,法線的定義有點(diǎn)類似我們以前確定物體表面時(shí)用逆/正時(shí)針的頂點(diǎn)指定順序來確定一個(gè)物體的正背面一樣,法線定義了光線入射時(shí)物體相對(duì)于光源的方向,這算是個(gè)額外的小題目,以后有機(jī)會(huì)再論及,可以參考《OpenGL 編程指南》的第2.5節(jié)。基本上知道法線的概念后,需要調(diào)用的就是glNormal*函數(shù),但是法線的計(jì)算并不總是那么容易。。。。。。比如此例的球體,還好,此例因?yàn)槭鞘褂矛F(xiàn)成的函數(shù),省了此步。
2.創(chuàng)建和選擇光源,并設(shè)置位置
glEnable
(
GL_LIGHTING
);
glEnable
(
GL_LIGHT0
);
兩句表示啟用了第0號(hào)光源,此光源默認(rèn)顏色為白色,位置為(0.0,0.0,1.0, 0.0)。我們利用了其默認(rèn)顏色。
glLight*函數(shù)是專門的光照配置控制函數(shù)。
glLight — set light source parameters
C Specification
void glLightf( GLenum light,
GLenum pname,
GLfloat param);
void glLighti( GLenum light,
GLenum pname,
GLint param);
Parameterslight
Specifies a light.
The number of lights depends on the implementation,
but at least eight lights are supported.
They are identified by symbolic names of the form GL_LIGHT
i,
where i ranges from 0 to the value of GL_MAX_LIGHTS - 1.
pnameSpecifies a single-valued light source parameter for light.
GL_SPOT_EXPONENT,
GL_SPOT_CUTOFF,
GL_CONSTANT_ATTENUATION,
GL_LINEAR_ATTENUATION, and
GL_QUADRATIC_ATTENUATION are accepted.
paramSpecifies the value that parameter pname of light source light
will be set to.
事實(shí)上,此函數(shù)遠(yuǎn)遠(yuǎn)不如看起來的那么簡單,上面說了,因?yàn)楣庹諏儆诒容^容易影響顯示效果的因素,所以光照的用法很多,上述的pname參數(shù)指定的n個(gè)值就可見一斑。但是本例中使用的方式卻很簡單,僅僅使用此函數(shù)來設(shè)定0號(hào)光源的位置而已。光照的分類其實(shí)包括環(huán)境光,散射光,鏡面光,發(fā)射光(實(shí)際指物體的發(fā)射顏色emissive color)等屬性,這里不詳述了。
glLightfv ( GL_LIGHT0 , GL_POSITION , light_position );
因?yàn)楣庹漳P偷奈恢弥付ㄊ鞘褂昧四P鸵晥D,所以我們也可以用模型變換的方式來指定光源的位置,就像光源是個(gè)普通的物體一樣,此例中使用了旋轉(zhuǎn)模型視圖的函數(shù)旋轉(zhuǎn)了光源的位置,旋轉(zhuǎn)方式是以Y軸為旋轉(zhuǎn)軸,看到的效果也就是如視頻所示,類似月相變化的動(dòng)畫。
3.創(chuàng)建和選擇光照模型
光照在OpenGL中有兩種模型,一種叫無限遠(yuǎn)觀察者模式,一種叫本地觀察者模式,通過glLightModel*函數(shù)設(shè)置,默認(rèn)為無限遠(yuǎn)觀察者模型,這里沒有修改,即使用了默認(rèn)值,區(qū)別在于是否考慮到觀察者位置對(duì)物體觀察角度不同引起變化,相對(duì)來說本地觀察者模型會(huì)更加逼真一些,但是因?yàn)橛蓄~外的光照計(jì)算,性能也會(huì)受到影響。。。。按照OpenGL管理,默認(rèn)選擇的往往是性能更優(yōu)的,而不是效果更好的。但是,就這么簡單的例子,我看不出區(qū)別。
glLightModel — set the lighting model parameters
C Specification
void glLightModelf( GLenum pname,
GLfloat param);
void glLightModeli( GLenum pname,
GLint param);
Parameterspname
Specifies a single-valued lighting model parameter.
GL_LIGHT_MODEL_LOCAL_VIEWER,
GL_LIGHT_MODEL_COLOR_CONTROL, and
GL_LIGHT_MODEL_TWO_SIDE are accepted.
paramSpecifies the value that param will be set to.
我們可以通過
glLightModeli(GL_LIGHT_MODEL_LOCAL_VIEWER, GL_TRUE);
的調(diào)用切換至本地觀察者模型。
4.定義物體的材料屬性
光是定義了光源而不定義物體怎么反射光,在OpenGL中是不行的,物體的材料屬性決定了其將會(huì)怎么對(duì)光進(jìn)行反應(yīng),與光源屬性對(duì)應(yīng),也有幾種與反射光相關(guān)材料屬性可以設(shè)置。我們通過glMateriall*來設(shè)置材料屬性。
glMaterial — specify material parameters for the lighting model
C Specificationvoid glMaterialfv( GLenum face,
GLenum pname,
const GLfloat * params);
void glMaterialiv( GLenum face,
GLenum pname,
const GLint * params);
Parametersface
Specifies which face or faces are being updated.
Must be one of
GL_FRONT,
GL_BACK, or
GL_FRONT_AND_BACK.
pnameSpecifies the material parameter of the face or faces that is being updated.
Must be one of
GL_AMBIENT,
GL_DIFFUSE,
GL_SPECULAR,
GL_EMISSION,
GL_SHININESS,
GL_AMBIENT_AND_DIFFUSE, or
GL_COLOR_INDEXES.
paramsSpecifies a pointer to the value or values that pname will be set to.
上例中:
GLfloat mat_specular [] = { 1.0, 0.0, 0.0, 1.0 }; GLfloat mat_shininess [] = { 50.0 }; GLfloat mat_ambient [] = { 1.0, 0.0, 0.0, 1.0 }; GLfloat mat_diffuse [] = { 1.0, 0.0, 0.0, 1.0 }; glMaterialfv ( GL_FRONT , GL_SPECULAR , mat_specular ); glMaterialfv ( GL_FRONT , GL_SHININESS , mat_shininess ); glMaterialfv ( GL_FRONT , GL_AMBIENT , mat_ambient ); glMaterialfv ( GL_FRONT , GL_DIFFUSE , mat_diffuse );
這樣8行,定義了4中材料的屬性,并且我們都是使用GL_FRONT定義正面受到光照(即默認(rèn)背面無光照),GL_SPECULAR表示鏡面顏色,GL_SHININESS表示鏡面指數(shù),GL_AMBIENT表示環(huán)境顏色,GL_DIFFUSE表示散射顏色,其中散射顏色和環(huán)境顏色影響物體所反色的散射光和環(huán)境光顏色,(與光源屬性對(duì)應(yīng))有直接光照時(shí),人體主要感知還是以散射顏色為主,無光照時(shí),環(huán)境顏色才會(huì)占重要地位。鏡面顏色決定鏡面反射產(chǎn)生的亮點(diǎn),GL_SHININESS參數(shù)設(shè)置亮點(diǎn)的大小和亮度,發(fā)射光顏色會(huì)讓物體微微發(fā)光(此例未用)。因?yàn)楸容^復(fù)雜,上述文字僅僅大概描述了一個(gè)概念,具體的參數(shù)還是參考《 OpenGL Programming Guide 》 并且實(shí)際的調(diào)整不同數(shù)值體驗(yàn)效果為好。其中,《 OpenGL Programming Guide 》一書中推薦的 Nate Robin的OpenGL教程 的教學(xué)程序程序中有兩個(gè)實(shí)例與光照有關(guān),能夠體驗(yàn)數(shù)值改變時(shí)顯示的效果。假如大家不想自己編程調(diào)試,可以使用教程中的lightposition,lightmateria兩個(gè)例子體驗(yàn)。
本系列其他文章見OpenGL專題 《 Win32 OpenGL系列專題 》
參考資料
1. 《 OpenGL Reference Manual 》,OpenGL參考手冊(cè)
2. 《OpenGL 編程指南》(《 OpenGL Programming Guide 》),Dave Shreiner,Mason Woo,Jackie Neider,Tom Davis 著,徐波譯,機(jī)械工業(yè)出版社
3. 《Nehe OpenGL Tutorials》,Nehe著,在 http://nehe.gamedev.net/ 上可以找到教程及相關(guān)的代碼下載,(有PDF版本教程下載)Nehe自己還做了一個(gè)面向?qū)ο蟮目蚣埽鳛檠菔境绦騺碚f,這樣的框架非常合適。也有 中文版 ,各取所需吧。
4. 《OpenGL入門學(xué)習(xí)》 ,eastcowboy著,這是我在網(wǎng)上找到的一個(gè)比較好的教程,較為完善,而且非常通俗。這是第一篇的地址: http://bbs.pfan.cn/post-184355.html
完整源代碼獲取說明
由于篇幅限制,本文一般僅貼出代碼的主要關(guān)心的部分,代碼帶工程(或者makefile)完整版(如果有的話)都能用Mercurial在Google Code中下載。文章以博文發(fā)表的日期分目錄存放,請(qǐng)直接使用Mercurial克隆下庫:
https://blog-sample-code.jtianling.googlecode.com/hg/
Mercurial使用方法見《 分布式的,新一代版本控制系統(tǒng)Mercurial的介紹及簡要入門 》
要是僅僅想瀏覽全部代碼也可以直接到google code上去看,在下面的地址:
http://code.google.com/p/jtianling/source/browse?repo=blog-sample-code
原創(chuàng)文章作者保留版權(quán) 轉(zhuǎn)載請(qǐng)注明原作者 并給出鏈接
write by 九天雁翎(JTianLing) -- blog.csdn.net/vagrxie
更多文章、技術(shù)交流、商務(wù)合作、聯(lián)系博主
微信掃碼或搜索:z360901061

微信掃一掃加我為好友
QQ號(hào)聯(lián)系: 360901061
您的支持是博主寫作最大的動(dòng)力,如果您喜歡我的文章,感覺我的文章對(duì)您有幫助,請(qǐng)用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點(diǎn)擊下面給點(diǎn)支持吧,站長非常感激您!手機(jī)微信長按不能支付解決辦法:請(qǐng)將微信支付二維碼保存到相冊(cè),切換到微信,然后點(diǎn)擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對(duì)您有幫助就好】元
