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

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藝術無庸技術
<2025年11月>
2627282930311
2345678
9101112131415
16171819202122
23242526272829
30123456

周波 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>
            久久最新视频| 午夜精品久久久久久久男人的天堂| 亚洲欧美视频在线观看| 日韩一区二区免费高清| 亚洲经典在线看| 欧美成人首页| 免费欧美日韩| 亚洲久久一区| 午夜伦欧美伦电影理论片| 亚洲欧美日韩精品在线| 亚洲欧美国产精品专区久久| 欧美亚洲免费高清在线观看| 久久精品国产一区二区三区免费看| 欧美一站二站| 免费视频一区| 国产精品你懂的在线| 好看的日韩视频| 日韩亚洲在线| 久久综合导航| 亚洲香蕉伊综合在人在线视看| 欧美中文字幕在线视频| 久久一区二区三区av| 亚洲日本va在线观看| 亚洲日本电影| 亚洲欧美影院| 日韩亚洲欧美一区二区三区| 欧美亚洲综合在线| 国产精品普通话对白| 日韩亚洲国产欧美| 欧美国产一区在线| 久久精品视频免费播放| 国产日韩精品久久| 午夜精品视频| 性欧美长视频| 国产亚洲一级| 美女视频一区免费观看| 欧美中文在线视频| 黄色一区二区在线| 玖玖玖国产精品| 久久er精品视频| 亚洲国产毛片完整版 | 国产麻豆综合| 一区二区三区欧美| 99精品视频免费在线观看| 国产精品区一区二区三区| 欧美在线精品一区| 亚洲欧美精品中文字幕在线| 日韩视频一区二区三区在线播放免费观看 | 亚洲资源在线观看| 国产精品免费一区二区三区在线观看| 亚洲男人的天堂在线aⅴ视频| 99亚洲精品| 一区二区三区在线不卡| 最新中文字幕亚洲| 国产网站欧美日韩免费精品在线观看| 99视频精品免费观看| 欧美日本一区二区视频在线观看 | 欧美在线一二三| 亚洲午夜黄色| av成人黄色| 久久av一区二区| 母乳一区在线观看| 午夜伦理片一区| 亚洲综合视频在线| 亚洲欧美在线aaa| 亚洲人久久久| 激情成人中文字幕| 国产精品入口日韩视频大尺度| 欧美xx69| 欧美乱在线观看| 亚洲第一精品夜夜躁人人爽 | 久久激情久久| 久久aⅴ国产紧身牛仔裤| 激情91久久| 亚洲精品国产日韩| 樱桃成人精品视频在线播放| 亚洲国产一区二区三区高清| 国产亚洲毛片| 久久久国产精品亚洲一区 | 狠狠色丁香婷婷综合| 一区二区三区免费在线观看| 亚洲人成7777| 欧美日韩国产精品专区| 欧美一区二区三区喷汁尤物| 久久久精品tv| 性欧美大战久久久久久久久| 久久精品国产免费看久久精品| 一区二区视频在线观看| 在线中文字幕日韩| 亚洲精品一区在线观看| 久久在线免费| 欧美影院视频| 国产精品每日更新在线播放网址| 亚洲欧洲综合另类| 亚洲日产国产精品| 欧美午夜激情视频| 亚洲欧美激情在线视频| 欧美粗暴jizz性欧美20| 欧美亚洲一级片| 欧美久久久久久久久| 久久亚洲欧美| 黄色资源网久久资源365| 欧美在线播放一区| 欧美激情国产高清| 中文网丁香综合网| 亚洲一区二区三区精品在线| 国产精品一区二区你懂的| 久久精品官网| 亚洲一品av免费观看| 欧美激情亚洲一区| 久久激情一区| 国产欧美一区二区精品忘忧草| 亚洲视频碰碰| 亚洲大片精品永久免费| 亚洲美女一区| 亚洲一区二区三区乱码aⅴ蜜桃女| 国产精品99久久久久久www| 国产精品一区2区| 欧美激情第1页| 欧美精品aa| 免费看亚洲片| 久热这里只精品99re8久| 欧美成人影音| 美国成人毛片| 亚洲一区在线直播| 亚洲精品在线电影| 欧美阿v一级看视频| 久久精品国语| 久久久久国产免费免费| 久久高清免费观看| 美女精品国产| 免费日韩视频| 蜜臀av国产精品久久久久| 欧美在线网址| 久久狠狠一本精品综合网| 午夜在线精品| 欧美在线一二三四区| 久久精品视频在线播放| 免费在线观看一区二区| 亚洲第一在线视频| 亚洲一区网站| 美女精品视频一区| 国产精品白丝jk黑袜喷水| 欧美精品日韩综合在线| 国产精品99免费看 | 亚洲高清不卡av| 亚洲一区在线观看视频| 久色成人在线| 亚洲国产精品久久久久| 亚洲国产欧美不卡在线观看| 91久久精品国产91久久性色| 国产精品一区亚洲| 国产综合色精品一区二区三区| 国产日本欧美一区二区| 韩国成人精品a∨在线观看| 日韩午夜三级在线| 欧美不卡高清| 老巨人导航500精品| 国产一区二区三区四区hd| 国产精品99久久久久久有的能看 | 欧美日韩亚洲一区二| 99www免费人成精品| 欧美高清视频一区二区三区在线观看 | 亚洲伊人一本大道中文字幕| 久久久伊人欧美| 亚洲欧美视频一区二区三区| 欧美国产一区在线| 国模精品娜娜一二三区| 午夜精品久久久久久久久久久久久 | 亚洲一区黄色| 国内伊人久久久久久网站视频| 亚洲第一精品影视| 在线亚洲电影| 欧美日韩一区二区三区视频| 在线视频观看日韩| 免费亚洲一区| 欧美制服第一页| 国产视频观看一区| 欧美连裤袜在线视频| 国产亚洲综合在线| 久久免费国产精品1| 欧美成人第一页| 性欧美在线看片a免费观看| 欧美xart系列在线观看| 性感少妇一区| 欧美午夜免费电影| 久久久亚洲影院你懂的| 久久精品视频在线| 亚洲久色影视| 久久成人精品视频| 一二三四社区欧美黄| 久久免费国产精品| 久久精品在线| 国产日韩欧美精品一区| 午夜欧美大尺度福利影院在线看| 国产欧美一区二区三区沐欲| 亚洲电影在线| 韩日成人在线| 亚洲小说区图片区| 亚洲免费av观看|