• <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>
            posts - 43,  comments - 64,  trackbacks - 0

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

            ??? 矩陣變換是個相當重要的要點,難度應該僅次于數據結構部分。倒不是因為本身掌握知識對能力的要求有多么高,而是因為從來沒有人說明白過在實際情況中如何應用。

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

            ??? 當美工制作模型的時候,肯定以建模工具提供的那個坐標系為基本坐標系進行建模。模型的頂點都是相對于各自基本的坐標系,我們稱之為Local坐標系統或者Object坐標系。

            ??? 美工把這些數據交給程序員。程序員需要在場景中安放這些模型,比如在地圖上放置建筑車輛人物等等??墒浅绦騿T面對的這些模型的坐標,數字可能是一樣的,因為都是相對于Local坐標系統,就這樣一股腦的載入,肯定都是在“世界”的中心位置進行繪制,根本不可能分開。于是我們需要對各個物體,也就是各個獨立的坐標系統進行Transform(包括Translate Rotate Scale操作)。

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

            ??? 比如我們輸入glTranslatef(1.0,0,0);glutSolidCube(4),其實它產生的“真正頂點”是,(3,2,2),(-1,-2,-2),統統向x方向移動了一個位置。如果你在自己的范例程序里看不到是因為perspective中的far near planes沒有設置好。這樣我們就仿佛實現了平移以及旋轉等等操作,注意,是仿佛。

            ??? 我們把變換頂點的矩陣一般稱為MV(Model View)矩陣,把和在一起一步到位的矩陣稱為MVP矩陣,在GLSL中就有gl_ModelViewMatirx和gl_ModelViewProjectionMatrix這兩個Uniform Matirx。我們輸入一個頂點,希望把它放到這個世界的正確位置上,就需要乘以適合它自己的MV,因為不一樣的模型當然需要不一樣的世界位置。矩陣乘法就可以完成這項神奇的工作。可是向量的概念則很大不同。

            ??? 一個正方體,只要它在我們的映像中從頭到尾都是方方正正的立在場景中,它的向量,無論朝上朝下都應該是相同的,比如(1,0,0)左邊的面,只要我們不旋轉這個正方體,它在Local坐標系還是變換后也應該是(1,0,0),這個時候我們用哪個矩陣呢?用MV顯然不同,就需要用MV的Inverse Transpose。在線性代數中,求一個矩陣的Inverse然后Transpose,與先求Transpose再求Inverse,這兩邊是完全相等的。在GLSL中,其實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)代表頂點,P(a,b,c,d)代表一個平面。相應的平面方程可以寫作,PV = 0,也就是ax + by + cz = 0,有個向量垂直于頂點所在的那個面。

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

            ?????????????????? PMV = ?

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

            ??????????????????P M-1 M V = 0

            ??? 有一個向量垂直于這個平面。于是引入n一個我們真正意義上的面向量,和平面內的任意一個向量都應該是正交的。那個任意向量如何獲得呢?最簡單的就是,那個頂點和原點構成的向量 —— 因為在我們最初的式子里面,默認這個平面就是通過原點的。我們想讓等式依舊成立,式子變成:

            ?????????????????N T V = 0

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

            ???????????????? (如果我沒有理解錯的話,V應該隱含著用了2次)

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

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

            Use Case
            ??? 古老的bump mapping

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

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

            ??????? Nx Bx Tx
            ??????? Ny By Ty
            ??????? Nz Bz Tz

            ??? 這里隱含的意思是:把頂點變換到以N B T為3個坐標軸的空間中,無論這個坐標系是不是和世界坐標系統“傾斜”的。

            ??? 再次寫成4x4的形式:

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

            ??? 注意第四列的連續三個0。代表的意義是:每個頂點變換到以這個N B T為坐標軸的坐標系后,需要Translate到的位置。因為在Bump Mapping中我們不需要對頂點的位置變換,所以隱含著寫成3x3的Matrix就足夠了。

            ??? 這里的這個3x3矩陣,其實就是對于每個頂點來說的MV矩陣,轉換的就是那個LightPosition??墒沁@里又有一個問題,如果轉換的不止一個LightPosition,還有Normal怎么辦?因為這是個“斜”的坐標系,原來的(1,0,0)可不是變換后的(1,0,0)。記起來了么?Inverse Transpose!我們只要把原來的向量乘以這個以NBT為正交坐標軸向量的MV的Inverse Transpose,就可以得到正確的結果了。(我說得對么?)其實,因為這個矩陣3個向量都已經normalized,它的Inverse = Transpose,所以這個NBT矩陣的IT矩陣就是它自己!

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

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

            ??????? 不再新潮的Shadow Mapping

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

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

            ??? 有的時候我們需要自己獨立求逆矩陣,如何辦到呢?這可不是紙上的線性代數考試可以用初等變換計算。
            ????????
            ??? 矩陣的變換和逆變換就那么3種,Translate,Rotate,Scale。
            ??? 我們知道MV =? T * R。(T R代表為了實現Translate和Rotate相應的矩陣)
            ??? 則Inv(MV) = Inv(T) * Inv(R)
            ????????
            ??? 也就是說,假設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)
            ??? 計算相應子矩陣的逆矩陣,Inv(T)就是
            ???????(?100 -P)
            ?????? ( 010 -P)
            ???????( 001 -P)
            ???????( 000? 1)
            ??? 逆旋轉矩陣可能復雜一些,不過依舊可以計算出來,也就是它的Transpose。然后乘一下,逆矩陣就出來了。

            ??? 目前我所想到的關鍵就這么多,更多的懇請大家添加,謝謝。

            解析 NVIDIA中的HW Shadow Mapping Demo
            ?????? 既然是SM的DEMO,在LightSpace和CameraSpace之間進行變換肯定是少不了的。當然,也應用到了多通道的思想。
            ????quad.new_list(GL_COMPILE);
            ????glPushMatrix();
            ????glRotatef(
            -90,?1,?0,?0);
            ????glScalef(
            4,4,4);
            ????glBegin(GL_QUADS);
            ????glNormal3f(
            0,?0,?1);
            ????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個顯示列表,可以看出,quad的意義是,處在世界平面的x z平面的尺寸為4x4的一個平面(先畫xy平面內的點,不過又旋轉了90度)。geometry么,就是那個著名的nurbs茶壺,我們想象為在世界平面y向上的0.4f處。注意每次繪制前都會調用glPushMatrix把MV矩陣推入Stack,這個步驟相當重要,因為我們還不知道前面的坐標系,究竟在哪里,不過后面我們又看到了如何解決這個問題。

            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();
            }

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

            ??? 不過這個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();
            ????}

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

            ???
            這里的
            view.apply_inverse_transform()就好理解了。不管我渲染什么,我總是要先把坐標系放回到世界坐標系中的原點處,保存好當前矩陣,然后再調用顯示列表。不過我們又發現那個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);
            }

            ??? 激活第一個紋理單元,自動生成紋理,調整紋理矩陣,準備好紋理,繪制桌面。這里繪制的是,以光源為視點的場景,應該是這個樣子,全面的內容解析看注釋。

            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的光源坐標是在object coordinates中,也就是它要被I矩陣轉換,結果依舊是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);
            ??? //這里生成的是一個生成紋理坐標的矩陣,它的形式是I*T*S*P,提供給處于以光源為原點的場景坐標使用。
            ??? 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);//讓思路回到上面的那個函數,仔細體會

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

            再把這個函數貼出來,請自己仔細推敲變換過程。

            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();
            }

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

            ??? D.normalize();C.normalize();D.normalize();把向量縮放為單位長度。
            ??? 構造這個矩陣。你可以理解為一個定義在原點的旋轉矩陣:
            ??? 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),構造一個translate矩陣:
            ??? matrix4f t(1,0,0,-4,
            ??? ??? ??? ??? 0,1,0,-4,
            ??? ??? ??? ??? 0,0,1,-4,
            ??? ??? ??? ??? 0,0,0,1
            ??? ??? );//注意是負的,因為這是用center - eyepos得到的

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

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

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

            ????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的順序,所以載入數組的時候需要把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();
            }

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

            對光源位置的轉換
              這個問題討論已久,仿佛久久沒有標準,總是有初學者不斷提問,而我們回答的也總是一個子集,治標不治本。

              在上文中,我們已經生成了用于轉換Object Space Coordinates的2個MV矩陣以及相應的逆矩陣。我們先從固定管線的Phone光照模型的GL入手,看看如何正確的轉換光源。我們先看看gl manual怎么定義那個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.

              意思是,我們指定的坐標是Object Space空間的坐標,然后被MV轉換。W是作為齊次縮放系數使用的,0代表無限遠好象太陽光束。
              我們上面已經提到光源的位置在(-2,4,2)。這里我們寫成無限遠的(-2,4,2,0)。為了測試起見,我的顯示函數寫成了切換視點的模式。
            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開關。如果我切換到Camera,我將看到這樣。

            cameraview.PNG

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

            lightview.PNG

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

              然后我們把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();


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

                Mvt(Mlvti * V)

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

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

            總結:
              對于一個成熟的3D引擎來說,矩陣都是自己計算出來的,絕非調用API自己的指令。在NVIDIA SDK的DEMO中包含了大量成熟的基礎代碼,在不侵犯原作者權益的情況下應該合理的采用,省下諸多開發調試時間。

            我的源程序和來自NV SDK的那個header文件也包括在一起,包含了向量計算,矩陣操作等等,對數據的處理相當實用!

            這里下載

            posted on 2006-12-10 13:46 周波 閱讀(2417) 評論(0)  編輯 收藏 引用 所屬分類: Cg藝術 、無庸技術
            <2025年5月>
            27282930123
            45678910
            11121314151617
            18192021222324
            25262728293031
            1234567

            周波 87年出生 南京林業大學05421班242信箱 專業木材科學與工程工業裝備與過程自動化 遷移到 jedimaster(dot)cnblogs(dot)com

            常用鏈接

            留言簿(4)

            隨筆分類

            隨筆檔案

            新聞檔案

            同學們Blog

            搜索

            •  

            積分與排名

            • 積分 - 54138
            • 排名 - 421

            最新評論

            閱讀排行榜

            久久国产热精品波多野结衣AV| 人人狠狠综合久久亚洲婷婷| 久久精品一区二区影院| 伊人丁香狠狠色综合久久| 观看 国产综合久久久久鬼色 欧美 亚洲 一区二区 | 伊人久久大香线蕉综合网站| 久久中文字幕人妻丝袜| 久久er99热精品一区二区| 亚洲伊人久久大香线蕉苏妲己| 久久国产免费直播| 久久精品国产99国产精品亚洲| jizzjizz国产精品久久| 中文成人久久久久影院免费观看| 久久永久免费人妻精品下载| 天天综合久久久网| 亚洲精品无码久久久久| 久久精品二区| 亚洲一区中文字幕久久| 久久久精品人妻一区二区三区蜜桃 | 久久免费视频6| 精品久久久久久久无码| 久久久久亚洲av成人无码电影| 人妻精品久久无码区| 亚洲国产成人久久综合区| 99久久国产免费福利| 国产成人无码久久久精品一| 久久久亚洲欧洲日产国码是AV| 久久精品国产亚洲欧美| 老色鬼久久亚洲AV综合| 久久99热这里只有精品国产| 久久婷婷人人澡人人| 久久婷婷久久一区二区三区| 久久99精品久久久久久久不卡 | 久久99免费视频| 亚洲国产精品久久久天堂| 奇米影视7777久久精品人人爽| 久久嫩草影院免费看夜色| 精品国产婷婷久久久| 久久久久亚洲精品天堂久久久久久 | 久久国产成人精品麻豆| 久久ww精品w免费人成|