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