青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

doing5552

記錄每日點滴,不枉人生一世

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

公告

常用鏈接

留言簿(24)

我參與的團隊

最新隨筆

搜索

  •  

積分與排名

  • 積分 - 456264
  • 排名 - 49

最新隨筆

最新評論

閱讀排行榜

評論排行榜

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

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

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

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

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

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

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

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

        一個正方體,只要它在我們的映像中從頭到尾都是方方正正的立在場景中,它的向量,無論朝上朝下都應該是相同的,比如(1,0,0)左邊的面,只要我們不旋轉(zhuǎn)這個正方體,它在Local坐標系還是變換后也應該是(1,0,0),這個時候我們用哪個矩陣呢?用MV顯然不同,就需要用MV的Inverse Transpose。在線性代數(shù)中,求一個矩陣的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,符合基本的幾何代數(shù)式,我們需要再給左邊乘以M-1,就是M的逆矩陣。

                  P M-1 M V = 0

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

                 N T V = 0

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

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

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

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

Use Case
    古老的bump mapping

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

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

        Nx Bx Tx
        Ny By Ty
        Nz Bz Tz

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

     再次寫成4x4的形式:

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

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

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

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

        不再新潮的Shadow Mapping

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

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

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

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

解析 NVIDIA中的HW Shadow Mapping Demo
        既然是SM的DEMO,在LightSpace和CameraSpace之間進行變換肯定是少不了的。當然,也應用到了多通道的思想。
    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個顯示列表,可以看出,quad的意義是,處在世界平面的x z平面的尺寸為4x4的一個平面(先畫xy平面內(nèi)的點,不過又旋轉(zhuǎn)了90度)。geometry么,就是那個著名的nurbs茶壺,我們想象為在世界平面y向上的0.4f處。注意每次繪制前都會調(diào)用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();
}

    通篇代碼閱讀完畢,發(fā)現(xiàn)這個函數(shù)最重要。參數(shù)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();
    }

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

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

    激活第一個紋理單元,自動生成紋理,調(diào)整紋理矩陣,準備好紋理,繪制桌面。這里繪制的是,以光源為視點的場景,應該是這個樣子,全面的內(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的光源坐標是在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);
    //這里生成的是一個生成紋理坐標的矩陣,它的形式是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);//讓思路回到上面的那個函數(shù),仔細體會

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

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

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)
   
    完成了大半工作了!讓我們繼續(xù)。

    D.normalize();C.normalize();D.normalize();把向量縮放為單位長度。
    構(gòu)造這個矩陣。你可以理解為一個定義在原點的旋轉(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)造一個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();
   
    接下來把數(shù)據(jù)放到2個數(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ù)組的時候需要把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();
}

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

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

  在上文中,我們已經(jīng)生成了用于轉(zhuǎn)換Object Space Coordinates的2個MV矩陣以及相應的逆矩陣。我們先從固定管線的Phone光照模型的GL入手,看看如何正確的轉(zhuǎn)換光源。我們先看看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轉(zhuǎn)換。W是作為齊次縮放系數(shù)使用的,0代表無限遠好象太陽光束。
  我們上面已經(jīng)提到光源的位置在(-2,4,2)。這里我們寫成無限遠的(-2,4,2,0)。為了測試起見,我的顯示函數(shù)寫成了切換視點的模式。
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所產(chǎn)生的位置。
  第二個case:載入LightViewTransformMatrix,依舊傳入(-2,4,2,0),得到的結(jié)果依舊正確。
  最好自己向自己復述一遍,注意一定要聯(lián)系我們上面計算矩陣的算式。

  然后我們把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后的那兩個連續(xù)的矩陣乘法,還有為什么光源成了(0,0,0,1)。NVIDIA的那個render_scene_from_camera也是這樣放置光源的。讓我們看看為什么。
  這個V(0,0,0,1)是Object Space中的點。我們先用Mvt代表ViewTransformMatrix,再用Mlvti代表LightViewTransformInverseMatrix。寫成完整的算式應該是

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

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

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

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


青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久福利影视| 亚洲精品午夜| 日韩午夜中文字幕| 亚洲大胆人体视频| 欧美一级二区| 羞羞视频在线观看欧美| 欧美日韩爆操| 欧美国产一区在线| 国产亚洲成av人片在线观看桃| 亚洲毛片一区二区| 亚洲国产aⅴ天堂久久| 欧美在线free| 性做久久久久久免费观看欧美| 欧美理论电影在线播放| 欧美成人免费在线视频| 在线观看日韩| 久久久久久综合网天天| 久久久久久一区| 国产一区二区黄| 午夜亚洲伦理| 久久精品道一区二区三区| 国产精品乱码久久久久久| 在线综合+亚洲+欧美中文字幕| 一区二区三区日韩精品| 欧美巨乳在线观看| 日韩一级免费| 亚洲一区二区三区欧美| 国产精品久久久久久久久果冻传媒 | 国产精品国产三级国产aⅴ入口| 亚洲国产精品久久人人爱蜜臀| 狠狠综合久久av一区二区小说| 亚洲中无吗在线| 欧美专区日韩视频| 国产午夜精品全部视频在线播放| 亚洲视频在线观看免费| 性久久久久久久| 国产视频精品网| 欧美影院在线| 欧美成人高清| 亚洲美女毛片| 欧美手机在线| 亚洲一区欧美二区| 久久精品一二三区| 亚洲第一二三四五区| 欧美国产专区| 亚洲视频在线一区| 久久久午夜电影| 亚洲国产精品一区二区三区| 欧美成人影音| 国产精品99久久久久久宅男| 久久精品青青大伊人av| 亚洲国产美女久久久久 | 欧美一区亚洲一区| 蜜臀av性久久久久蜜臀aⅴ| 91久久在线视频| 欧美午夜精品理论片a级按摩 | 欧美三级午夜理伦三级中文幕| 亚洲一区精品电影| 欧美电影在线观看| 午夜久久久久久| 亚洲国产精品va在线看黑人动漫 | 国产亚洲精品久久飘花| 久久偷窥视频| 中文精品视频| 欧美大片免费看| 亚洲一区在线免费| 伊人婷婷久久| 国产精品国产三级国产aⅴ无密码| 久久er精品视频| 日韩视频免费观看高清在线视频| 久久精品1区| 一区二区三区 在线观看视频| 国产日韩视频| 欧美日韩中文字幕日韩欧美| 久久久一本精品99久久精品66| 一本色道综合亚洲| 欧美fxxxxxx另类| 欧美一区免费视频| 亚洲美女视频| 尹人成人综合网| 国产精品午夜在线| 欧美日韩一级片在线观看| 久久视频在线看| 午夜亚洲精品| 亚洲视频在线观看免费| 亚洲国产视频a| 久久夜色精品国产噜噜av| 亚洲中字黄色| 一区二区三区久久精品| 亚洲高清资源| 亚洲第一精品福利| 国产亚洲成精品久久| 国产精品男gay被猛男狂揉视频| 欧美大片在线观看一区| 久久这里有精品15一区二区三区| 午夜精品久久久久久久99樱桃| 日韩视频永久免费观看| 最新精品在线| 亚洲第一福利视频| 麻豆91精品91久久久的内涵| 久久久久国产免费免费| 欧美一级二级三级蜜桃| 亚洲欧美在线x视频| 亚洲一级影院| 亚洲一二三区精品| 在线午夜精品自拍| 一区二区三区视频在线看| 99视频日韩| 亚洲一区二区高清| 亚洲一区国产精品| 亚洲欧美一区二区三区极速播放| 亚洲午夜电影网| 亚洲手机视频| 午夜亚洲影视| 欧美一区二区三区在线观看视频| 欧美一区二区女人| 久久久久久香蕉网| 久久免费国产| 欧美暴力喷水在线| 亚洲区中文字幕| 亚洲精品影视在线观看| 一区二区三区视频免费在线观看| 一区二区三区高清视频在线观看| 在线综合亚洲| 久久成人一区二区| 蜜乳av另类精品一区二区| 欧美激情精品久久久久久黑人| 欧美日韩国产成人精品| 国产精品a级| 国产一区二区三区在线免费观看| 黄色在线一区| 一本久道久久综合中文字幕| 亚洲欧美变态国产另类| 久久精品网址| 亚洲国产三级网| 亚洲在线一区| 久久久久久亚洲综合影院红桃| 毛片基地黄久久久久久天堂| 欧美精品日韩综合在线| 国产欧美精品xxxx另类| 在线精品视频一区二区三四| 99精品国产热久久91蜜凸| 午夜精品成人在线| 蘑菇福利视频一区播放| 99在线精品观看| 久久精品一区| 欧美午夜在线视频| 精品不卡在线| 午夜精品久久久久久久99水蜜桃| 葵司免费一区二区三区四区五区| 亚洲六月丁香色婷婷综合久久| 欧美一区二区视频在线观看| 欧美精品网站| 黑人巨大精品欧美一区二区小视频 | 欧美福利小视频| 国产精品亚发布| 亚洲精品一区二区三区不| 午夜欧美不卡精品aaaaa| 欧美激情va永久在线播放| 亚洲欧美日韩在线观看a三区| 免费亚洲电影在线| 国产综合久久久久久| 亚洲综合三区| 亚洲国产婷婷综合在线精品 | 欧美激情在线免费观看| 国产日产亚洲精品| 亚洲午夜av| 亚洲国内欧美| 久久综合久久久久88| 国产日韩欧美精品综合| 亚洲在线一区二区| 亚洲国产清纯| 久久久亚洲人| 国产一区二区成人久久免费影院| 亚洲性视频h| 亚洲美女中文字幕| 欧美激情久久久久| 亚洲欧洲一区| 欧美激情一区二区三区高清视频 | 亚洲免费在线视频| 欧美日韩亚洲综合| 亚洲最新合集| 亚洲黄色毛片| 欧美第一黄色网| 亚洲高清视频在线观看| 美女任你摸久久| 欧美在线高清视频| 韩国成人精品a∨在线观看| 欧美一区二区三区在线看| 亚洲一区黄色| 国产精品一区一区| 欧美一区二区福利在线| 午夜精品短视频| 国产精品午夜视频| 久久精品国产久精国产爱| 欧美一激情一区二区三区| 国产偷久久久精品专区| 久久综合色综合88| 久久综合九色九九| 亚洲精品国久久99热|