亂彈OpenGL中的矩陣變換(上)
在前面的日志(Shadow Volume 陰影錐技術(shù)之探Ⅲ )中,自己稍微提及了NEHE的"3D矩陣求逆"方法之高,并談了談自己的一點(diǎn)拙略理解。呵呵,然后,既然如此,一不做二不休,在本篇中得繼續(xù)亂彈一下下,關(guān)于OpenGL矩陣的理解。因?yàn)槲覍?shí)在不知道會(huì)不會(huì)在哪兒就把你忽悠+誤導(dǎo)了(但還是期盼你的信任),請(qǐng)看官自重哈哈。
這里有一句,我認(rèn)為,最能夠消解頭腦中的云霧的話:OpenGL中所有的變換,都是在變換坐標(biāo)系。
你還好嗎?你還能看見(jiàn)你眼前書桌上那個(gè)蘋果嗎?你把它向右邊移動(dòng)10厘米看看?移好了嗎?想一想,假如你眼前就是OpenGL的一個(gè)渲染窗口的話,蘋果是不是往x軸正方向移動(dòng)了10單位(假設(shè)單位:厘米)?恩,你現(xiàn)在所感受到的真實(shí)告訴你:蘋果確實(shí)不再在原位置,而是向右移了10厘米到了一個(gè)新的位置。我想說(shuō)的是,當(dāng)你完全邁進(jìn)3D圖形學(xué)殿堂后,請(qǐng)不要再那么輕易相信的眼睛所感受到的真實(shí)——它是真實(shí),但不是真實(shí)的全部。在你剛才一瞬間想象出的OpenGL渲染窗口里,蘋果沒(méi)有移動(dòng),它一直在那里,一直....而移動(dòng)的是整個(gè)空間,整個(gè)世界,包括書桌,包括你,包括你的眼睛!
在OpenGL的那個(gè)世界中,最初存在著幾個(gè)重疊的空間。有模型空間,有世界空間,視圖空間,屏幕空間等。每個(gè)空間實(shí)質(zhì)就是一個(gè)坐標(biāo)系(統(tǒng))——坐標(biāo)系統(tǒng)當(dāng)然就有坐標(biāo)軸。在最初的這個(gè)時(shí)刻,也許整個(gè)OpenGL世界也就只有這一套坐標(biāo)軸了:恩,一套。世界永遠(yuǎn)只有一個(gè),但是依附于這個(gè)世界的空間則平行地同時(shí)存在——實(shí)體(例如坐標(biāo)軸,蘋果)唯一,而實(shí)體的表示(各個(gè)空間中對(duì)應(yīng)的坐標(biāo)軸,蘋果)則多樣。我是這么理解的,OpenGL世界。
最初的OpenGL世界只有一套看不見(jiàn)的坐標(biāo)軸,然后,用戶說(shuō):蘋果!于是世上就有了蘋果。蘋果輕輕地出現(xiàn)在映射著這個(gè)世界的各個(gè)空間中——就在那個(gè)萬(wàn)物之源的坐標(biāo)原點(diǎn)上。原點(diǎn)與蘋果的“中心”對(duì)應(yīng)(當(dāng)然“中心”不一定指蘋果中間,它由上帝...不,用戶在制造這個(gè)蘋果時(shí)決定。確切地說(shuō),我們?cè)诋嬑锛?或者直接叫:模型)時(shí)給予這些東西的各個(gè)glVertex3頂點(diǎn)(坐標(biāo)x,y,z),它們的存在必然以位置(0,0,0)為標(biāo)尺——這個(gè)位置就是此物件的“中心”)。就在蘋果出現(xiàn)的此瞬間,模型空間(Model-Coordination,也稱Local-Corrdination)安靜了,此刻的整個(gè)世界的一切如同屏幕截圖般被保存起來(lái),儲(chǔ)存在模型空間——永遠(yuǎn)以當(dāng)前這個(gè)“中心”為原點(diǎn),以當(dāng)前這些坐標(biāo)(x,y,z)為蘋果各頂點(diǎn)的"模型坐標(biāo)系坐標(biāo)"。
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
當(dāng)然,蘋果出現(xiàn)在模型空間坐標(biāo)系統(tǒng)原點(diǎn),必然也就出現(xiàn)在其他空間坐標(biāo)系統(tǒng)的原點(diǎn),此時(shí)各空間的坐標(biāo)系是重合的,只不過(guò)模型空間被固定了,其他空間還沒(méi)而已。蘋果怎么移動(dòng)到別處呢?恩,編程的時(shí)候可以這么問(wèn),但當(dāng)你正在了解OpenGL世界的時(shí)候(如現(xiàn)在),這個(gè)問(wèn)法很不靠譜。你看上面的代碼(glVertex3f(A); glVertex3f(B)....)這里點(diǎn)A,B...不是給定了嗎?就算是變量,在“畫點(diǎn)”的時(shí)刻變量的值也是給定的呀。是的,變的不該是蘋果,而是坐標(biāo)系,準(zhǔn)確地理解,是世界空間(World-Coordination)的那個(gè)坐標(biāo)系要變,在原先與模型空間坐標(biāo)系重合的基礎(chǔ)上變。這個(gè)變的過(guò)程,叫模型變換(Model-Translation)。具體來(lái)說(shuō)就是用一個(gè)表示“轉(zhuǎn)動(dòng)/移動(dòng)/縮放”的矩陣左乘蘋果的各個(gè)頂點(diǎn)坐標(biāo),得出的結(jié)果(是一些不同的坐標(biāo))作為蘋果對(duì)應(yīng)頂點(diǎn)在世界空間坐標(biāo)系中的位置坐標(biāo)。誒?蘋果變了嗎?可以這么說(shuō),蘋果的“坐標(biāo)”還是原來(lái)那些A.B...但對(duì)應(yīng)于世界坐標(biāo)系的“位置”變化了。
這里確實(shí)是很容易迷糊,默念吧:OpenGL中所有的變換,都是在變換坐標(biāo)系。不是蘋果右移了10厘米,而是世界坐標(biāo)系左移了10厘米!假設(shè)蘋果中心最初恰就是出現(xiàn)在模型空間并與模型坐標(biāo)系原點(diǎn)重合,然后考慮以此為初狀態(tài)的世界空間,在用戶做了模型變換(向蘋果中心點(diǎn)所在之坐標(biāo)(0,0,0),左乘一個(gè)表示右移10厘米的模型變換矩陣)后,這個(gè)坐標(biāo)由(0,0,0)變成(10,0,0),相當(dāng)于坐標(biāo)系原來(lái)的虛擬坐標(biāo)(10,0,0)變成現(xiàn)在的(0,0,0)。看,坐標(biāo)系(由無(wú)限的虛擬坐標(biāo)構(gòu)成)左移10厘米!我想說(shuō),這才是模型變換(Model-Translation)的真正所為。與之前一樣,當(dāng)一切變換完成,結(jié)果被截屏存入世界空間。世界空間的動(dòng)蕩結(jié)束,從此定型。
//2.用戶創(chuàng)世第2天,說(shuō):蘋果右移! 于是世界坐標(biāo)系便左移了,結(jié)果存入世界空間
glMatrixMode(GL_MODEL);//表示接下來(lái)要作“模型轉(zhuǎn)換”
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
//2.用戶創(chuàng)世第2天,說(shuō):蘋果右移! 于是世界坐標(biāo)系便左移了,結(jié)果存入世界空間
glMatrixMode(GL_MODEL);//表示接下來(lái)要作“模型轉(zhuǎn)換”
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
接下來(lái)發(fā)生的事情跟之前一樣,只是從模型空間---(模型變換)---》世界空間的關(guān)系,改成世界空間---(視圖變換 View Translation)---》視圖空間的關(guān)系而已。請(qǐng)好好再模擬一次:一模一樣的過(guò)程,這次的移動(dòng)所需要的左乘矩陣(左乘剛才保存的模型轉(zhuǎn)換結(jié)果嘛)由相機(jī)(眼睛,視線)的設(shè)置提供,結(jié)果存入視圖空間而已。
//3.用戶創(chuàng)世第3天,說(shuō):我是主角! 于是他擁有了第一人稱視覺(jué),結(jié)果存視圖空間
glMatrixMode(GL_VIEW);//表示接下來(lái)要作“視圖轉(zhuǎn)換”
gluLookat(eye,look,up)//也相當(dāng)于一個(gè)平移矩陣
//2.用戶創(chuàng)世第2天,說(shuō):蘋果右移! 于是世界坐標(biāo)系便左移了,結(jié)果存入世界空間
glMatrixMode(GL_MODEL);//表示接下來(lái)要作“模型轉(zhuǎn)換”
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
//3.用戶創(chuàng)世第3天,說(shuō):我是主角! 于是他擁有了第一人稱視覺(jué),結(jié)果存視圖空間
glMatrixMode(GL_VIEW);//表示接下來(lái)要作“視圖轉(zhuǎn)換”
gluLookat(eye,look,up)//也相當(dāng)于一個(gè)平移矩陣
//2.用戶創(chuàng)世第2天,說(shuō):蘋果右移! 于是世界坐標(biāo)系便左移了,結(jié)果存入世界空間
glMatrixMode(GL_MODEL);//表示接下來(lái)要作“模型轉(zhuǎn)換”
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
噢,接下來(lái)是:視圖空間---(投影變換)---》屏幕空間。投影變換的變換手法與之前的不同,屏幕空間以視圖空間結(jié)果為基礎(chǔ),先用一個(gè)平頭錐體(視景錐)把視線范圍外的空間割了(裁剪),再把投影到一個(gè)可以覆蓋渲染屏幕窗口的矩形上(具體做法是,XYZ除以隱含的齊次坐標(biāo)W,然后舍棄深度Z),保存為屏幕空間——一個(gè)平面,讓用戶所能感悟到這一切不過(guò)顯示器屏幕一部分像素的把戲。當(dāng)然,最后的坐標(biāo)還要隱映射為渲染窗口的客戶區(qū)坐標(biāo)(原點(diǎn)在窗口左上角,Y軸下X軸右的平面坐標(biāo)),算是走出了OPENGL世界。
//4.用戶創(chuàng)世第4天,說(shuō):到顯示屏來(lái)吧! 世界便是"平"的了.最后得屏幕空間
glViewport(0,0,width,height);//設(shè)置那個(gè)"視景區(qū)矩形"大小
glMatrixMode(GL_PROJECTION));//表示接下來(lái)要作“投影轉(zhuǎn)換”
gluPerspective(fov,aspect,near,far);//視景體應(yīng)用
//3.用戶創(chuàng)世第3天,說(shuō):我是主角! 于是他擁有了第一人稱視覺(jué),結(jié)果存視圖空間
glMatrixMode(GL_VIEW);//表示接下來(lái)要作“視圖轉(zhuǎn)換”
gluLookat(eye,look,up)//也相當(dāng)于一個(gè)平移矩陣
//2.用戶創(chuàng)世第2天,說(shuō):蘋果右移! 于是世界坐標(biāo)系便左移了,結(jié)果存入世界空間
glMatrixMode(GL_MODEL);//表示接下來(lái)要作“模型轉(zhuǎn)換”
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
//4.用戶創(chuàng)世第4天,說(shuō):到顯示屏來(lái)吧! 世界便是"平"的了.最后得屏幕空間
glViewport(0,0,width,height);//設(shè)置那個(gè)"視景區(qū)矩形"大小
glMatrixMode(GL_PROJECTION));//表示接下來(lái)要作“投影轉(zhuǎn)換”
gluPerspective(fov,aspect,near,far);//視景體應(yīng)用
//3.用戶創(chuàng)世第3天,說(shuō):我是主角! 于是他擁有了第一人稱視覺(jué),結(jié)果存視圖空間
glMatrixMode(GL_VIEW);//表示接下來(lái)要作“視圖轉(zhuǎn)換”
gluLookat(eye,look,up)//也相當(dāng)于一個(gè)平移矩陣
//2.用戶創(chuàng)世第2天,說(shuō):蘋果右移! 于是世界坐標(biāo)系便左移了,結(jié)果存入世界空間
glMatrixMode(GL_MODEL);//表示接下來(lái)要作“模型轉(zhuǎn)換”
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
//1.用戶創(chuàng)世第1天,說(shuō):蘋果! 世上便有了蘋果---蘋果在模型空間(local 空間)被永恒描述
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
事實(shí)上OpenGL的glMatrixMode函數(shù)里沒(méi)有GL_MODEL和GL_VIEW這兩種設(shè)值。上面提到過(guò),模型變換和視圖變換其實(shí)是同一種處理,而且視圖變換只由相機(jī)控制,有則有,無(wú)則默認(rèn)。因此OpenGL直接就用GL_MODELVIEW一起轉(zhuǎn)換了。簡(jiǎn)潔是簡(jiǎn)潔,但是這樣一來(lái)用戶就不知道世界空間里的坐標(biāo)系什么時(shí)候改變了,也難得到物體在世界坐標(biāo)系下的位置坐標(biāo)了(事實(shí)上還是有辦法的,不過(guò)搞復(fù)雜了,見(jiàn)以后的博文啦)。
glViewport(0,0,width,height);//設(shè)置那個(gè)"視景區(qū)矩形"大小
glMatrixMode(GL_PROJECTION));//表示接下來(lái)要作“投影轉(zhuǎn)換”
glLoadIdentity();
gluPerspective(fov,aspect,near,far);//視景體應(yīng)用
gluLookat(eye,look,up)//控制整個(gè)視景體
glMatrixMode(GL_MODELVIEW);//表示接下來(lái)要作“模型視圖轉(zhuǎn)換”
glLoadIdentity();
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
........
glViewport(0,0,width,height);//設(shè)置那個(gè)"視景區(qū)矩形"大小
glMatrixMode(GL_PROJECTION));//表示接下來(lái)要作“投影轉(zhuǎn)換”
glLoadIdentity();
gluPerspective(fov,aspect,near,far);//視景體應(yīng)用
gluLookat(eye,look,up)//控制整個(gè)視景體
glMatrixMode(GL_MODELVIEW);//表示接下來(lái)要作“模型視圖轉(zhuǎn)換”
glLoadIdentity();
glTranslatef(10,0,0);//相當(dāng)于一個(gè)平移矩陣
DrawApple() //{glBegin();glVertex3f(A);glVertex3f(B)....glEnd()}
........
此外,每次變換不是以之前保存的那個(gè)空間的位置信息為基礎(chǔ)嗎?這要用到glLoadIdentity()函數(shù),初始化當(dāng)前矩陣,為什么?還是看下篇吧:
本文來(lái)源于ZwqXin http://www.zwqxin.com/ , 轉(zhuǎn)載請(qǐng)注明
原文地址:http://www.zwqxin.com/archives/opengl/opengl-matrix-what.html
本篇文章承接上文:亂彈OpenGL中的矩陣變換(上) 。上篇中,我嘗試從一種不太成熟的OpenGL世界觀(注意,或許也是3D渲染世界的世界觀)來(lái)認(rèn)識(shí)這個(gè)OpenGL世界,向自己澄清了一些概念(盡管不夠高清)。這里我想繼續(xù)從矩陣數(shù)學(xué)的角度想一想:究竟模型位置怎么在世界中各個(gè)空間的坐標(biāo)系統(tǒng)中“轉(zhuǎn)換”?——ZwqXin
OpenGL中的坐標(biāo)系是右手坐標(biāo)系,矩陣按列優(yōu)先存儲(chǔ)。這很讓人混亂,因?yàn)榫€代里接觸的都是行優(yōu)先存儲(chǔ),突然就這么column-major了.....最初還竊認(rèn)為該不該從向量角度讓頭腦清晰,后來(lái)看別人文章說(shuō)這跟向量沒(méi)啥關(guān)系,完全是一種規(guī)范:如同右手坐標(biāo)系,只是一種規(guī)范。好吧,列就列啦:
mt0 mt4 mt8 mt12
mt1 mt5 mt9 mt13
mt2 mt6 mt10 mt14
mt3 mt7 mt11 mt15
有點(diǎn)怪是不?當(dāng)然啦,連C語(yǔ)言學(xué)2維數(shù)組時(shí)都是row-major的,OpenGL卻是先一豎下來(lái),再來(lái)下一豎(column-major)...(考慮一塊內(nèi)存,第一個(gè)格子存mt0,第二存mt1.....)這就是OpenGL矩陣存儲(chǔ)方式。上篇提到,模型變換(Mode-Translation),視圖變換(View-Translation)乃至投影變換,這些決定了各個(gè)空間之不同的"針對(duì)坐標(biāo)系的變換",實(shí)質(zhì)產(chǎn)生出來(lái)的是一個(gè)左乘矩陣,這些矩陣就是這樣存儲(chǔ)的。如果你看過(guò)任何一本3D圖形學(xué)的入門書前兩章,那你應(yīng)該知道模型變換(在OpenGL中反映為glTranslate,glScale,glRotate)所使用的矩陣。譬如我這里先讓蘋果繞z軸逆轉(zhuǎn)個(gè)角度Θ,再讓它變成原大小的A倍,再右移10單位:(P.S.翻譯成OpenGL世界的語(yǔ)言:在模型空間坐標(biāo)系確定的位置基礎(chǔ)上,讓世界空間坐標(biāo)系繞z軸順轉(zhuǎn)角度Θ,坍縮成原來(lái)一半大小,并左移10單位,注意是坍縮后1單位表示的長(zhǎng)度跟之前的不一樣了。)
A*cosΘ-sinΘ0 10
sinΘA*cosΘ00
00A*1 0
0001
X
* y = M * P(point of Apple)
z
1
其中M是變換矩陣,點(diǎn)P(x,y,z,1)是蘋果的其中一個(gè)點(diǎn)的坐標(biāo)(記得嗎?這個(gè)坐標(biāo)是在模型空間確定的并且不變),它是一個(gè)豎向量形式的點(diǎn)。為什么是豎向量呢,因?yàn)镺penGL矩陣的左乘:(4X4)*(4X1)=(4X1)嘛,這是線形代數(shù)知識(shí),(A行數(shù)XA列數(shù))*(B行數(shù)XB列數(shù))中,只有A列數(shù)=B行數(shù),這兩個(gè)矩陣(向量)才可以相乘。那為什么要左乘?因?yàn)镺penGL(點(diǎn))向量是列向量!哈哈,不是忽悠,這根本就是“規(guī)范問(wèn)題”,譬如D3D就全采取相反的,不多說(shuō)。最后,思考者應(yīng)該已經(jīng)把上面那個(gè)矩陣跟再上面那個(gè)“模板”對(duì)應(yīng)起來(lái)了吧(OPENGL在內(nèi)存中把相乘的結(jié)果——這16個(gè)數(shù)按mt0=A*cosΘ, mt1=sinΘ, mt2=0, mt3=0, mt4=-sinΘ, mt5=A*cosΘ, mt6=0......這樣的豎的順序存放)。再譬如我在之前的ShadowVolume Demo中就這樣定義了一個(gè)4X4矩陣類,并這樣定義了一個(gè)矩陣(就是上面的mt嘛)左乘某點(diǎn)向量的方法:
class CMatrix16
{
public:
float mt[16];
inline CVector4D operator*(CVector4D & vec4) {
return CVector4D(mt[0] * vec4.x + mt[4] * vec4.y + mt[8] * vec4.z + mt[12] * vec4.w ,
mt[1] * vec4.x + mt[5] * vec4.y + mt[9] * vec4.z + mt[13] * vec4.w ,
mt[2] * vec4.x + mt[6] * vec4.y + mt[10]* vec4.z + mt[14] * vec4.w ,
mt[3] * vec4.x + mt[7] * vec4.y + mt[11]* vec4.z + mt[15] * vec4.w );
};
};
class CMatrix16
{
public:
float mt[16];
inline CVector4D operator*(CVector4D & vec4) {
return CVector4D(mt[0] * vec4.x + mt[4] * vec4.y + mt[8] * vec4.z + mt[12] * vec4.w ,
mt[1] * vec4.x + mt[5] * vec4.y + mt[9] * vec4.z + mt[13] * vec4.w ,
mt[2] * vec4.x + mt[6] * vec4.y + mt[10]* vec4.z + mt[14] * vec4.w ,
mt[3] * vec4.x + mt[7] * vec4.y + mt[11]* vec4.z + mt[15] * vec4.w );
};
};
然后,我們回頭看之前那條式子,得出的結(jié)果是(A*cosΘ*x - sinΘ*y + 10,sinΘ*x + A*cosΘ*y,A*z,1),這是點(diǎn)P的新位置,注意,再說(shuō)一次,是“位置”而不是“坐標(biāo)”,點(diǎn)P坐標(biāo)依然是(x,y,z,1)。(當(dāng)然,你還可以用這樣的概念來(lái)區(qū)分。)我姑且把結(jié)果記作(MP),所有點(diǎn)的結(jié)果構(gòu)成了蘋果在世界空間的位置。好了,看看代碼:
glMatrixMode(GL_MODEL);
glTranslatef(10,0,0);
glScalef(A,A,A);
glRotatef(Θ, 0,0,1)
DrawApple() ;
glMatrixMode(GL_MODEL);
glTranslatef(10,0,0);
glScalef(A,A,A);
glRotatef(Θ, 0,0,1)
DrawApple() ;
請(qǐng)從下往上看。你問(wèn)為什么順序是倒著的呢?這跟上篇遺留下來(lái)的問(wèn)題很相似。現(xiàn)在經(jīng)過(guò)模型變換來(lái)到了世界空間,接下來(lái)是視圖變換了。設(shè)當(dāng)前相機(jī)得出的左乘矩陣為V,那么下一步,相似地,就是V*(MP)=(VMP)。上篇說(shuō)過(guò),OpenGL把模型變換和視圖變換合并為“模型視圖變換”,也就是說(shuō),OpenGL只生成一個(gè)左乘矩陣VM,上面的兩步乘法,在OpenGL中其實(shí)一步到位:VM*P=(VMP)。我們來(lái)到了視圖空間。真正代碼在此:
gluLookat(eye,look,up);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(10,0,0);
glScalef(A,A,A);
glRotatef(Θ, 0,0,1)
DrawApple() ;
gluLookat(eye,look,up);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(10,0,0);
glScalef(A,A,A);
glRotatef(Θ, 0,0,1)
DrawApple() ;
接下來(lái)由視景體設(shè)置,視口設(shè)置,投影處理得出的左乘矩陣是J,那么相似地,我們繼續(xù)左乘:J*(VMP)=(JVMP),來(lái)到屏幕空間——OpenGL世界的出口(之后的往窗口坐標(biāo)系映射等等已經(jīng)不算OpenGL的工作了)。
glViewport(0,0,width,height);//設(shè)置視口(視窗)大小
glMatrixMode(GL_PROJECTION));
glLoadIdentity();
gluPerspective(fov,aspect,near,far);
gluLookat(eye,look,up);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(10,0,0);
glScalef(A,A,A);
glRotatef(Θ, 0,0,1)
DrawApple() ;
glViewport(0,0,width,height);//設(shè)置視口(視窗)大小
glMatrixMode(GL_PROJECTION));
glLoadIdentity();
gluPerspective(fov,aspect,near,far);
gluLookat(eye,look,up);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(10,0,0);
glScalef(A,A,A);
glRotatef(Θ, 0,0,1)
DrawApple() ;
是不是回到了上篇的結(jié)尾呢?恩,我從矩陣意義上再梳理了一次流程。然后,可以回答為什么代碼是倒過(guò)來(lái)寫的了吧?——這說(shuō)明你很清楚,代碼是按順序從上往下執(zhí)行的。沒(méi)錯(cuò),OpenGL中執(zhí)行這些代碼也是從上往下執(zhí)行的。所以你看看吧,這次要從上往下看:第1句:視窗設(shè)置,沒(méi)什么,它只剔除,不直接產(chǎn)生矩陣(而且還得在下面投影變換處發(fā)揮作用);第2句:接下來(lái)要作投影變換啦;第3句:glLoadIdentity(),哈哈!這才是重點(diǎn)——無(wú)論眼前的是什么,它都能把它初始化成一個(gè)單位矩陣!而這里的這個(gè)單位矩陣,是一切的開(kāi)端,它確實(shí)就是一個(gè)單位矩陣(I)了,后面第4句視景體的設(shè)置產(chǎn)生的投影變換矩陣(J)右乘它:J= I*J;接下來(lái)第5句相機(jī)設(shè)置,第6句表示模型視圖變換的開(kāi)始,第7句又一個(gè)glLoadIdentity(),繼續(xù)右乘:J= I*J*I,這有什么用啊?呵呵,因?yàn)榻酉聛?lái)“模型視圖變換”產(chǎn)生的矩陣VM要繼續(xù)右乘這個(gè)結(jié)果,直接讓兩個(gè)變換矩陣相乘可以是可以,那么如果沒(méi)有投影變換呢?呵呵,這是有可能的,先不說(shuō)平時(shí)我們渲染,就我們哪天突然要在模型空間搞事(譬如上次ShadowVolumeDemo那樣“回光返照”),單單應(yīng)用“模型視圖變換”就關(guān)聯(lián)不到最初那個(gè)glLoadIdentity()了。所以讓一個(gè)glLoadIdentity()在作變換前出現(xiàn)是好的:VM = I*VM,實(shí)際上所有變換前加一個(gè)glLoadIdentity()是好習(xí)慣。
好吧,整理一下:假設(shè)把蘋果旋轉(zhuǎn),放大,移動(dòng)的相應(yīng)變換矩陣是R,S,T,相機(jī)那個(gè)是L,那么這里就是 VM =L*T*S*R,也是倒過(guò)來(lái)的吧呵呵。按代碼順序的話右乘起來(lái)是這樣的:(JVM)= I*J*I*VM =I *J*I*L*S*T,等式右邊項(xiàng)是嚴(yán)格按照相應(yīng)代碼順序來(lái)右乘的。為什么這里變右乘了?事實(shí)上我們說(shuō)OpenGL的矩陣左乘規(guī)范,它跟代碼順序是兩碼事,矩陣左乘是從邏輯上來(lái)說(shuō)的(也就是上篇和本篇我在開(kāi)講代碼執(zhí)行順序前講述的那些故事的邏輯,是屬于OpenGL世界的邏輯),代碼順序是屬于編譯器的哦。好了,這個(gè)(JVM)就是要把處于模型空間的物體轉(zhuǎn)移到屏幕上來(lái)——這時(shí)候它才和蘋果的頂點(diǎn)相乘:(JVMP)=(JVM)*P,看好了,也是右乘,當(dāng)然的了——結(jié)果是,蘋果那么多頂點(diǎn)在一次渲染周期內(nèi)只被“用”了一次!要知道,得出(JVM)結(jié)果的計(jì)算只有那么幾個(gè)矩陣相乘運(yùn)算,但是蘋果N個(gè)頂點(diǎn)就有N個(gè)(JVM)*P的相乘運(yùn)算哦!如果代碼執(zhí)行順序相反你覺(jué)得會(huì)怎樣?“幾個(gè)N相乘”這么多次運(yùn)算哦!
這里還有概念要澄清:
1.我說(shuō)了,蘋果也好,其他任何畫出來(lái)的“東西”也好,它們坐標(biāo)是在給出頂點(diǎn)坐標(biāo)的時(shí)候就確定在模型空間的了,變換的是位置,也許說(shuō)它們變換了還不對(duì)——因?yàn)檎嬲蛔儞Q的是坐標(biāo)系:你上面也看到了,蘋果的坐標(biāo)是最后右乘上去的,之前一直變換的是最初那個(gè)單位矩陣,單位矩陣的變換也代表了最初各空間重合的OpenGL世界的坐標(biāo)系——上篇說(shuō)過(guò),它的變換是反著模型的(當(dāng)然也反著單位矩陣),這里也不妨說(shuō),它受變換的階段順序都是反著模型的——而且它是按照編譯器執(zhí)行順序變換出來(lái)的,是真正的,真實(shí)存在的變換:OpenGL中所有的變換,都是在變換坐標(biāo)系。
2.說(shuō)法問(wèn)題,“坐標(biāo)”和“位置”,一路看下來(lái)你也知道,我不把它們當(dāng)一回事(不想再羅嗦了恩)。但是更通常的叫法(雖然容易引起歧義但說(shuō)得廣泛),是把“坐標(biāo)”(固定于模型空間的那個(gè),模型剛剛在世界出現(xiàn)時(shí)候的位置- -看,又羅嗦了)稱為“局部坐標(biāo)(Local-Coordinate)”,把世界空間里面那個(gè)“位置”叫“世界坐標(biāo)(World-Coordinate)”,也有直接簡(jiǎn)稱它為坐標(biāo)的(這樣的明顯不是高手),因?yàn)樗钅鼙桓兄苍S你在了解什么模型空間模型變換等等等等這些概念前,唯一認(rèn)識(shí)的就是世界坐標(biāo)——它就標(biāo)識(shí)著3D渲染窗口里那個(gè)虛擬3D世界不是么?恩,所以說(shuō)世界空間是OpenGL世界中近乎“正常”的空間,類比一下,如果把我們?nèi)祟愃畹目臻g叫“世界空間”的話,小說(shuō)中描繪的到處扭曲的異次元空間就叫“模型空間”呀“屏幕空間”呀之類的了(笑)。遺憾的是OpenGL不直接給我們提供這個(gè)空間中各物體的坐標(biāo)系位置呢~不怕,我們還有逆矩陣!
3.等有緣的你來(lái)發(fā)挖啦!錯(cuò)誤啦認(rèn)識(shí)問(wèn)題啦,隨便提。這里是ZwqXin: www.zwqxin.com, 我的3D旅途記錄簿。(紀(jì)念今天2009.2.5.BLOG上軌道啦。)
最后...還有g(shù)lPushMatrix()和glPopMatrix()呢?相信大家用得很多,也知道用處:在它們之外的東西不受它們里面的那些模型變換代碼影響。譬如下面這里,蘋果跟上面一樣也是做那些變換。可是橙呢?旋轉(zhuǎn)和放大對(duì)它無(wú)影響,它只是受到移動(dòng)了(OpenGL世界說(shuō)法是,對(duì)應(yīng)坐標(biāo)系移動(dòng)了)。
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(10,0,0);
glPushMatrix();
glScalef(A,A,A);
glRotatef(Θ, 0,0,1);
DrawApple() ;
glPopMatrix();
DrawOrange() ;
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
glTranslatef(10,0,0);
glPushMatrix();
glScalef(A,A,A);
glRotatef(Θ, 0,0,1);
DrawApple() ;
glPopMatrix();
DrawOrange() ;
不知道我的講解有沒(méi)有人滿意。我自己倒是滿意了。哈哈。后面我還會(huì)有文章說(shuō)說(shuō)逆矩陣運(yùn)算的問(wèn)題和作用,并贊NEHE,有興趣的不妨CLICK一CLICK:
本文來(lái)源于ZwqXin http://www.zwqxin.com/ , 轉(zhuǎn)載請(qǐng)注明
原文地址:http://www.zwqxin.com/archives/opengl/opengl-matrix-what-2.html