• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            doing5552

            記錄每日點(diǎn)滴,不枉人生一世

              C++博客 :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              73 Posts :: 0 Stories :: 94 Comments :: 0 Trackbacks

            公告

            常用鏈接

            留言簿(24)

            我參與的團(tuán)隊(duì)

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 455284
            • 排名 - 48

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

                  想來編程也有一段時(shí)間,什么都很明白就是對于坐標(biāo)變換不是很理解,總是在關(guān)鍵的時(shí)候迷亂不已,胡亂的寫一些變換代碼,得到的結(jié)果當(dāng)然讓自己云里霧里。仔細(xì)的看了一下好幾本書關(guān)于3D變換的篇章,總結(jié)了一下,希望對大家有幫助。末了聲明以下,可能我說得也有錯(cuò)誤的地方,敬請局內(nèi)人明鑒指正,我只是一個(gè)在校學(xué)生沒有實(shí)際的工作經(jīng)驗(yàn)。懇請大家提出寶貴的意見,打造一個(gè)Matrix Bible,讓更多的初學(xué)者不要走彎路。謝謝大家。

                    矩陣變換是個(gè)相當(dāng)重要的要點(diǎn),難度應(yīng)該僅次于數(shù)據(jù)結(jié)構(gòu)部分。倒不是因?yàn)楸旧碚莆罩R(shí)對能力的要求有多么高,而是因?yàn)閺膩頉]有人說明白過在實(shí)際情況中如何應(yīng)用。

                    在現(xiàn)代游戲的制作過程中,肯定是先由美工制作好要用到的模型,比如人物車輛地形等等,我們稱之為基本模型。而諸如3dsmax maya等等建模工具產(chǎn)生的二進(jìn)制文件是Application特有的格式,所以一般需要導(dǎo)出,各大論壇上無數(shù)人曾經(jīng)提問過如何載入3ds模型。成熟的3D引擎都有自己的一套Util工具用來把模型導(dǎo)出為引擎特有的數(shù)據(jù)格式,比如Doom3引擎開源論壇上就提供3dsmax maya等使用的導(dǎo)出插件,用來輸出為MD5格式的模型文件。其中會(huì)用到一種叫做Data Chunk的概念,不再多說。

                    當(dāng)美工制作模型的時(shí)候,肯定以建模工具提供的那個(gè)坐標(biāo)系為基本坐標(biāo)系進(jìn)行建模。模型的頂點(diǎn)都是相對于各自基本的坐標(biāo)系,我們稱之為Local坐標(biāo)系統(tǒng)或者Object坐標(biāo)系。

                    美工把這些數(shù)據(jù)交給程序員。程序員需要在場景中安放這些模型,比如在地圖上放置建筑車輛人物等等。可是程序員面對的這些模型的坐標(biāo),數(shù)字可能是一樣的,因?yàn)槎际窍鄬τ贚ocal坐標(biāo)系統(tǒng),就這樣一股腦的載入,肯定都是在“世界”的中心位置進(jìn)行繪制,根本不可能分開。于是我們需要對各個(gè)物體,也就是各個(gè)獨(dú)立的坐標(biāo)系統(tǒng)進(jìn)行Transform(包括Translate Rotate Scale操作)。

                    這里我們以GL為概念。當(dāng)我們輸入glutSolidCube(4)的時(shí)候,它會(huì)產(chǎn)生這樣的數(shù)據(jù):glVertex3f(2,2,2),glVertex3f(-2,-2,-2)等等,也就是長度為4的一個(gè)立方體。注意我們畫的這個(gè)立方體的位置,肯定是出現(xiàn)在“世界”的中心位置。如果我們希望它移動(dòng)到其他的位置呢?只能先glTranslatef(),再glutSolidCube。這個(gè)glTranslatef作用在MODELVIEW_MATRIX上,具體的形式請到OpenGL Wiki上看,那里連載了RedBook。

                    比如我們輸入glTranslatef(1.0,0,0);glutSolidCube(4),其實(shí)它產(chǎn)生的“真正頂點(diǎn)”是,(3,2,2),(-1,-2,-2),統(tǒng)統(tǒng)向x方向移動(dòng)了一個(gè)位置。如果你在自己的范例程序里看不到是因?yàn)閜erspective中的far near planes沒有設(shè)置好。這樣我們就仿佛實(shí)現(xiàn)了平移以及旋轉(zhuǎn)等等操作,注意,是仿佛。

                    我們把變換頂點(diǎn)的矩陣一般稱為MV(Model View)矩陣,把和在一起一步到位的矩陣稱為MVP矩陣,在GLSL中就有g(shù)l_ModelViewMatirx和gl_ModelViewProjectionMatrix這兩個(gè)Uniform Matirx。我們輸入一個(gè)頂點(diǎn),希望把它放到這個(gè)世界的正確位置上,就需要乘以適合它自己的MV,因?yàn)椴灰粯拥哪P彤?dāng)然需要不一樣的世界位置。矩陣乘法就可以完成這項(xiàng)神奇的工作。可是向量的概念則很大不同。

                    一個(gè)正方體,只要它在我們的映像中從頭到尾都是方方正正的立在場景中,它的向量,無論朝上朝下都應(yīng)該是相同的,比如(1,0,0)左邊的面,只要我們不旋轉(zhuǎn)這個(gè)正方體,它在Local坐標(biāo)系還是變換后也應(yīng)該是(1,0,0),這個(gè)時(shí)候我們用哪個(gè)矩陣呢?用MV顯然不同,就需要用MV的Inverse Transpose。在線性代數(shù)中,求一個(gè)矩陣的Inverse然后Transpose,與先求Transpose再求Inverse,這兩邊是完全相等的。在GLSL中,其實(shí)gl_Normal*gl_NormalMatrix等同于gl_Normal*gl_ModelViewInverseTransposeMatrix。REDBOOK是這樣說的:

                                     In other words, normal vectors are transformed by the inverse transpose of the transformation that transforms points.

                 為什么有這樣的變化呢?我們用V(x,y,z,w)代表頂點(diǎn),P(a,b,c,d)代表一個(gè)平面。相應(yīng)的平面方程可以寫作,PV = 0,也就是ax + by + cz = 0,有個(gè)向量垂直于頂點(diǎn)所在的那個(gè)面。

                 這是個(gè)萬用公式么?還早呢?如果我們要把這個(gè)模型的位置改變掉,我們就一定需要把頂點(diǎn)乘以ModelView矩陣,為了方便我就用M代表MV。這里寫作:

                               PMV = ?

                 可是這個(gè)式子右邊等于什么呢?我也不知道。為了這個(gè)式子依舊讓右邊等于0,符合基本的幾何代數(shù)式,我們需要再給左邊乘以M-1,就是M的逆矩陣。

                              P M-1 M V = 0

                 有一個(gè)向量垂直于這個(gè)平面。于是引入n一個(gè)我們真正意義上的面向量,和平面內(nèi)的任意一個(gè)向量都應(yīng)該是正交的。那個(gè)任意向量如何獲得呢?最簡單的就是,那個(gè)頂點(diǎn)和原點(diǎn)構(gòu)成的向量 —— 因?yàn)樵谖覀冏畛醯氖阶永锩妫J(rèn)這個(gè)平面就是通過原點(diǎn)的。我們想讓等式依舊成立,式子變成:

                             N T V = 0

                 注意上式的T。如果單純的N矩陣乘以V,得到的結(jié)果還是一個(gè)向量而不是數(shù)字,所以需要一個(gè)Transpose變換。綜合后,式子變換為:
                             N T  M-1 M V = 0

                             (如果我沒有理解錯(cuò)的話,V應(yīng)該隱含著用了2次)

                 好的,我們開始用矩陣運(yùn)算法則去分解上述的式子。MV不變,剩下的也就是(M-1)T N,也就是需要ModelView矩陣的Inverse Transpose矩陣乘以向量。

                 只要這些明白了,高級(jí)變換也就沒有什么難得了。

            Use Case
                古老的bump mapping

                 在BumpMapping里面有個(gè)很重要的過程,就是把光源位置轉(zhuǎn)換到以每個(gè)頂點(diǎn)處的向量為Z軸的空間中去,求向量的方法我不多說,為什么這樣做也沒有必要講,最關(guān)鍵的就是可能很多人不明白為什么要乘以以N B T為元素的矩陣。

                 其實(shí)這里很多書籍要么沒有解釋,要么一筆帶過。我來嘗試的解釋通透,可能有錯(cuò)誤,希望大家指正。
                 GL的Matrix是Column-Major的形勢。我們以N B T為元素的矩陣為例。

                    Nx Bx Tx
                    Ny By Ty
                    Nz Bz Tz

                 這里隱含的意思是:把頂點(diǎn)變換到以N B T為3個(gè)坐標(biāo)軸的空間中,無論這個(gè)坐標(biāo)系是不是和世界坐標(biāo)系統(tǒng)“傾斜”的。

                 再次寫成4x4的形式:

                    Nx Bx Tx 0
                    Ny By Ty 0
                    Nz Bz Tz  0
                    0    0   0   1

                 注意第四列的連續(xù)三個(gè)0。代表的意義是:每個(gè)頂點(diǎn)變換到以這個(gè)N B T為坐標(biāo)軸的坐標(biāo)系后,需要Translate到的位置。因?yàn)樵贐ump Mapping中我們不需要對頂點(diǎn)的位置變換,所以隱含著寫成3x3的Matrix就足夠了。
             
                 這里的這個(gè)3x3矩陣,其實(shí)就是對于每個(gè)頂點(diǎn)來說的MV矩陣,轉(zhuǎn)換的就是那個(gè)LightPosition。可是這里又有一個(gè)問題,如果轉(zhuǎn)換的不止一個(gè)LightPosition,還有Normal怎么辦?因?yàn)檫@是個(gè)“斜”的坐標(biāo)系,原來的(1,0,0)可不是變換后的(1,0,0)。記起來了么?Inverse Transpose!我們只要把原來的向量乘以這個(gè)以NBT為正交坐標(biāo)軸向量的MV的Inverse Transpose,就可以得到正確的結(jié)果了。(我說得對么?)其實(shí),因?yàn)檫@個(gè)矩陣3個(gè)向量都已經(jīng)normalized,它的Inverse = Transpose,所以這個(gè)NBT矩陣的IT矩陣就是它自己!

                 微軟DirectX SDK Oct里面有個(gè)Shadow Mapping的Sample,代碼中有一部分詳細(xì)的說明了這個(gè)過程。它需要變換光源的向量,如果當(dāng)我們把光源綁定到車上,就需要更改矩陣中w行的元素的。有興趣的朋友可以看看。

                 gl_LightPosition提供的光源參數(shù)是針對Eye Space,也就是所有的頂點(diǎn)已經(jīng)經(jīng)過MV變換的空間中的那個(gè)點(diǎn)。如果你有自己的光源安排一定要在空間中互相轉(zhuǎn)換,頭疼。GLSL用Uniform3f自己指定變換后空間中光源位置,比較方便,適合完成以場景為單位的光照計(jì)算。

                    不再新潮的Shadow Mapping

              
            Shadow mapping,包括后來的Variance SM,PCF等,有個(gè)關(guān)鍵的步驟,就是把場景轉(zhuǎn)換到以光源為攝像機(jī)的空間LightCamera中,獲得深度。這里,場景中所有的頂點(diǎn)需要變換到以LightCamera的NBT為坐標(biāo)軸的坐標(biāo)系中,向量的正確變換則需要乘以NBT的Inverse Transpose矩陣。(我推測的,希望大家指正)。接下來的事情么,在Shader中愛做什么做什么。

                 那么如何傳入所需要的矩陣呢?其實(shí)相當(dāng)簡單。功夫厲害的,直接把數(shù)組通過glUniform4fmatrix(),或者cgSetParameter傳入。功夫弱一些的,老老實(shí)實(shí)的gl_MatrixModel(GL_MODELVIEW);glLoadIndentity();glMultMatrix();//乘以需要的矩陣到單位矩陣上,然后后再傳入Shader。

                 有的時(shí)候我們需要自己獨(dú)立求逆矩陣,如何辦到呢?這可不是紙上的線性代數(shù)考試可以用初等變換計(jì)算。
                    
                 矩陣的變換和逆變換就那么3種,Translate,Rotate,Scale。
                 我們知道MV =  T * R。(T R代表為了實(shí)現(xiàn)Translate和Rotate相應(yīng)的矩陣)
                 則Inv(MV) = Inv(T) * Inv(R)
                    
                 也就是說,假設(shè)MV是
                    ( R R R P)
                    ( R R R P)
                    ( R R R P)
                    ( 0001)
                 則它所代表的 R T矩陣就是
                   ( R R R 0)
                   (R R R 0)
                   ( R R R 0)
                   ( 0001)
                 和
                  ( 100 P)
                  ( 010 P)
                  ( 001 P)
                   ( 0001)
                 計(jì)算相應(yīng)子矩陣的逆矩陣,Inv(T)就是
                   ( 100 -P)
                   ( 010 -P)
                   ( 001 -P)
                   ( 000  1)
                 逆旋轉(zhuǎn)矩陣可能復(fù)雜一些,不過依舊可以計(jì)算出來,也就是它的Transpose。然后乘一下,逆矩陣就出來了。

                 目前我所想到的關(guān)鍵就這么多,更多的懇請大家添加,謝謝。

            解析 NVIDIA中的HW Shadow Mapping Demo
                    既然是SM的DEMO,在LightSpace和CameraSpace之間進(jìn)行變換肯定是少不了的。當(dāng)然,也應(yīng)用到了多通道的思想。
                quad.new_list(GL_COMPILE);
                glPushMatrix();
                glRotatef(
            -90100);
                glScalef(
            4,4,4);
                glBegin(GL_QUADS);
                glNormal3f(
            001);
                glVertex2f(
            -1-1);
                glVertex2f(
            -1,  1);
                glVertex2f( 
            1,  1);
                glVertex2f( 
            1-1);
                glEnd();
                glPopMatrix();
                quad.end_list();

                wirecube.new_list(GL_COMPILE);
                glutWireCube(
            2); 
                wirecube.end_list();
                
                geometry.new_list(GL_COMPILE);
                glPushMatrix();
                glTranslatef(
            0, .4f, 0);
                glutSolidTeapot(.5f);
                glPopMatrix();
                geometry.end_list();
               
                首選我們新建了3個(gè)顯示列表,可以看出,quad的意義是,處在世界平面的x z平面的尺寸為4x4的一個(gè)平面(先畫xy平面內(nèi)的點(diǎn),不過又旋轉(zhuǎn)了90度)。geometry么,就是那個(gè)著名的nurbs茶壺,我們想象為在世界平面y向上的0.4f處。注意每次繪制前都會(huì)調(diào)用glPushMatrix把MV矩陣推入Stack,這個(gè)步驟相當(dāng)重要,因?yàn)槲覀冞€不知道前面的坐標(biāo)系,究竟在哪里,不過后面我們又看到了如何解決這個(gè)問題。

            void render_scene(glut_simple_mouse_interactor & view)
            {
                glColor3f(
            1,1,1);
                glPushMatrix();
                view.apply_inverse_transform();

                glPushMatrix();
                
            object.apply_transform();

                render_quad();

                glEnable(GL_LIGHTING);
                geometry.call_list();
                glDisable(GL_LIGHTING);

                glPopMatrix();
                glPopMatrix();
            }

                通篇代碼閱讀完畢,發(fā)現(xiàn)這個(gè)函數(shù)最重要。參數(shù)view,我的理解是,它是View變換矩陣,也就是儲(chǔ)存了3個(gè)正交單位向量,有可能包括眼睛的位置(注意是有可能),無論這個(gè)眼睛是攝像機(jī),還是光源。

                不過這個(gè)view.apply_inverse_transform(),它究竟代表了哪些操作呢?讓我們在nvidia自己寫的glh文件里面探尋一下吧。

                void apply_transform()
                {
                  translator.apply_transform();
                  trackball.apply_transform();
                }

                
            void apply_inverse_transform()
                {
                  trackball.apply_inverse_transform();
                  translator.apply_inverse_transform();
                }

                如果要調(diào)用apply_transform()進(jìn)行坐標(biāo)變換,那么是先位移,再旋轉(zhuǎn)。如果要返回到最初的坐標(biāo)系,那么就應(yīng)該是先旋轉(zhuǎn)回來,再位移回去。知道為什么么?
               
                我們默認(rèn)的位移其實(shí)應(yīng)該是相對于World Coordinate,也就是說,我們意義上的向xyz方向移動(dòng)幾個(gè)單位其實(shí)是在那個(gè)最初的平面世界中的,而不是應(yīng)該在攝像機(jī)空間中的位移 —— 因?yàn)樽畛跏澜缱鴺?biāo)系里面的三個(gè)正交方向向量其實(shí)也已經(jīng)旋轉(zhuǎn)過了,也就是說,如果我們先旋轉(zhuǎn)再位移,得到的軌跡相對于我們腦海中的世界坐標(biāo)系是一條斜直線 —— 雖然說它對于攝像機(jī)坐標(biāo)系來說是坐標(biāo)軸直線。
                如果用線性代數(shù)的性質(zhì)也很好解釋,本來正確的transform順序(原因在上面)就是I*T*R,如果要回到I,就必須I*T*R*R-1*T-1 = I。OpenGL的matrix操作是右結(jié)合的。

               
            這里的
            view.apply_inverse_transform()就好理解了。不管我渲染什么,我總是要先把坐標(biāo)系放回到世界坐標(biāo)系中的原點(diǎn)處,保存好當(dāng)前矩陣,然后再調(diào)用顯示列表。不過我們又發(fā)現(xiàn)那個(gè)render_quad(),好,我們再把它揪出來。

            void  render_quad()
            {
                glActiveTextureARB(GL_TEXTURE0_ARB);
                obj_linear_texgen();
                texgen(
            true );
                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
                glScalef(
            4 , 4 , 1 );
                glMatrixMode(GL_MODELVIEW);

                glDisable(GL_LIGHTING);
                decal.bind();
                decal.enable();
                quad.call_list();
                decal.disable();
                glEnable(GL_LIGHTING);

                texgen(
            false );
                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
                glMatrixMode(GL_MODELVIEW);
            }

                激活第一個(gè)紋理單元,自動(dòng)生成紋理,調(diào)整紋理矩陣,準(zhǔn)備好紋理,繪制桌面。這里繪制的是,以光源為視點(diǎn)的場景,應(yīng)該是這個(gè)樣子,全面的內(nèi)容解析看注釋。

            lightcamera.PNG


            void  render_scene_from_light_view()
            {
                //放置燈光

                glPushMatrix();
                  glLoadIdentity();
                  glLightfv(GL_LIGHT0, GL_POSITION, 
            &  vec4f( 0 , 0 , 0 , 1 )[ 0 ]);
                glPopMatrix();
                //為什么這里光源是(0,0,0)呢?gl的光源坐標(biāo)是在object coordinates中,也就是它要被I矩陣轉(zhuǎn)換,結(jié)果依舊是EyeSpace中的(0,0,0)
                
            //  spot image
                glActiveTextureARB(GL_TEXTURE1_ARB);
                glPushMatrix();
                  eye_linear_texgen();    
                  texgen(
            true );
                glPopMatrix();

                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
                glTranslatef(.5f, .5f, .5f);
                glScalef(.5f, .5f, .5f);
                gluPerspective(lightshaper.fovy, 
            1 , lightshaper.zNear, lightshaper.zFar);
                //這里生成的是一個(gè)生成紋理坐標(biāo)的矩陣,它的形式是I*T*S*P,提供給處于以光源為原點(diǎn)的場景坐標(biāo)使用。
                glMatrixMode(GL_MODELVIEW);
                light_image.bind();
                light_image.enable();
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

                glActiveTextureARB(GL_TEXTURE0_ARB);

                lightshaper.apply();
                
            if (display_funcs[current_display_func]  ==  render_scene_from_light_view)
                    largest_square_power_of_two_viewport();
                render_scene(spotlight);//讓思路回到上面的那個(gè)函數(shù),仔細(xì)體會(huì)

                glActiveTextureARB(GL_TEXTURE1_ARB);
                light_image.disable();
                glActiveTextureARB(GL_TEXTURE0_ARB);
            }

            再把這個(gè)函數(shù)貼出來,請自己仔細(xì)推敲變換過程。

            void  render_scene_from_camera_view()
            {
                
            //  place light
                glPushMatrix();
                glLoadIdentity();
                camera.apply_inverse_transform();
                spotlight.apply_transform();
                glLightfv(GL_LIGHT0, GL_POSITION, 
            &  vec4f( 0 , 0 , 0 , 1 )[ 0 ]);
                glPopMatrix();

                
            //  spot image
                glActiveTextureARB(GL_TEXTURE1_ARB);

                glPushMatrix();
                camera.apply_inverse_transform();
                eye_linear_texgen();    
                texgen(
            true );
                glPopMatrix();

                glMatrixMode(GL_TEXTURE);
                glLoadIdentity();
                glTranslatef(.5f, .5f, .5f);
                glScalef(.5f, .5f, .5f);
                gluPerspective(lightshaper.fovy, 
            1 , lightshaper.zNear, lightshaper.zFar);
                spotlight.apply_inverse_transform();
                glMatrixMode(GL_MODELVIEW);

                light_image.bind();
                light_image.enable();
                glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

                glActiveTextureARB(GL_TEXTURE0_ARB);
                reshaper.apply();
                render_scene(camera);

                glActiveTextureARB(GL_TEXTURE1_ARB);
                light_image.disable();
                glActiveTextureARB(GL_TEXTURE0_ARB);

                render_light_frustum();
            }

            看哪,天梯!
               說了這么多的東西,貼了這么多代碼,我們究竟應(yīng)該把握住哪些東西呢?
                1、計(jì)算出自己需要的View變換矩陣,從此告別gluLookAt或者D3DXMatrixLookAtLH
                首先選擇Eye所在世界中的位置,比如說在(4,4,4)處。選擇目光所看的點(diǎn),比如原點(diǎn)O(0,0,0),或者一個(gè)方向向量 D(-4,-4,-4)
                選擇一個(gè)世界坐標(biāo)系中Up向量,在GL中就是UpTmp(0,1,0)
                得到一個(gè)新向量C = cross(D,UpTmp)。注意是D叉乘UpTmp。
                仍掉那個(gè)UpTmp。U(Up)= cross(C,D)
               
                完成了大半工作了!讓我們繼續(xù)。

                D.normalize();C.normalize();D.normalize();把向量縮放為單位長度。
                構(gòu)造這個(gè)矩陣。你可以理解為一個(gè)定義在原點(diǎn)的旋轉(zhuǎn)矩陣:
                matrix4f v( c[0],c[1],c[2],0,
                            u[0],u[1],u[2],0,
                            -d[0],-d[1],-d[2],0,
                            0,0,0,1
                    );

                再次引用Eye的位置(4,4,4),構(gòu)造一個(gè)translate矩陣:
                matrix4f t(1,0,0,-4,
                            0,1,0,-4,
                            0,0,1,-4,
                            0,0,0,1
                    );//注意是負(fù)的,因?yàn)檫@是用center - eyepos得到的

                有了這兩個(gè)矩陣,一切就都好辦了。我們就可以得到一個(gè)View Transform的完整矩陣:
                matrix4f ViewTransformMatrix = v.mult_right(t);注意是右乘,它的效果等同于:

                glMatrixMode(GL_MODELVIEW);
                glLoadIndentity();
                glMultMatrixf(v);//這里只是比喻一下
                glTranslatef(-4,-4,-4);

                有了這個(gè)變換矩陣后,我們還需要它的逆矩陣。
                matrix4f ViewTransformInverseMatrix = ViewTransformMatrix.inverse();
               
                接下來把數(shù)據(jù)放到2個(gè)數(shù)組中去。

                for( i = 0;i<4;i++ )
                    
            for( j=0;j<4;j++){
                        ViewTransformMatrixArray[i
            *4+j] =ViewTransformMatrix.element(j,i);
                        ViewTransformInverseMatrixArray[i
            *4+j] =ViewTransformInverseMatrix.element(j,i);
                    }
               
                注意,OpenGL的矩陣是Colunm - Major的順序,所以載入數(shù)組的時(shí)候需要把i j位置替換下。

            static void 
            display(
            void)
            {
                glClear(GL_COLOR_BUFFER_BIT 
            | GL_DEPTH_BUFFER_BIT);
                
                
            //gluLookAt(4,4,4,0,0,0,0,1,0);
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                glMultMatrixf(ViewTransformMatrixArray);
                glMultMatrixf(ViewTransformInverseMatrixArray);

                glMultMatrixf(LightViewTransformMatrix);//我生成了2套矩陣,分別用于Eye和Camera
            /*
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                gluLookAt(4,4,4,0,0,0,0,1,0);

            */
                glPushMatrix();
                glPointSize(
            4.0f);
                    glBegin(GL_LINES);
                        glColor3f(
            0,1.0,0);
                        glVertex3f(
            0,0,0);
                        glVertex3f(
            1,0,0);

                        glVertex3f(
            0,0,0);
                        glVertex3f(
            0,0,1);

                        glVertex3f(
            0,0,0);
                        glVertex3f(
            0,1,0);
                    glEnd();
                glPopMatrix();
                glPushMatrix();
                    glTranslatef(
            0,ypos,0);
                    glutSolidSphere(
            0.5,32,32);
                glPopMatrix();

                glutSwapBuffers();
            }

               
                是不是覺得我多此一舉?為什么要乘來乘去的,不就是回到單位矩陣么?事實(shí)上我曾經(jīng)調(diào)試了很多次,通過比較輸出gluLookAt(4,4,4,0,0,0,0,1,0)生成的矩陣和自己生成的矩陣是否相同,結(jié)果正確的變換到了LightView空間。

            對光源位置的轉(zhuǎn)換
              這個(gè)問題討論已久,仿佛久久沒有標(biāo)準(zhǔn),總是有初學(xué)者不斷提問,而我們回答的也總是一個(gè)子集,治標(biāo)不治本。

              在上文中,我們已經(jīng)生成了用于轉(zhuǎn)換Object Space Coordinates的2個(gè)MV矩陣以及相應(yīng)的逆矩陣。我們先從固定管線的Phone光照模型的GL入手,看看如何正確的轉(zhuǎn)換光源。我們先看看gl manual怎么定義那個(gè)GL_POSITION的。

            The params parameter contains four integer or floating-point values that specify the position of the light in homogeneous object coordinates. Both integer and floating-point values are mapped directly. Neither integer nor floating-point values are clamped. 
            The position 
            is transformed by the modelview matrix when glLight is called (just as if it were a point), and it is stored in eye coordinates. If the w component of the position is 0.0, the light is treated as a directional source. Diffuse and specular lighting calculations take the lights direction, but not its actual position, into account, and attenuation is disabled. Otherwise, diffuse and specular lighting calculations are based on the actual location of the light in eye coordinates, and attenuation is enabled. The default position is (0,0,1,0); thus, the default light source is directional, parallel to, and in the direction of the –z axis.

              意思是,我們指定的坐標(biāo)是Object Space空間的坐標(biāo),然后被MV轉(zhuǎn)換。W是作為齊次縮放系數(shù)使用的,0代表無限遠(yuǎn)好象太陽光束。
              我們上面已經(jīng)提到光源的位置在(-2,4,2)。這里我們寫成無限遠(yuǎn)的(-2,4,2,0)。為了測試起見,我的顯示函數(shù)寫成了切換視點(diǎn)的模式。
            static void 
            display(
            void)
            {
                glClear(GL_COLOR_BUFFER_BIT 
            | GL_DEPTH_BUFFER_BIT);
                
            switch(InWhichSpace){
                    
            case 0:
                        glMatrixMode(GL_MODELVIEW);
                        glLoadIdentity();
                        glMultMatrixf(ViewTransformMatrix);
                        glLightfv(GL_LIGHT0, GL_POSITION, 
            & vec4f(-2,4,2,0)[0]);
                      break;
                    
            case 1:
                        glMatrixMode(GL_MODELVIEW);
                        glLoadIdentity();
                        glMultMatrixf(LightViewTransformMatrix);
                        glLightfv(GL_LIGHT0, GL_POSITION, & vec4f(-2,4,2,0)[0]);

                        
            break;
                    }

                glPushMatrix();
                glPointSize(
            12.0f);
                    glScalef(
            4,4,4);
                    glBegin(GL_LINES);
                        
                        glColor3f(
            1,1,1);
                        
                        glVertex3f(
            0,0,0);
                        glVertex3f(
            -2,4,2);
                        
                        glColor3f(
            1,0,0);
                        glVertex3f(
            0,0,0);
                        glVertex3f(
            1,0,0);
                        
                        glColor3f(
            0,1,0);
                        glVertex3f(
            0,0,0);
                        glVertex3f(
            0,0,1);

                        glColor3f(
            0,0,1);
                        glVertex3f(
            0,0,0);
                        glVertex3f(
            0,1,0);
                    glEnd();
                glPopMatrix();
                glPushMatrix();
                    glTranslatef(
            0,zviewpos,0);
                    glutSolidSphere(
            0.5,32,32);
                glPopMatrix();


                glutSwapBuffers();
            }


              注意看switch開關(guān)。如果我切換到Camera,我將看到這樣。

            cameraview.PNG

            如果切換到光源視圖,是這樣的。

            lightview.PNG

              下面讓我們來看看為什么,還有注釋掉的矩陣乘法代碼。
              第一個(gè)case:我們用載入ViewTransformMatrix,下面聲明LightPosition,是(-2,4,2,0),這個(gè)坐標(biāo)是Object Space的坐標(biāo),在我們的想象中,就是相對于世界坐標(biāo)系的位置,也就是每次我繪制一個(gè)Sphere所產(chǎn)生的位置。
              第二個(gè)case:載入LightViewTransformMatrix,依舊傳入(-2,4,2,0),得到的結(jié)果依舊正確。
              最好自己向自己復(fù)述一遍,注意一定要聯(lián)系我們上面計(jì)算矩陣的算式。

              然后我們把case0代碼改一下。

                switch(InWhichSpace){
                    
            case 0:
                        glutSetWindowTitle(
            "From Camera View");
                        glMatrixMode(GL_MODELVIEW);
                        glLoadIdentity();
                        glMultMatrixf(ViewTransformMatrix);

                        glPushMatrix();
                        //glLoadIdentity();
                        //glMultMatrixf(ViewTransformMatrix);
                        glMultMatrixf(LightViewTransformInverseMatrix);
                        glLightfv(GL_LIGHT0, GL_POSITION, 
            & vec4f(0,0,0,1)[0]);
                        glPopMatrix();


              我們要好好剖析第二個(gè)PushMatrix,LoadIndentity后的那兩個(gè)連續(xù)的矩陣乘法,還有為什么光源成了(0,0,0,1)。NVIDIA的那個(gè)render_scene_from_camera也是這樣放置光源的。讓我們看看為什么。
              這個(gè)V(0,0,0,1)是Object Space中的點(diǎn)。我們先用Mvt代表ViewTransformMatrix,再用Mlvti代表LightViewTransformInverseMatrix。寫成完整的算式應(yīng)該是

                Mvt(Mlvti * V)

              想起來了么?矩陣乘法的結(jié)合形式,意思是,“ vertex V under transformed by Matrix Mlvt”。這里產(chǎn)生光源的過程如下:

                Object Space中的(0,0,0,1)被Mlvti轉(zhuǎn)換到Object空間,是多少呢?(-2,4,2,1),就是光源的相對于世界的位置。其實(shí)你也可以通過vec4f new = LightViewTransformInverseMatrix.mult_matrix_vec(vec4f(0,0,0,1))自己驗(yàn)證。
                由于轉(zhuǎn)換到LightView空間后,產(chǎn)生的是,世界空間和模型空間中的(-2,4,2,1) —— GL沒有世界坐標(biāo),而且我們一般認(rèn)為Object Space是和世界空間重合的。即使在D3D中,一般情況下初始化世界矩陣也都是載入單位矩陣。
                (-2,4,2,1)再乘以Mlvt,又被轉(zhuǎn)換到了 —— 其實(shí)我不知道它在哪里!相對于轉(zhuǎn)換后的CAMERA坐標(biāo)系,它的位置我可以手動(dòng)求出來,得到的是光柵化坐標(biāo)。但是它的位置的確是正確的,效果等同于直接在glLightv中傳入(-2,4,2,1)

            總結(jié):
              對于一個(gè)成熟的3D引擎來說,矩陣都是自己計(jì)算出來的,絕非調(diào)用API自己的指令。在NVIDIA SDK的DEMO中包含了大量成熟的基礎(chǔ)代碼,在不侵犯原作者權(quán)益的情況下應(yīng)該合理的采用,省下諸多開發(fā)調(diào)試時(shí)間。我引用的HEADER文件和代碼。

            posted on 2009-01-06 17:26 doing5552 閱讀(2361) 評(píng)論(0)  編輯 收藏 引用

            只有注冊用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久国产色AV免费看| 久久精品国产精品亚洲下载| 伊人精品久久久久7777| 精品久久久久久久久免费影院| 久久人人爽人人爽人人片av麻烦 | 亚洲国产另类久久久精品| 色综合久久久久久久久五月| 国产情侣久久久久aⅴ免费| 国产ww久久久久久久久久| 性做久久久久久久久| 久久99精品久久久久久| 久久一区二区三区免费| 精品乱码久久久久久久| 亚洲国产成人精品女人久久久| 亚洲国产精品无码久久久秋霞2| 久久亚洲精品视频| 国产激情久久久久久熟女老人 | 国产成人久久AV免费| 久久久久无码专区亚洲av| 久久天天躁狠狠躁夜夜网站| 欧美与黑人午夜性猛交久久久| 精品综合久久久久久888蜜芽| 日产久久强奸免费的看| 久久综合综合久久狠狠狠97色88| 亚洲国产精品无码久久98| 香蕉aa三级久久毛片| 欧美激情精品久久久久久| 国产成人无码精品久久久免费| 久久久亚洲欧洲日产国码aⅴ| 久久久久久伊人高潮影院| 亚洲精品tv久久久久| 亚洲v国产v天堂a无码久久| 狠狠精品干练久久久无码中文字幕| 俺来也俺去啦久久综合网| 久久超碰97人人做人人爱| 久久精品国产第一区二区三区| 少妇高潮惨叫久久久久久| 伊人久久大香线蕉AV色婷婷色| 狠狠色丁香久久婷婷综合| 久久人与动人物a级毛片| 欧美午夜精品久久久久免费视|