• <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++博客 :: 首頁(yè) :: 聯(lián)系 :: 聚合  :: 管理
              73 Posts :: 0 Stories :: 94 Comments :: 0 Trackbacks

            公告

            常用鏈接

            留言簿(24)

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

            最新隨筆

            搜索

            •  

            積分與排名

            • 積分 - 454884
            • 排名 - 48

            最新隨筆

            最新評(píng)論

            閱讀排行榜

            評(píng)論排行榜

            前言:以前寫(xiě)了第一版,有些初學(xué)者反映許多地方還不是很明白。一來(lái)是因?yàn)檎f(shuō)得比較瑣碎,二來(lái)內(nèi)容太多不容易理解。于是乎我就把原文改寫(xiě)以更加的明白直觀,并且從建模、美工技術(shù)的角度開(kāi)始講述矩陣、坐標(biāo)變換等知識(shí),希望可以更好的幫助初學(xué)者理解。如果有錯(cuò)誤,敬請(qǐng)不吝賜教。如果轉(zhuǎn)載請(qǐng)附上我的聯(lián)系方式謝謝。

            一、符號(hào)以及命名風(fēng)格約定

            Object Coordinates = LC,以模型內(nèi)部的“原點(diǎn)”為原點(diǎn)的坐標(biāo)系。
            Local Coordinates = OC,以每個(gè)頂點(diǎn)處的向量Normal為Z軸,正切Tangent向量為X(或者Y)軸,以Binormal為Y(或者X)軸的一個(gè)坐標(biāo)系。
            World Coordinates = WC,應(yīng)用程序自己維護(hù)的一個(gè)坐標(biāo)系統(tǒng),主要作用是儲(chǔ)存各個(gè)模型的位置,在處理的流水線中把模型正確的在假想的世界中變換。
            Camera Space  = Eye Space = ES,真正的視空間。
            ModelView矩陣 = MV,把模型從模型空間直接變換到Camera空間的矩陣。
            以此類推,ModelViewProject = MVP。
            ModelViewInverseTranspose = MVit,MV矩陣做過(guò)線性代數(shù)IT操作后的矩陣,用來(lái)把OC空間每個(gè)頂點(diǎn)的向量變換到ES中。
            以此類推,MVi = ModelViewInverse,MVt = ModelViewTranspose,以及MVPit = ModelViewProjectionInverseTranspose,Wiv = WorldInverseTranspose

            二、從美工說(shuō)起

              讓每個(gè)藝術(shù)設(shè)計(jì)系畢業(yè)的美工都如同我們一樣精通線性代數(shù)高等數(shù)學(xué)是幾乎不可能的事情,尤其是中國(guó)四年本科的學(xué)生,會(huì)熟練使用3dsmax、maya的都很少,更別提讓他們闡述光照模型等CG基本概念了。于是我們需要首先了解美工的工作,以及他們制作出來(lái)的素材。這是一幅來(lái)自3dsmax中Perspective視圖的截圖,大家注意左下角畫(huà)白 色雙環(huán)的部分,我把3dsmax內(nèi)部的坐標(biāo)軸給標(biāo)示了出來(lái)。

            3dsmax

             

              嗯……看似還不錯(cuò)的樣子,一個(gè)簡(jiǎn)單的室內(nèi)模型。里大家可能有疑問(wèn),這有什么特殊的么?有,而且很大。如果是玩過(guò)《翡翠帝國(guó)》或者《鬼武者》就知道,這些游戲是使用3dsmax建模的,但是它的BINK動(dòng)畫(huà)和游戲場(chǎng)景所使用的模型是一樣的,這里面就牽涉到的一個(gè)模型的導(dǎo)出問(wèn)題。對(duì)于從3dsmax導(dǎo)出的模型來(lái)說(shuō),遵守我們?nèi)粘?shù)學(xué)上的坐標(biāo)系(注意我沒(méi)有使用任何左右手的術(shù)語(yǔ)),Z向上,XY表示水平面。所以,如果你想使用3DS模型,并且和我一樣使用它的貼圖、材質(zhì),而且還想就在這個(gè)世界中進(jìn)行坐標(biāo)平移等等工作,請(qǐng)準(zhǔn)備兩樣神兵利器 —— Deep Exploration和lib3ds,如何使用我后面會(huì)舉例說(shuō)明。

            RH
             

             

               這里,如果把這個(gè)場(chǎng)景導(dǎo)出,那么,這個(gè)室內(nèi)場(chǎng)景其實(shí)就表達(dá)了一個(gè)世界坐標(biāo)系WC。因?yàn)檫@個(gè)坐標(biāo)系是由3dsmax自己維護(hù)的。我們?cè)購(gòu)?dsmax中導(dǎo)出一個(gè)模型。注意左下角,坐標(biāo)系我用雙圓著重了。

            Rose

             

              如果假如這個(gè)玫瑰花要被放入上面的室內(nèi)場(chǎng)景,為了精確起見(jiàn),首先在3dsmax中要把這個(gè)玫瑰花對(duì)齊到它自己的XYZ坐標(biāo)原點(diǎn)。然后在OpenGL中,如果從頭開(kāi)始繪制,我們可以這樣寫(xiě),

            glMatrixMode(GL_MODELVIEW);

            glLoadIdentity();

            gluLookAt(/*Somewhere*/,/*Somewhere*/,/*must be vec3(0,0,1)*/);

            DrawScene();

            glPushMatrix();

            glTranslatef(/*Somewhere*/);

            DrawWhiteRose();

            glPopMatrix();

              為什么繪制Scene室內(nèi)場(chǎng)景的時(shí)候沒(méi)有使用glTranslatef做變換?

               因?yàn)槲野褕?chǎng)景當(dāng)作了一個(gè)“世界”。

              為什么繪制Rose的時(shí)候使用了glTranslatef?

              因?yàn)镽ose自己有一個(gè)LC,為了把它放到世界內(nèi)的某個(gè)角落,需要做平移變換。

              所以說(shuō)對(duì)于一個(gè)成熟的CG Coder來(lái)說(shuō),他肯定是使用導(dǎo)出的模型,使用它里面的燈光、場(chǎng)景、攝像機(jī)參數(shù),而不是將一些參數(shù)硬編碼。當(dāng)然隨著編程能力的提高,你一定也會(huì)這樣,尤其是對(duì)于大型成熟的GAME、3D軟件,都有自己內(nèi)部的模型格式,比如,WarCraft3。

             

            三、看一下

              按照《Writing RenderMan Shaders Siggraph 1992 Course 21》這篇文章中的教程PPT,一切都簡(jiǎn)化為一下幾個(gè)公式,

            XformPixar

             

              我想上面的三個(gè)式子已經(jīng)解決了99%的問(wèn)題。式子中的M就相當(dāng)于GL中的MV矩陣,轉(zhuǎn)換后的空間就是ES。對(duì)于變換頂點(diǎn),整個(gè)過(guò)程可以如下圖所示,

            GL

             

              在這里,為什么把Scale、Rotate、Translate放在一起,這是因?yàn)?,只要你知道你在做什么,你就可以調(diào)整順序。

              很多初學(xué)者在寫(xiě)GL程序的時(shí)候總會(huì)被Rotate、Translate變換弄的頭大,其實(shí),對(duì)于我也一樣^_^。但是我知道有些好方法可以控制復(fù)雜度,少出錯(cuò)。

            whofirst

             

              先平移還是先旋轉(zhuǎn),看了上圖估計(jì)就明白了八九分,至于要什么效果可是心隨你動(dòng)。但是要注意,這個(gè)圓柱本身的LC可以一點(diǎn)都沒(méi)有變化,每個(gè)頂點(diǎn)相對(duì)于自己的原點(diǎn)都沒(méi)有任何變化。實(shí)時(shí)上,這種情況在實(shí)際編程中不會(huì)非常復(fù)雜。

              隨便打開(kāi)一個(gè)游戲,場(chǎng)景大部分是不動(dòng)的,相應(yīng)的,它們的表面Normal也是不變的。運(yùn)動(dòng)的東西有,玩家角色本身,場(chǎng)景中的一些運(yùn)動(dòng)物體比如街上的汽車,夜總會(huì)門(mén)口拉客的女人。為什么我使用了兩個(gè)這兩個(gè)運(yùn)動(dòng)范例,因?yàn)檫@兩個(gè)例子牽涉到2樣?xùn)|西,一樣是只變換靜態(tài)的頂點(diǎn)和向量,一樣是變換動(dòng)態(tài)的向量。

              一輛車,開(kāi)始建模,以XYZ為平面,車輪正好貼著在XY平面。如果我們以“米”為單位建立世界模型,假設(shè)汽車先在( 0,0,0 )處出現(xiàn),然后直線移動(dòng)到( 6,8,0 )處,那么就相當(dāng)于是平移,程序我們可以這樣寫(xiě),

            glMatrixMode(GL_MODELVIEW);

            glLoadIdentity();

            gluLookAt(/*Somewhere*/,/*Somewhere*/,/*must be vec3(0,0,1)*/);

            DrawScene();

            float delta_X = Speed*TimeSlice*0.6;//X方向的位移累加量

            float delta_Y = Speed*TimeSlice*0.8;//Y方向的位移累加量

            float S_X = 0.0;

            if( S_X < 6.0 ){

            glPushMatrix();

            glTranslatef( delta_X,delta_Y,0 );

            DrawCar();

            glPopMatrix();

            S_X += delta_X;

            }else{

            glPushMatrix();

            glLoadIdentity();

            glTranslatef( 6,8,0 );

            DrawCar();

            glPopMatrix();

            }

              哈,一切都很簡(jiǎn)單,模擬了一輛車的移動(dòng)。在這里,首先讓它一步一步的移動(dòng),到了目的地后程序判斷是不是應(yīng)該停下來(lái)了,如果到了目的地那么只需要在目的地繪制它就可以了。所以說(shuō)對(duì)于大量物體運(yùn)動(dòng)的場(chǎng)景,CPU的負(fù)擔(dān)是很大的。我們想象一下,對(duì)于一個(gè)直線運(yùn)動(dòng)的物體來(lái)說(shuō),它的表面Normal會(huì)變化么?答案是,不會(huì)。但是假如車輛拐彎了,也就是牽涉到了旋轉(zhuǎn),Normal肯定是變化了。所以對(duì)于一般情況下,向量是根本不需要變換的,如果物體僅僅是平移。

              我們已經(jīng)知道,變換Normal是通過(guò)MVit矩陣。假設(shè)我們的MV只是通過(guò)glTranslatef( 6,8,0 )得到,那么變換頂點(diǎn)就是這個(gè)過(guò)程。假設(shè)車的中心在(0,0,0),向量為(0,0,1)指向天空。

            Eqn0

             

              變換向量就是這個(gè)樣子,

            Eqn1

             

              其實(shí)變換向量只需要MV矩陣的左上3x3的it,也就是成了這個(gè)樣子,

            Eqn2
             

             

              很明顯,對(duì)于只在WC中Translate平移變換來(lái)說(shuō),向量其實(shí)是不需要變換的。但是旋轉(zhuǎn)了,怎么辦?好辦,動(dòng)手計(jì)算Wit就是了。怎么算??jī)煞N方法。

              第一種:先清空當(dāng)前的MV,然后反相變換,然后把它弄出來(lái)再Transpose一下就可以了。比如繪制車的方式是這個(gè)樣子,

            glMatrixMode(GL_MODELVIEW);

            glLoadIdentity();

            gluLookAt(/*Somewhere*/,/*Somewhere*/,/*must be vec3(0,0,1)*/);

            glPushMatrix();

            glTranslatef( 6,8,0 );

            glRotatef(45,0,0,1);

            DrawCar();

            glPopMatrix();

            那么下面的代碼得到Wit儲(chǔ)存在數(shù)組中

            GLdouble WCit[16];

            glMatrixMode(GL_MODELVIEW);

            glPushMatrix();

            glLoadIdentity();

            glTranslatef(-6,-8,0);

            glRotatef(-45,0,0,1);

            glGetDoublev(GL_TRANSPOSE_MODELVIEW_MATRIX_ARB,WCit);

            glPopMatrix();

              第二種方法其實(shí)更加的簡(jiǎn)便,使用現(xiàn)成的庫(kù),比如NVIDIA SDK中附帶的nv_math庫(kù)、MathGL++等。比如上面的變化,用nv_math改寫(xiě)后就是這樣,

            mat4 wc(1,0,0,0,
                            0,1,0,0,
                            0,0,1,0,
                            0,0,0,1);
            vec3 t(6,8,0);
            vec3 r(0,0,1);
            wc.set_translation(t);
            wc.set_rot(nv_two_pi*30.0/360.0,r);注意這里的set_rot接受弧度
            for( int i =0 ; i<4; i++){
                cout<<wc.mat_array[4*i+0]<<'\t'<<wc.mat_array[4*i+1]<<'\t'<<wc.mat_array[4*i+2]<<'\t'<<wc.mat_array[4*i+3]<<endl;
            }

              如果是用MathGL++就是這個(gè)樣子,

            GLMatrix<double> mat;
            mat.identity();
            mat.loadTranslate(6,8,0);
            mat.applyRotateZ(30);//這里就方便一些可以直接輸入角度
            for( int i =0 ; i<4; i++){
                cout<<mat[4*i+0]<<'\t'<<mat[4*i+1]<<'\t'<<mat[4*i+2]<<'\t'<<mat[4*i+3]<<endl;
            }

              他們都會(huì)輸出下列矩陣的形式,但是注意!沒(méi)有被轉(zhuǎn)置!所以在載入GL的時(shí)候需要轉(zhuǎn)置,對(duì)于這個(gè)簡(jiǎn)單的變換來(lái)說(shuō),也就是保證第四列是你的Translate值。

            Eqn3

              你覺(jué)得這樣的方法怎么樣?如果變換太多,自己根本不清楚的。所以,我推薦用第二種方法,手動(dòng)計(jì)算絕對(duì)不會(huì)出錯(cuò),而且可以與自己的程序框架結(jié)合在一起。

            四、我好想見(jiàn)到她

              上面說(shuō)的都是WC的事情,現(xiàn)在呢,讓我們結(jié)合OpenGL程序來(lái)談如何做到準(zhǔn)確的場(chǎng)景變換,同時(shí)自己又不會(huì)迷糊。我上篇一文章用3dsmax做了個(gè)CornelBox然后導(dǎo)了出來(lái),在Deep Exploration中觀察它,

            RHmarble

             

              哈,注意看Scene Tree,我這個(gè)里面有個(gè)FreeSpot燈光,ITVIRGEN也就是雕塑,還有CornelBox的5個(gè)平板。讓我們看一下那個(gè)雕塑的屬性,

            ITVIRGENProp

             

              哇塞,東西挺詳細(xì),包括這個(gè)模型的位置旋轉(zhuǎn)角度都有了,下面還有一個(gè)挺復(fù)雜的變換矩陣。在這里有些朋友可能會(huì)想,“當(dāng)我載入這個(gè)3DS的時(shí)候,是不是也要將屬于這個(gè)雕塑MESH的每個(gè)頂點(diǎn)都用這個(gè)矩陣去變換一下呢?”,哈,如果你想了這個(gè)問(wèn)題說(shuō)明你已經(jīng)登堂入室了。我的回答是:有好心人幫我們做過(guò)了這個(gè)事情,但是是誰(shuí)這么好心呢?應(yīng)該是3dsmax,因?yàn)樵趌ib3ds的中我沒(méi)有找到任何有關(guān)于矩陣變換的代碼。也就是說(shuō),從3dsmax導(dǎo)出的場(chǎng)景可以被當(dāng)作是一個(gè)WC來(lái)看,所有的模型都已經(jīng)各就各位,所以我們就不需要讀取mesh的頂點(diǎn)后再用這個(gè)矩陣去變換。

              下一個(gè)問(wèn)題就是,我們把“眼睛”放在什么地方?如果是一個(gè)酷酷的動(dòng)態(tài)攝像機(jī),我們又該如何處理,最重要的是,VIEW變換是一個(gè)什么東西?

              從另外一個(gè)角度說(shuō),VIEW變換是,一個(gè)Translate和一個(gè)Rotate

              在GLU中有一個(gè)極其好用的函數(shù)叫做gluLookAt,MSDN中它的定義是,

            “The gluLookAt function creates a viewing matrix derived from an eye point, a reference point indicating the center of the scene, and an up vector. The matrix maps the reference point to the negative z-axis and the eye point to the origin, so that when you use a typical projection matrix, the center of the scene maps to the center of the viewport. Similarly, the direction described by the up vector projected onto the viewing plane is mapped to the positive y-axis so that it points upward in the viewport. The up vector must not be parallel to the line of sight from the eye to the reference point.”

              gluLookAt函數(shù)使用3個(gè)參數(shù),Eye Position、Reference Position、Up Vector去構(gòu)造了一個(gè)View矩陣。它是如何計(jì)算的呢?

              假如我們有了上面三個(gè)量,分別叫做e、r、u。我們先獲得視線的方向d=normalize(r-e),我們用D與U的叉乘得到一個(gè)量c=cross(d,u),最后重新計(jì)算一下u'=cross(c,d),然后我們就得到了一個(gè)矩陣o,讓它與一個(gè)T矩陣相乘,就得到了與gluLookAt生成的矩陣相同的VIEW矩陣,過(guò)程如下,

            Eqn4
             

             

              詳情可以去找那本《OpenGL® Distilled》,這本書(shū)的其他部分都不怎么樣,就這個(gè)地方很有價(jià)值^_^

              讓我們開(kāi)始使用剛剛學(xué)到的知識(shí)了。如何讀取3DS模型是一個(gè)老生常談的故事,幾乎所有新手都會(huì)問(wèn)這個(gè)問(wèn)題。首先,3DS是最經(jīng)典的模型,AutoCAD等工具都支持,但是它的缺點(diǎn)也很明顯,非文本而是二進(jìn)制,處理比較麻煩。下面我將展示如何使用lib3ds去讀取一個(gè)模型文件中的所有的頂點(diǎn)、向量、紋理坐標(biāo)、材質(zhì)、貼圖,并有效的組合在一起渲染。

              首先我們定義一些基本的結(jié)構(gòu),比如這個(gè),

            typedef struct _RenderModel
            {
                ushort* m_TriangleIndex;
                uint m_Triangles;
                float* m_VertexPtr;
                uint m_Indexs;
                float* m_NormalPtr;
                uint m_Normals;

                float* m_TexCoordPtr;
                uint m_Texels;
                float m_BoundBoxMin[3];
                float m_BoundBoxMax[3];

                float m_XformMatrix[16];

                std::string m_ModelName;
                std::string m_MatName;
            }RenderModel;

            typedef struct  _RenderMesh
            {
                uint m_Models;
                RenderModel* m_ModelPtr;
            }RenderMesh;

            typedef struct _Phong
            {
                float emission[4];
                float ambient[4];
                float diffuse[4];
                float specular[4];
                float shininess;

            }Phong;

            typedef struct _Camera
            {
                float m_Position[3];
                float m_Direction[3];
                float m_Center[3];
                float m_Up[3];
                float m_Fovy;
                float m_Near;
                float m_Far;
            }Camera;

            static map<string, Phong> MaterialMap;
            typedef pair<string,Phong> materialpair;
            static vector<RenderModel> Models;

              但是要知道,有不少成員是根本用不到的。如何處理呢?這個(gè)函數(shù),

            static void Init3DSModel(const char* filename)
            {
                Lib3dsFile* file = 0;
                file = lib3ds_file_load(filename);
                if ( !file ){
                    printf("Error On Open File!\n");
                    system("PAUSE");
                    exit(-1);
                }
                Lib3dsMesh* mesh = 0;
                Lib3dsMaterial* mat = 0;
                typedef vector<float> scalarvec;
                typedef vector<uint> uintvec;
                vector<scalarvec> TriSoups;
                printf("INFO : Processing the 3ds file...\n");
                for( mesh=file->meshes; mesh!=0; mesh=mesh->next ){
                    scalarvec Object[3];
                    uintvec IDVector;
                    RenderModel Model;
                    Lib3dsVector *  normalL = new  Lib3dsVector[3*sizeof(Lib3dsVector)*mesh->faces];
                    lib3ds_mesh_calculate_normals(mesh,normalL);

                    for( Lib3dsDword i = 0; i < mesh->faces; i++ ){

            //取得索引
                        Lib3dsWord _0= mesh->faceL[i].points[0];
                        Lib3dsWord _1= mesh->faceL[i].points[1];
                        Lib3dsWord _2= mesh->faceL[i].points[2];

            //取得頂點(diǎn)

                        vec3 V0(mesh->pointL[_0].pos[0],mesh->pointL[_0].pos[1],mesh->pointL[_0].pos[2]);
                        vec3 V1(mesh->pointL[_1].pos[0],mesh->pointL[_1].pos[1],mesh->pointL[_1].pos[2]);
                        vec3 V2(mesh->pointL[_2].pos[0],mesh->pointL[_2].pos[1],mesh->pointL[_2].pos[2]);

            //取得法向量

                        Object[1].push_back( normalL[3*i+0][0] );Object[1].push_back( normalL[3*i+0][1] );Object[1].push_back( normalL[3*i+0][2] );
                        Object[1].push_back( normalL[3*i+1][0] );Object[1].push_back( normalL[3*i+1][1] );Object[1].push_back( normalL[3*i+1][2] );
                        Object[1].push_back( normalL[3*i+2][0] );Object[1].push_back( normalL[3*i+2][1] );Object[1].push_back( normalL[3*i+2][2] );

            //取得紋理坐標(biāo)
                        Object[2].push_back( mesh->texelL[_0][0] );Object[2].push_back( mesh->texelL[_0][1] );
                        Object[2].push_back( mesh->texelL[_1][0] );Object[2].push_back( mesh->texelL[_1][1] );
                        Object[2].push_back( mesh->texelL[_2][0] );Object[2].push_back( mesh->texelL[_2][1] );

            //儲(chǔ)存起來(lái)

                        Object[0].push_back( V0.x );Object[0].push_back( V0.y );Object[0].push_back( V0.z );
                        Object[0].push_back( V1.x );Object[0].push_back( V1.y );Object[0].push_back( V1.z );
                        Object[0].push_back( V2.x );Object[0].push_back( V2.y );Object[0].push_back( V2.z );
                    }

            //分配內(nèi)存

                    delete [] normalL;
                    Model.m_Triangles = mesh->faces*3;
                    Model.m_VertexPtr = new float[Object[0].size()];
                    Model.m_Normals = mesh->faces*3;
                    Model.m_NormalPtr = new float[Object[1].size()];
                    Model.m_Texels = mesh->texels*3;
                    Model.m_TexCoordPtr = new float[Object[2].size()];

            //拷貝進(jìn)內(nèi)存
                    copy(Object[0].begin(),Object[0].end(),Model.m_VertexPtr);
                    copy(Object[1].begin(),Object[1].end(),Model.m_NormalPtr);
                    copy(Object[2].begin(),Object[2].end(),Model.m_TexCoordPtr);
                    Model.m_MatName = mesh->faceL[0].material;
                    Model.m_ModelName = mesh->name;
                    Models.push_back(Model);
                }

            //這個(gè)函數(shù)一定要注意,這是個(gè)小小的修補(bǔ)程序,比如用lib3ds計(jì)算得到的Bottom的向量是向下的,我們需要翻轉(zhuǎn)一下
                for(vector<RenderModel>::iterator itr = Models.begin(); itr !=Models.end(); itr++){
                    if( itr->m_ModelName == string("Bottom01") ){
                        for( unsigned int i =2; i<itr->m_Normals*3;i+=3 )
                            itr->m_NormalPtr[i] *= -1.0f;
                    }
                }
                printf("INFO : Processing the material...\n");
                for( mat = file->materials; mat != 0; mat=mat->next ){
                    string _MatName( mat->name );
                    Phong _Mat;
                    //載入來(lái)自3ds模型文件的材質(zhì)
                    for(int i = 0; i<4; i++){
                        _Mat.ambient[i] = mat->ambient[i];
                        _Mat.diffuse[i] = mat->diffuse[i];
                        _Mat.specular[i] = mat->specular[i];
                    }
                    _Mat.shininess = mat->shininess;
                    MaterialMap.insert( materialpair( _MatName , _Mat  ) );
                }
            }

              那么我們現(xiàn)在有了Models與MaterialMap這兩個(gè)容器,如何渲染呢?

            glEnableClientState(GL_VERTEX_ARRAY);
            glEnableClientState(GL_NORMAL_ARRAY);
            glEnableClientState(GL_TEXTURE_COORD_ARRAY);
            for( vector<RenderModel>::iterator i=Models.begin(); i!=Models.end(); i++ ){
                map<string, Phong>::iterator matItr = MaterialMap.find( i->m_MatName );
                glMaterialfv(GL_FRONT_AND_BACK,GL_AMBIENT,&matItr->second.ambient[0]);
                glMaterialfv(GL_FRONT_AND_BACK,GL_DIFFUSE,&matItr->second.ambient[0]);
                glMaterialfv(GL_FRONT_AND_BACK,GL_SPECULAR,&matItr->second.specular[0]);
                glMaterialf(GL_FRONT_AND_BACK,GL_SHININESS,matItr->second.shininess);
                glVertexPointer(3,GL_FLOAT,0,i->m_VertexPtr);
                glNormalPointer(GL_FLOAT,0,i->m_NormalPtr);
                glTexCoordPointer(2,GL_FLOAT,0,i->m_TexCoordPtr);
                glDrawArrays(GL_TRIANGLES,0,i->m_Triangles);
            }
            glDisableClientState(GL_TEXTURE_COORD_ARRAY);
            glDisableClientState(GL_NORMAL_ARRAY);
            glDisableClientState(GL_VERTEX_ARRAY);

               我相信些代碼是很簡(jiǎn)單的,通過(guò)mesh儲(chǔ)存到的material名稱,載入GL,然后渲染就可以了。思路就是這么明顯。

              不知道你有沒(méi)有注意,在glDrawArrays時(shí),我沒(méi)有使用任何的glTranslatef、glRotatef等函數(shù),原因我已經(jīng)解釋過(guò)了,我將整個(gè)場(chǎng)景當(dāng)作了整個(gè)世界,那么我就沒(méi)有必要再多此一舉。

              那么此時(shí)我的Reshape函數(shù)呢?

            static void Reshape(int w,int h)
            {
                glMatrixMode(GL_MODELVIEW);
                glLoadIdentity();
                gluLookAt(Eye.m_Position[0],Eye.m_Position[1],Eye.m_Position[2],Eye.m_Center[0],Eye.m_Center[1],Eye.m_Center[2],Eye.m_Up[0],Eye.m_Up[1],Eye.m_Up[2]);
                glMatrixMode(GL_PROJECTION);
                glLoadIdentity();
                gluPerspective(46,(double)w/(double)h,0.5,100);

                glViewport(0,0,w,h);

                glMatrixMode(GL_MODELVIEW);
            }

              是不是也極其簡(jiǎn)單呢?那個(gè)Eye我是直接從lib3ds讀取得到的CAMERA的位置載入的,具體的實(shí)現(xiàn)很簡(jiǎn)單。同樣,那個(gè)Spot的位置與方向也是這樣獲得的。

            五、Im Stillen lass ich ab von Dir

              到現(xiàn)在我們說(shuō)的都是MV變換,沒(méi)有說(shuō)到透視變換Projection Transform。

              對(duì)比MV變換,PT就顯得簡(jiǎn)單了許多,它的根本作用是,將ES中的所有點(diǎn)“盡量”都轉(zhuǎn)換到X與Y方向上都是[-1,1]的這個(gè)屏幕區(qū)間里面去。那我們應(yīng)該如何理解呢?

              打開(kāi)你的Shader Designer,渲染busto人頭。在Fragment Shader中輸入,

            void main()
            {
                gl_FragColor =     gl_TexCoord[0];
            }

              在Vertex Shader中輸入,

            void main()
            {
                gl_TexCoord[0] = gl_MultiTexCoord0;
                gl_Position = vec4(gl_TexCoord[0].st,0,1.0);
            }

              這里估計(jì)很多朋友就迷糊了,這是什么意思啊,為什么gl_Position不是寫(xiě)入ftransform()或者是gl_ModelViewProjectionMatrix*gl_Vertex,而是怪模怪樣的給它賦值為UV坐標(biāo)?

              先不提這叫做什么,反正你將看到這個(gè)樣子,

            R2TSWrg

              我們?cè)侔裋ertex Shader改成

            void main()
            {
                gl_TexCoord[0] = gl_MultiTexCoord0;
                gl_Position = vec4(gl_TexCoord[0].st*2.0-1.0,0,1.0);
            }

              這個(gè)時(shí)候就塞滿了整個(gè)屏幕,

            R2TS

              其實(shí)這就是Render To Texture Space,NVIDIA在用Cheating的方式處理SSS的時(shí)候做的那個(gè)“渲染到紋理空間”。

              我們當(dāng)然知道,UV空間是[0,1],我如果給它乘以2減去1就是變換到[-1,1]的空間中去。但是在這里我們可以明白一個(gè)道理,頂點(diǎn)經(jīng)過(guò)MVP處理后,要么被裁減掉,要么將得到在[-1,1]中間的一對(duì)數(shù)值。

              其實(shí)當(dāng)我們明白了這個(gè)道理后,就可以完全的發(fā)揮GPGPU的功能,因?yàn)槲覀円呀?jīng)明白了數(shù)值的Framebuffer的準(zhǔn)確寫(xiě)入位置。這樣就可以隨心所欲的在Framebuffer中寫(xiě)入數(shù)據(jù),讀取想要的數(shù)據(jù),甚至做遍歷。

              我們可以用glFrustum或者是gluPerspective函數(shù)獲得一個(gè)透視矩陣,gluPerspective更常用一些。gluPerspective定義的了一個(gè)***模型,它有fovy、aspect、zNear、zFar組成。這4個(gè)參數(shù)定義了一個(gè)平截頭體,這個(gè)平截頭體就是攝像機(jī),在這個(gè)Volumn中的東西才是可見(jiàn)的,其他都是不可見(jiàn)的。示意圖如下,

            Camera

              但是我們又知道,GL其實(shí)是把人眼放在了(0,0,0,0)位置,每一個(gè)象素都遵循(0,0,-1,0)這個(gè)方向。其實(shí)這就是(x,y,z,w)的最后一個(gè)分量的作用,當(dāng)我們寫(xiě)入gl_Position后,硬件會(huì)自動(dòng)的除以w分量以獲得裁減坐標(biāo)系,然后再使用Viewport參數(shù)將這些玩意與屏幕象素對(duì)應(yīng)在一起。

              但是我們一定一定要知道:無(wú)論是OpenGL、Direct3D,甚至是RenderMan、mental ray都有一些這樣的問(wèn)題,那就是,它們其實(shí)都是***模型。RenderMan和mental ray有些特殊,它們即有光柵化又有光線跟蹤模塊,所以處理攝像機(jī)就比較靈活,可是對(duì)于GL和DX來(lái)說(shuō),模擬一些攝像機(jī)效果如DOF就只能用Trick。

              再來(lái)一個(gè)紋理投射Projection Texture。

              紋理投射是個(gè)很經(jīng)常的問(wèn)題,比如在做Shadow Mapping的時(shí)候,還有就是做光照的時(shí)候,還有比如模擬一個(gè)電影放映機(jī)的過(guò)程,都是紋理投射。它的根本意義是:“假想我們從投射物體比如光源去看場(chǎng)景,我們希望得到世界里的每個(gè)頂點(diǎn)在那個(gè)虛擬攝像機(jī)屏幕上的XY位置”。寫(xiě)成連續(xù)矩陣的形式就是,

            glMatrixMode(GL_TEXTURE);
            glLoadIdentity();
            glTranslatef(.5f, .5f, .5f);
            glScalef(.5f, .5f, .5f);
            gluPerspective(/*LIght Shape*/);
            gluLookAt(/*Light Position,direction,up vector*/)

              這個(gè)時(shí)候,GL的紋理矩陣其實(shí)就是我們想要的那個(gè)矩陣:它將世界頂點(diǎn)變換到光源攝像機(jī)空間去,在Shader中我們可以直接使用texture2DProj去做紋理采樣。比如這個(gè)聚光燈效果,

            NoInDir

            六、結(jié)束

              在這篇文章中,我把GL中的矩陣處理流程,手動(dòng)生成矩陣的方式,結(jié)合了真實(shí)的場(chǎng)景教了大家如何去認(rèn)識(shí)矩陣變換問(wèn)題,后面還說(shuō)了一些與矩陣變換相關(guān)的應(yīng)用,如果你覺(jué)得有錯(cuò)可以聯(lián)系我謝謝,如果你覺(jué)得本文對(duì)你有幫助、幫助大多數(shù)新手進(jìn)步、分享知識(shí)是我的責(zé)任。轉(zhuǎn)載時(shí)請(qǐng)附上我的聯(lián)系方式,謝謝。

            周波 Bo Schwarzstein

            Mailbox 242,Nanjing Forestry University,Jiangsu,China

            jedimaster.cnblogs.com

            zhoubo22 'at' hotmail.com

            posted on 2009-01-06 17:17 doing5552 閱讀(851) 評(píng)論(0)  編輯 收藏 引用
            久久嫩草影院免费看夜色| 亚洲AV日韩精品久久久久| 久久这里只有精品视频99| 久久国产色av免费看| 久久免费精品视频| 久久天天躁夜夜躁狠狠| 久久精品成人国产午夜| 久久午夜夜伦鲁鲁片免费无码影视| 久久久久免费看成人影片| 国内精品伊人久久久久网站| 亚洲中文字幕无码久久2020| 久久精品成人免费国产片小草| 久久久女人与动物群交毛片| 午夜肉伦伦影院久久精品免费看国产一区二区三区 | 欧美伊人久久大香线蕉综合| 久久免费小视频| 亚洲va久久久噜噜噜久久| 色综合合久久天天给综看| 2020最新久久久视精品爱| 欧美精品久久久久久久自慰| 久久久久久国产a免费观看黄色大片| 99热成人精品免费久久| 久久精品无码专区免费东京热| 日韩精品久久久久久久电影| 久久久久黑人强伦姧人妻| 日本精品久久久中文字幕| 99久久精品费精品国产一区二区| 精品久久久久久国产| 国产精品久久新婚兰兰| 亚洲а∨天堂久久精品9966| 久久中文字幕视频、最近更新| 国产高潮久久免费观看| 伊人色综合久久天天| 久久国产亚洲精品麻豆| 狠狠色噜噜狠狠狠狠狠色综合久久| 国产毛片欧美毛片久久久| 老男人久久青草av高清| 久久91精品国产91久| 亚洲狠狠婷婷综合久久久久| 久久亚洲欧美国产精品| 69国产成人综合久久精品|