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

posts - 43,  comments - 64,  trackbacks - 0

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

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

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

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

??? 美工把這些數據交給程序員。程序員需要在場景中安放這些模型,比如在地圖上放置建筑車輛人物等等。可是程序員面對的這些模型的坐標,數字可能是一樣的,因為都是相對于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 周波 閱讀(2446) 評論(0)  編輯 收藏 引用 所屬分類: Cg藝術無庸技術
<2007年2月>
28293031123
45678910
11121314151617
18192021222324
25262728123
45678910

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

常用鏈接

留言簿(4)

隨筆分類

隨筆檔案

新聞檔案

同學們Blog

搜索

  •  

積分與排名

  • 積分 - 55425
  • 排名 - 421

最新評論

閱讀排行榜

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            久久阴道视频| 久久成人免费网| 亚洲美女电影在线| 国产日韩在线视频| 国一区二区在线观看| 欧美激情国产精品| 欧美高潮视频| 欧美激情精品久久久六区热门| 久久精品国产综合精品| 久久九九热免费视频| 男女精品网站| 国产精品一区在线播放| 国产日产亚洲精品| 91久久综合| 亚洲日本激情| 亚洲视频狠狠| 欧美激情综合五月色丁香小说| 欧美久久99| 影视先锋久久| 亚洲一区在线播放| 亚洲精品日日夜夜| 久久综合网色—综合色88| 欧美日韩精品系列| 亚洲精品四区| 亚洲大黄网站| 欧美顶级少妇做爰| 亚洲国产精品成人久久综合一区| 亚洲永久免费观看| 99精品99| 国产精品青草久久| 性欧美暴力猛交69hd| 在线性视频日韩欧美| 欧美激情91| 在线视频日韩| 一区二区三区欧美| 国产精品日韩欧美一区二区| 亚洲欧美日韩天堂一区二区| 中文精品一区二区三区| 国产精品v一区二区三区| 亚洲免费在线精品一区| 亚洲欧美中文在线视频| 欧美天堂亚洲电影院在线观看| 亚洲欧洲精品一区| 欧美激情视频给我| 欧美三级日韩三级国产三级 | 另类av一区二区| 午夜一区二区三视频在线观看| 欧美日韩国产综合新一区| 亚洲一区999| 开元免费观看欧美电视剧网站| 亚洲精品乱码久久久久久黑人| 亚洲一区二区三区四区中文 | 亚洲影院色在线观看免费| 亚洲综合国产| 亚洲伦理一区| 久久天堂精品| 欧美一二三视频| 欧美久久久久久| 蜜桃av噜噜一区二区三区| 欧美视频成人| 亚洲精品少妇30p| 亚洲激情网址| 老巨人导航500精品| 亚洲第一成人在线| 欧美日韩在线高清| 激情久久综合| 99精品国产在热久久下载| 中文av一区二区| 欧美亚一区二区| 欧美一区二区三区日韩视频| 国产精品九九| 亚洲一区图片| 久久精品久久99精品久久| 激情91久久| 欧美成年视频| 久久人人爽人人爽| 在线观看精品一区| 欧美劲爆第一页| 日韩视频第一页| 中文在线资源观看网站视频免费不卡| 亚洲欧美一区二区视频| 国产精品啊v在线| 欧美激情1区2区| 亚洲午夜视频在线观看| 欧美视频一区二区三区| 欧美一区二区三区四区在线 | 在线成人性视频| 亚洲人成网站影音先锋播放| 国内精品一区二区三区| 欧美久久久久免费| 老鸭窝亚洲一区二区三区| 亚洲免费观看高清完整版在线观看熊| 亚洲精品久久| 国产精品久久久久久久久果冻传媒| 亚洲高清成人| 欧美一区二区大片| av成人免费观看| 国产亚洲一级高清| 欧美日韩二区三区| 久久精品盗摄| 日韩视频免费| 欧美freesex8一10精品| 亚洲一区不卡| 99国产麻豆精品| 亚洲欧洲一区| 在线观看日韩国产| 国产精品一区亚洲| 国产精品啊啊啊| 欧美精品在线播放| 久久综合中文| 玖玖玖国产精品| 欧美三级免费| 国产一区二区三区高清| 国产日韩精品一区二区| 黄色在线一区| 先锋影音久久久| 亚洲第一黄色网| 亚洲国产免费| 亚洲精品一区二区三区蜜桃久 | 欧美一区国产二区| 亚洲午夜精品久久| 亚洲福利视频一区二区| 欧美韩日亚洲| 亚洲精品视频啊美女在线直播| 亚洲国产精品久久人人爱蜜臀| 免费欧美在线视频| 亚洲欧美日韩综合一区| 久久免费国产精品1| 美女被久久久| 最新中文字幕亚洲| 亚洲综合第一| 亚洲福利视频一区| 性欧美8khd高清极品| 久久精品日韩| 日韩午夜在线| 久久亚洲不卡| 国产精品免费一区二区三区在线观看| 国产精品夜夜夜| 亚洲精品资源| 欧美一区二区三区免费视| 欧美超级免费视 在线| 国产日韩欧美在线视频观看| 亚洲国产欧美一区| 亚洲一区二区在线观看视频| 久久午夜羞羞影院免费观看| 欧美激情一区二区三区蜜桃视频 | 国产欧美va欧美va香蕉在| 亚洲国产成人av好男人在线观看| 亚洲深爱激情| 亚洲福利专区| 猫咪成人在线观看| 亚洲专区免费| 国产精品成人观看视频免费 | 午夜在线精品| 亚洲精品久久久久久下一站| 性欧美精品高清| 欧美理论电影在线播放| 一区二区三区四区在线| 久久久久久精| 亚洲高清久久| 欧美国产激情| 久久一二三四| 一本大道久久a久久精品综合| 欧美视频久久| 羞羞色国产精品| 久久中文在线| 亚洲国产经典视频| 亚洲国产欧美日韩精品| 亚洲欧美在线网| 国产精品sm| 亚洲乱码国产乱码精品精天堂| 久久天堂国产精品| 亚洲午夜精品网| 久久久久一区二区| 在线高清一区| 夜夜嗨av色综合久久久综合网| 欧美日韩网站| 欧美国产日产韩国视频| 久久全球大尺度高清视频| 久久五月天婷婷| 午夜一区不卡| 久久嫩草精品久久久久| 亚洲欧洲一区二区天堂久久| 亚洲少妇最新在线视频| 国产欧美高清| 一区二区三区日韩| 亚洲在线观看| 国精产品99永久一区一区| 亚洲高清不卡在线| 蜜臀av国产精品久久久久| 欧美人成在线| 欧美大片在线观看一区| 欧美激情成人在线视频| 欧美日韩国产首页在线观看| 久久精品一二三区| 欧美视频一区二| 亚洲精品国产精品乱码不99| 激情欧美丁香| 亚洲一区在线播放| 亚洲欧美日本视频在线观看|