• <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>

            小星星的天空

            O(∩_∩)O 小月亮的fans ^_^

              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              16 隨筆 :: 0 文章 :: 61 評(píng)論 :: 0 Trackbacks

            2009年10月20日 #

            多態(tài)(Polymorphism)是面向?qū)ο蟮暮诵母拍睿疚囊訡++為例,討論多態(tài)的具體實(shí)現(xiàn)。C++中多態(tài)可以分為基于繼承和虛函數(shù)的動(dòng)態(tài)多態(tài)以及基于模板的靜態(tài)多態(tài),如果沒(méi)有特別指明,本文中出現(xiàn)的多態(tài)都是指前者,也就是基于繼承和虛函數(shù)的動(dòng)態(tài)多態(tài)。至于什么是多態(tài),在面向?qū)ο笾腥绾问褂枚鄳B(tài),使用多態(tài)的好處等等問(wèn)題,如果大家感興趣的話,可以找本面向?qū)ο蟮臅?shū)來(lái)看看。
                為了方便說(shuō)明,下面舉一個(gè)簡(jiǎn)單的使用多態(tài)的例子(From [1] ):

            class Shape
            {
            protected:
              int m_x;    // X coordinate
              int m_y;  // Y coordinate
            public:
              // Pure virtual function for drawing
              virtual void Draw() = 0;  

              // A regular virtual function
              virtual void MoveTo(int newX, int newY);

             // Regular method, not overridable.
              void Erase();

              // Constructor for Shape
              Shape(int x, int y); 

             // Virtual destructor for Shape
              virtual ~Shape();
            };
            // Circle class declaration
            class Circle : public Shape
            {
            private:
               int m_radius;    // Radius of the circle
            public:
               // Override to draw a circle
               virtual void Draw();    

               // Constructor for Circle
               Circle(int x, int y, int radius);

              // Destructor for Circle
               virtual ~Circle();
            };
            // Shape constructor implementation
            Shape::Shape(int x, int y)
            {
               m_x = x;
               m_y = y;
            }
            // Shape destructor implementation
            Shape::~Shape()
            {
            //...
            }
             // Circle constructor implementation
            Circle::Circle(int x, int y, int radius) : Shape (x, y)
            {
               m_radius = radius;
            }

            // Circle destructor implementation
            Circle::~Circle()
            {
            //...
            }

            // Circle override of the pure virtual Draw method.
            void Circle::Draw()
            {
               glib_draw_circle(m_x, m_y, m_radius);
            }

            main()
            {
              // Define a circle with a center at (50,100) and a radius of 25
              Shape *pShape = new Circle(50, 100, 25);

              // Define a circle with a center at (5,5) and a radius of 2
              Circle aCircle(5,5, 2);

              // Various operations on a Circle via a Shape pointer
              //Polymorphism
              pShape->Draw();
              pShape->MoveTo(100, 100);

              pShape->Erase();
              delete pShape;

             // Invoking the Draw method directly
              aCircle.Draw();
            }   

                 例子中使用到多態(tài)的代碼以黑體標(biāo)出了,它們一個(gè)很明顯的特征就是通過(guò)一個(gè)基類的指針(或者引用)來(lái)調(diào)用不同子類的方法。
                 那么,現(xiàn)在的問(wèn)題是,這個(gè)功能是怎樣實(shí)現(xiàn)的呢?我們可以先來(lái)大概猜測(cè)一下:對(duì)于一般的方法調(diào)用,到了匯編代碼這一層次的時(shí)候,一般都是使用 Call funcaddr 這樣的指令進(jìn)行調(diào)用,其中funcaddr是要調(diào)用函數(shù)的地址。按理來(lái)說(shuō),當(dāng)我使用指針pShape來(lái)調(diào)用Draw的時(shí)候,編譯器應(yīng)該將Shape::Draw的地址賦給funcaddr,然后Call 指令就可以直接調(diào)用Shape::Draw了,這就跟用pShape來(lái)調(diào)用Shape::Erase一樣。但是,運(yùn)行結(jié)果卻告訴我們,編譯器賦給funcaddr的值卻是Circle::Drawde的值。這就說(shuō)明,編譯器在對(duì)待Draw方法和Erase方法時(shí)使用了雙重標(biāo)準(zhǔn)。那么究竟是誰(shuí)有這么大的法力,使編譯器這個(gè)鐵面無(wú)私的判官都要另眼相看呢?virtual!!
                
            Clever!!正是virtual這個(gè)關(guān)鍵字一手導(dǎo)演了這一出“乾坤大挪移”的好戲。說(shuō)道這里,我們先要明確兩個(gè)概念:靜態(tài)綁定和動(dòng)態(tài)綁定。
                1、靜態(tài)綁定(static bingding),也叫早期綁定,簡(jiǎn)單來(lái)說(shuō)就是編譯器在編譯期間就明確知道所要調(diào)用的方法,并將該方法的地址賦給了Call指令的funcaddr。因此,運(yùn)行期間直接使用Call指令就可調(diào)用到相應(yīng)的方法。
                2、動(dòng)態(tài)綁定(dynamic binding),也叫晚期綁定,與靜態(tài)綁定不同,在編譯期間,編譯器并不能明確知道究竟要調(diào)用的是哪一個(gè)方法,而這,要知道運(yùn)行期間使用的具體是哪個(gè)對(duì)象才能決定。
                好了,有了這兩個(gè)概念以后,我們就可以說(shuō),virtual的作用就是告訴編譯器:我要進(jìn)行動(dòng)態(tài)綁定!編譯器當(dāng)然會(huì)尊重你的意見(jiàn),而且為了完成你這個(gè)要求,編譯器還要做很多的事情:編譯器自動(dòng)在聲明了virtual方法的類中插入一個(gè)指針vptr和一個(gè)數(shù)據(jù)結(jié)構(gòu)VTable(vptr用以指向VTable;VTable是一個(gè)指針數(shù)組,里面存放著函數(shù)的地址),并保證二者遵守下面的規(guī)則:
                1、VTable中只能存放聲明為virtual的方法,其它方法不能存放在里面。在上面的例子中,Shape的VTable中就只有Draw,MoveTo和~Shape。方法Erase的地址并不能存放在VTable中。此外,如果方法是純虛函數(shù),如 Draw,那么同樣要在VTable中保留相應(yīng)的位置,但是由于純虛函數(shù)沒(méi)有函數(shù)體,因此該位置中并不存放Draw的地址,而是可以選擇存放一個(gè)出錯(cuò)處理的函數(shù)的地址,當(dāng)該位置被意外調(diào)用時(shí),可以用出錯(cuò)函數(shù)進(jìn)行相應(yīng)的處理。
                2、派生類的VTalbe中記錄的從基類中繼承下來(lái)的虛函數(shù)地址的索引號(hào)必須跟該虛函數(shù)在基類VTable中的索引號(hào)保持一致。如在上例中,如果在Shape的VTalbe中,Draw為 1 號(hào), MoveTo 2 號(hào),~Shape為 3 號(hào),那么,不管這些方法在Circle中是按照什么順序定義的,Circle的VTable中都必須保證Draw為 1 號(hào),MoveTo為 2號(hào)。至于 3號(hào),這里是~Circle。為什么不是~Shape啊?嘿嘿,忘啦,析構(gòu)函數(shù)不會(huì)繼承的。
                3、vptr是由編譯器自動(dòng)插入生成的,因此編譯器必須負(fù)責(zé)為其進(jìn)行初始化。初始化的時(shí)間選在對(duì)象創(chuàng)建時(shí),而地點(diǎn)就在構(gòu)造函數(shù)中。因此,編譯器必須保證每個(gè)類至少有一個(gè)構(gòu)造函數(shù),若沒(méi)有,自動(dòng)為其生成一個(gè)默認(rèn)構(gòu)造函數(shù)。
                 4、vptr通常放在對(duì)象的起始處,也就是Addr(obj) == Addr(obj.vptr)。
                你看,天下果然沒(méi)有免費(fèi)的午餐,為了實(shí)現(xiàn)動(dòng)態(tài)綁定,編譯器要為我們默默干了這么多的臟話累活。如果你想體驗(yàn)一下編譯器的辛勞,那么可以嘗試用C語(yǔ)言模擬一下上面的行為,【1】中就有這么一個(gè)例子。好了,現(xiàn)在萬(wàn)事具備,只欠東風(fēng)了。編譯,連接,載入,GO!當(dāng)程序執(zhí)行到 pShape->Draw()的時(shí)候,上面的設(shè)施也開(kāi)始起作用了。。
                前面已經(jīng)提到,晚期綁定時(shí)之所以不能確定調(diào)用哪個(gè)函數(shù),是因?yàn)榫唧w的對(duì)象不確定。好了,當(dāng)運(yùn)行到pShape->Draw()時(shí),對(duì)象出來(lái)了,它由pShape指針標(biāo)出。我們找到這個(gè)對(duì)象后,就可以找到它里面的vptr(在對(duì)象的起始處),有了vptr后,我們就找到了VTable,調(diào)用的函數(shù)就在眼前了。。等等,VTable中方法那么多,我究竟使用哪個(gè)呢?不用著急,編譯器早已為我們做好了記錄:編譯器在創(chuàng)建VTable時(shí),已經(jīng)為每個(gè)virtual函數(shù)安排好了座次,并且把這個(gè)索引號(hào)記錄了下來(lái)。因此,當(dāng)編譯器解析到pShape->Draw()的時(shí)候,它已經(jīng)悄悄的將函數(shù)的名字用索引號(hào)來(lái)代替了。這時(shí)候,我們通過(guò)這個(gè)索引號(hào)就可以在VTable中得到一個(gè)函數(shù)地址,Call it!
                在這里,我們就體會(huì)到為什么會(huì)有第二條規(guī)定了,通常,我們都是用基類的指針來(lái)引用派生類的對(duì)象,但是不管具體對(duì)象是哪個(gè)派生類的,我們都可以使用相同的索引號(hào)來(lái)取得對(duì)應(yīng)的函數(shù)實(shí)現(xiàn)。
                 現(xiàn)實(shí)中有一個(gè)例子其實(shí)跟這個(gè)蠻像的:報(bào)警電話有110,119,120(VTable中不同的方法)。不同地方的人撥打不同的號(hào)碼所產(chǎn)生的結(jié)果都是不一樣的。譬如,在三環(huán)外的一個(gè)人(具體對(duì)象)跟一環(huán)內(nèi)的一個(gè)人(另外一個(gè)具體對(duì)象)打119,最后調(diào)用的消防隊(duì)肯定是不一樣的,這就是多態(tài)了。這是怎么實(shí)現(xiàn)的呢,每個(gè)人都知道一個(gè)報(bào)警中心(VTable,里面有三個(gè)方法 110,119,120)。如果三環(huán)外的一個(gè)人需要火警搶險(xiǎn)(一個(gè)具體對(duì)象)時(shí),它就撥打119,但是他肯定不知道最后是哪一個(gè)消防隊(duì)會(huì)出現(xiàn)的。這得有報(bào)警中心來(lái)決定,報(bào)警中心通過(guò)這個(gè)具體對(duì)象(例子中就是具體位置了)以及他說(shuō)撥打的電話號(hào)碼(可以理解成索引號(hào)),報(bào)警中心可以確定應(yīng)該調(diào)度哪一個(gè)消防隊(duì)進(jìn)行搶險(xiǎn)(不同的動(dòng)作)。
                 這樣,通過(guò)vptr和VTable的幫助,我們就實(shí)現(xiàn)了C++的動(dòng)態(tài)綁定。當(dāng)然,這僅僅是單繼承時(shí)的情況,多重繼承的處理要相對(duì)復(fù)雜一點(diǎn),下面簡(jiǎn)要說(shuō)一下最簡(jiǎn)單的多重繼承的情況,至于虛繼承的情況,有興趣的朋友可以看看 Lippman的《Inside the C++ Object Model》,這里暫時(shí)就不展開(kāi)了。(主要是自己還沒(méi)搞清楚,況且現(xiàn)在多重繼承都不怎么使用了,虛繼承應(yīng)用的機(jī)會(huì)就更少了)
                 首先,我要先說(shuō)一下多重繼承下對(duì)象的內(nèi)存布局,也就是說(shuō)該對(duì)象是如何存放本身的數(shù)據(jù)的。

            class Cute
            {
            public:
             int i;
             virtual void cute(){ cout<<"Cute cute"<<endl; }
            };
            class Pet
            {
            public:
               int j;
               virtual void say(){ cout<<"Pet say"<<endl;  }
            };
            class Dog : public Cute,public Pet
            {
            public:
             int z;
             void cute(){ cout<<"Dog cute"<<endl; }
             void say(){ cout<<"Dog say"<<endl;  }
            };

                在上面這個(gè)例子中,一個(gè)Dog對(duì)象在內(nèi)存中的布局如下所示:                    

            Dog

            Vptr1

            Cute::i

            Vptr2

            Pet::j

            Dog::z


                 也就是說(shuō),在Dog對(duì)象中,會(huì)存在兩個(gè)vptr,每一個(gè)跟所繼承的父類相對(duì)應(yīng)。如果我們要想實(shí)現(xiàn)多態(tài),就必須在對(duì)象中準(zhǔn)確地找到相應(yīng)的vptr,以調(diào)用不同的方法。但是,如果根據(jù)單繼承時(shí)的邏輯,也就是vptr放在指針指向位置的起始處,那么,要在多重繼承情況下實(shí)現(xiàn),我們必須保證在將一個(gè)派生類的指針隱式或者顯式地轉(zhuǎn)換成一個(gè)父類的指針時(shí),得到的結(jié)果指向相應(yīng)派生類數(shù)據(jù)在Dog對(duì)象中的起始位置。幸好,這工作編譯器已經(jīng)幫我們完成了。上面的例子中,如果Dog向上轉(zhuǎn)換成Pet的話,編譯器會(huì)自動(dòng)計(jì)算Pet數(shù)據(jù)在Dog對(duì)象中的偏移量,該偏移量加上Dog對(duì)象的起始位置,就是Pet數(shù)據(jù)的實(shí)際地址了。

            int main()
            {
             Dog* d = new Dog();
             cout<<"Dog object addr : "<<d<<endl;
             Cute* c = d;
             cout<<"Cute type addr : "<<c<<endl;
             Pet* p = d;
             cout<<"Pet type addr : "<<p<<endl;
             delete d;
            }
            output:
            Dog object addr : 0x3d24b0
            Cute type addr : 0x3d24b0
            Pet type addr : 0x3d24b8   // 正好指向Dog對(duì)象的vptr2處,也就是Pet的數(shù)據(jù)

                  好了,既然編譯器幫我們自動(dòng)完成了不同父類的地址轉(zhuǎn)換,我們調(diào)用虛函數(shù)的過(guò)程也就跟單繼承統(tǒng)一起來(lái)了:通過(guò)具體對(duì)象,找到vptr(通常指針的起始位置,因此Cute找到的是vptr1,而Pet找到的是vptr2),通過(guò)vptr,我們找到VTable,然后根據(jù)編譯時(shí)得到的VTable索引號(hào),我們?nèi)〉孟鄳?yīng)的函數(shù)地址,接著就可以馬上調(diào)用了。

                  在這里,順便也提一下兩個(gè)特殊的方法在多態(tài)中的特別之處吧:第一個(gè)是構(gòu)造函數(shù),在構(gòu)造函數(shù)中調(diào)用虛函數(shù)是不會(huì)有多態(tài)行為的,例子如下:

            class Pet
            {
            public:
               Pet(){ sayHello(); }
               void say(){ sayHello(); }

               virtual void sayHello()
               {
                 cout<<"Pet sayHello"<<endl;
               }
              
            };
            class Dog : public Pet
            {
            public:
               Dog(){};
               void sayHello()
               {
                 cout<<"Dog sayHello"<<endl;
               }
            };
            int main()
            {
             Pet* p = new Dog();
             p->sayHello();
             delete p;
            }
            output:
            Pet sayHello //直接調(diào)用的是Pet的sayHello()
            Dog sayHello //多態(tài)

                 第二個(gè)就是析構(gòu)函數(shù),使用多態(tài)的時(shí)候,我們經(jīng)常使用基類的指針來(lái)引用派生類的對(duì)象,如果是動(dòng)態(tài)創(chuàng)建的,對(duì)象使用完后,我們使用delete來(lái)釋放對(duì)象。但是,如果我們不注意的話,會(huì)有意想不到的情況發(fā)生。

            class Pet
            {
            public:
               ~Pet(){ cout<<"Pet destructor"<<endl;  }
              //virtual ~Pet(){ cout<<"Pet virtual destructor"<<endl;  }
            };
            class Dog : public Pet
            {
            public:
               ~Dog(){ cout<<"Dog destructor"<<endl;};
               //virtual ~Dog(){ cout<<"Dog virtual destructor"<<endl;  }
            };
            int main()
            {
             Pet* p = new Dog();
             delete p;
            }
            output:
            Pet destructor  //糟了,Dog的析構(gòu)函數(shù)沒(méi)有調(diào)用,memory leak!

            如果我們將析構(gòu)函數(shù)改成virtual以后,結(jié)果如下
            Dog virtual destructor
            Pet virtual destructor   // That's OK!

                所以,如果一個(gè)類設(shè)計(jì)用來(lái)被繼承的話,那么它的析構(gòu)函數(shù)應(yīng)該被聲明為virtual的。

            posted @ 2009-10-20 21:36 Little Star 閱讀(375) | 評(píng)論 (0)編輯 收藏

            通常在C的編程中,我們經(jīng)常使用memset函數(shù)將一塊連續(xù)的內(nèi)存區(qū)域清零或設(shè)置為其它指定的值,最近在移植一段java代碼到C++的時(shí)候,不當(dāng)使用memset函數(shù)花費(fèi)了我?guī)讉€(gè)小時(shí)的調(diào)試時(shí)間。對(duì)于虛函數(shù)的底層機(jī)制很多資料都有較詳細(xì)闡述,但對(duì)我個(gè)人而言,這次的調(diào)試讓我感觸頗深。

            先來(lái)看一段代碼,在繼承的類Advance之中,有很多屬性字段,我希望將其清成0或NULL,于是在構(gòu)造函數(shù)中我通過(guò)memset將當(dāng)前類的所有屬性置0。

            class Base{

            public:

            virtual void kickoff() = 0;

            };
            class Advance:public Base{

            public:

            Advance(){

            memset(this, 0, sizeof(Advance));

            }

            void kickoff(){

            count++;

            //... do something else;

            }

            private:

            int attr1, attr2;

            char* label;

            int count;

            //... other attributes, they should be initiated to 0 or NULL at beginning.

            };

            int _tmain(int argc, _TCHAR* argv[])

            {
            Base* ptr = new Advance();
            ptr->kickoff();
            return 0;
            }

            這樣看似能正常運(yùn)行,但運(yùn)行程序時(shí),你會(huì)發(fā)現(xiàn)類似于下面的錯(cuò)誤:

            TestVirtual.exe 中的 0x00415390 處未處理的異常: 0xC0000005: 讀取位置 0x00000000 時(shí)發(fā)生訪問(wèn)沖突

            同時(shí)斷點(diǎn)停留在ptr->kickoff()處,從錯(cuò)誤提示我們可以得知無(wú)法調(diào)用kickoff方法,這個(gè)方法的指針沒(méi)有被正確初始化,但為什么呢?

            指出問(wèn)題之前,先看看這段文獻(xiàn)上的關(guān)于虛函數(shù)機(jī)制的說(shuō)明:

            函數(shù)賴以生存的底層機(jī)制:vptr + vtable。虛函數(shù)的運(yùn)行時(shí)實(shí)現(xiàn)采用了VPTR/VTBL的形式,這項(xiàng)技術(shù)的基礎(chǔ):
            ①編譯器在后臺(tái)為每個(gè)包含虛函數(shù)的類產(chǎn)生一個(gè)靜態(tài)函數(shù)指針數(shù)組(虛函數(shù)表),在這個(gè)類或者它的基類中定義的每一個(gè)虛函數(shù)都有一個(gè)相應(yīng)的函數(shù)指針。
            ②每個(gè)包含虛函數(shù)的類的每一個(gè)實(shí)例包含一個(gè)不可見(jiàn)的數(shù)據(jù)成員vptr(虛函數(shù)指針),這個(gè)指針被構(gòu)造函數(shù)自動(dòng)初始化,指向類的vtbl(虛函數(shù)表)
            ③當(dāng)客戶調(diào)用虛函數(shù)的時(shí)候,編譯器產(chǎn)生代碼反指向到vptr,索引到vtbl中,然后在指定的位置上找到函數(shù)指針,并發(fā)出調(diào)用。

            這里的問(wèn)題,就出在

            memset(this, 0, sizeof(Advance));

            上面,虛函數(shù)指針應(yīng)該在進(jìn)入構(gòu)造函數(shù)賦值體之前自動(dòng)初始化的,而memset卻又將已經(jīng)初始化好的指針清0了,這就是為什么會(huì)產(chǎn)生上面的訪問(wèn)零址的錯(cuò)誤。將上面的memset語(yǔ)句去除程序就可以正常運(yùn)行了。

            所以,從上面的問(wèn)題中,我們可以看出在構(gòu)造函數(shù)體內(nèi)調(diào)用memset將整個(gè)對(duì)象清0是很有風(fēng)險(xiǎn)的,當(dāng)沒(méi)有虛函數(shù)的時(shí)候上面程序可以正常運(yùn)行(可以試著將Base類的純虛函數(shù)聲明改成非虛函數(shù)再運(yùn)行程序)。初始化類的屬性對(duì)象時(shí),比較穩(wěn)妥的辦法還是手動(dòng)逐個(gè)進(jìn)行初使化
            posted @ 2009-10-20 21:11 Little Star 閱讀(2810) | 評(píng)論 (7)編輯 收藏

            2009年10月13日 #




                   shadow map 以前早就研究過(guò),不過(guò)一次不小心把以前做的東西都弄丟了,今天重新做了一下,
            加到了系統(tǒng)里,給大家看下效果:)



            shadow map算法原理很簡(jiǎn)單,先簡(jiǎn)單介紹下算法給新人:
            1.以光源所在位置為觀察點(diǎn)渲染場(chǎng)景(可以只渲染需要產(chǎn)生陰影的物體)將渲染后的深度值保存深度圖(一張事先準(zhǔn)備好的紋理)。
            在此步需要注意的是 此次渲染用到的模型觀視投影矩陣(以后簡(jiǎn)稱mvp)需要保存一下,下一步要用到。
            2.正常渲染場(chǎng)景,將頂點(diǎn)坐標(biāo)乘以步驟一時(shí)候的mvp,將坐標(biāo)變換到以光源為觀察點(diǎn)的坐標(biāo)系里,比較z值和從深度圖中讀出來(lái)的
            值得大小判斷遮擋,有遮擋的話將輸出顏色減弱或者換成別的隨筆你了。其中有個(gè)地方需要注意,如何從深度圖紋理中讀數(shù)據(jù),
            這個(gè)我是這么解決的:float2 suv = ((spos.xy/spos.z))//其中spos是變換到光源坐標(biāo)系下的頂點(diǎn)數(shù)據(jù),得到的suv經(jīng)過(guò)處理后可以
            當(dāng)做深度圖的紋理坐標(biāo)值,讀取方法為float4 shadow = tex2D(t11,(suv+1.0)*0.5),其中用到一個(gè)【-1,1】到【0,1】的變換。
            剩下的就是比較了  :
                        float sz =  1 - spos.z/(gDepthSize);//將深度值變換到【0,1】區(qū)間,gDepthSize是獲得深度紋理時(shí)渲染場(chǎng)景的最深值  
                        //增加陰影
                        if((sz < shadow.x))//sz是就是
                        {
                            color = color*(1 - shadow);
                            color.w = 1.0;
                            //color = float4(sz,sz,sz,1);
                        }
            //----------------------------------------------------------------------------------------------------------------------------
            關(guān)于shadow map 算法的缺點(diǎn),跟大家討論一下:
            永遠(yuǎn)的困擾shadow map的失真問(wèn)題,當(dāng)光源照射場(chǎng)景稍大的時(shí)候失真現(xiàn)象就會(huì)很嚴(yán)重,有些改進(jìn)算法,但都覺(jué)得治標(biāo)不治本。
            如果說(shuō)我整個(gè)場(chǎng)景有很多到處跑的人,那他們的陰影效果要怎樣做呢???

            感覺(jué)shadow map用在生成當(dāng)前角色的陰影挺好,如果是大范圍的不大適合。很多靜態(tài)的物體可以先把陰影事先計(jì)算好,用的時(shí)候
            直接讀取,沒(méi)有必要每幀都重新計(jì)算。

            //----------------------------------------------------------------------------------------------------------------------------
            shadow map 最大的好處是可以處理透明紋理的陰影,以為我的場(chǎng)景的樹(shù)是用透明紋理畫(huà)上去的,如果得到的陰影是個(gè)矩
            形那就很怪了,幸好shadow map 沒(méi)有這個(gè)問(wèn)題!!!



            posted @ 2009-10-13 23:34 Little Star 閱讀(3637) | 評(píng)論 (6)編輯 收藏



                     今天考慮程序的優(yōu)化問(wèn)題,突然想到說(shuō)現(xiàn)在vetex shader已經(jīng)可以訪問(wèn)紋理資源了,可以把訪問(wèn)高度圖的操作轉(zhuǎn)移
            到vetex shader中去做計(jì)算,GPU訪問(wèn)紋理的速度要比CPU訪問(wèn)內(nèi)存快多了吧(我是這么認(rèn)為的)。不過(guò)遇見(jiàn)一個(gè)問(wèn)題。
            用tex2D訪問(wèn)出來(lái)的值怪怪的,不是我要的高度紋理值,后來(lái)發(fā)現(xiàn)如果fragment shader里面訪問(wèn)了別的紋理會(huì)影響到vetex里
            面的,上網(wǎng)google了一下,好多人都說(shuō)得用 tex2DLod才行,試了下一點(diǎn)好轉(zhuǎn)的跡象都沒(méi)有,又有人說(shuō)紋理得是float的,參照
            著改了下,還是不行。(很多時(shí)候在網(wǎng)上查到的都不好用,當(dāng)然也有好用的,有點(diǎn)廢話,哈哈)
                    后來(lái)突然發(fā)現(xiàn)我的sampler都沒(méi)有跟寄存器綁定,綁定了以后就好了,很奇怪,難道說(shuō)只調(diào)用cgGLSetTextureParameter
            并不能實(shí)現(xiàn)紋理的綁定?   現(xiàn)在我把所有sampler都跟一個(gè)寄存器綁定后就一切正常了。

            就像這樣:
            //--------------------------------------------------------------------------------
            sampler2D t10 :TEXUNIT0;//地面高度圖,    其他的sampler也需要綁定到寄存器才會(huì)好用

            OutPut xS_v(float4 ipos:POSITION,
                        float2 tex:TEXCOORD0,
                        float3 normal:NORMAL)
            {
                OutPut OUT;                            //out是關(guān)鍵字
                ipos.y =tex2D(t10,tex).z*256;
            //---------------------------------------------------------------------------------


            cg語(yǔ)言運(yùn)行出來(lái)的結(jié)果經(jīng)常怪怪的,有一次我故意寫(xiě)錯(cuò)了一句話,結(jié)果編譯也能通過(guò),只是顯示出來(lái)的完全不
            是我想要的,有沒(méi)有高人指點(diǎn)一下,多謝!!


            不過(guò)把訪問(wèn)高度圖改到用GPU訪問(wèn)紋理后,效果還是很明顯的,速度提升了差不多一倍。




            posted @ 2009-10-13 09:02 Little Star 閱讀(2482) | 評(píng)論 (4)編輯 收藏

            2009年10月12日 #


            最近的工作:

            1.實(shí)現(xiàn)了刀客的動(dòng)作控制。(md2文件,GPU實(shí)現(xiàn)幀動(dòng)畫(huà))。
            2.試驗(yàn)了水面效果(有反射紋理映射,水面法線貼圖計(jì)算光照和擾動(dòng))。
            3.試驗(yàn)了billboard效果。
            4.修改程序中的一些bug。

            有兩個(gè)個(gè)問(wèn)題:
            1.OpenGL如何提高效率,歡迎指教!
            2.flt文件如何簡(jiǎn)化,有好用的工具么,我的都是從3ds導(dǎo)過(guò)來(lái)的,面數(shù)太多了,不大適用。

            發(fā)幾張孬圖,貽笑大方!



            感覺(jué)水面反射加了樹(shù)的倒影反而變得怪怪的!!

            再來(lái)幾張前幾天截得圖


            這是我的小花園,大樹(shù)的紋理爛透了。




            小河流水 嘩啦啦……





            去掉地形的效果,感覺(jué)還蠻漂亮的,呵呵

            大家有什么看法盡量留言,您的關(guān)注就是對(duì)我最大的幫助!!

            posted @ 2009-10-12 00:48 Little Star 閱讀(4527) | 評(píng)論 (9)編輯 收藏

            2009年9月25日 #

            normal map



            Parallax Mapping



            對(duì)應(yīng)的網(wǎng)格圖



            趁著人少發(fā)兩張等著挨磚頭的效果圖。

            看了一天的資料,證實(shí)了原來(lái)的想法是對(duì)的。
            normal map  是 bump map 的改進(jìn),主要是GPU可編程為其創(chuàng)造了條件。
            parallax map 是 normal map 的改進(jìn)版本,相當(dāng)于是對(duì)normal map 的修正,沒(méi)什么心意。

            Displacement Mapping貌似挺牛,明天仔細(xì)研究下 :-)
            posted @ 2009-09-25 03:33 Little Star 閱讀(2046) | 評(píng)論 (2)編輯 收藏

            2009年9月24日 #



            先來(lái)張調(diào)整了參數(shù)的roam網(wǎng)格圖片,把面片數(shù)約束在1w,這下roam的效果就明顯了吧!
            發(fā)現(xiàn)地勢(shì)如果不是特別平坦的話,很難找到一個(gè)滿意的參數(shù),既能減少面片數(shù),又能取得很好的顯示效果。
            如果有大片大片的平地,那就簡(jiǎn)單了。



            這是使用cg語(yǔ)言實(shí)現(xiàn)的 texture blend 使用四張紋理加一個(gè)細(xì)節(jié)紋理混合而成,
            增加了像素級(jí)的 Phone 光照模型。光照的顏色有點(diǎn)怪怪的。

            posted @ 2009-09-24 02:09 Little Star 閱讀(1300) | 評(píng)論 (3)編輯 收藏

            2009年9月21日 #



            面片數(shù)跟幀數(shù)永遠(yuǎn)的讓人很矛盾。這樣以后突變就很不明顯了,肉眼幾乎發(fā)現(xiàn)不了,不過(guò)感覺(jué)有退化成普通的lod的趨勢(shì)。
            當(dāng)然,遠(yuǎn)處的網(wǎng)格還是會(huì)有跳動(dòng)現(xiàn)象,不過(guò)那已經(jīng)很遠(yuǎn)了,一般不會(huì)有人注意到發(fā)生了什么。
            可是這樣大大增加了網(wǎng)格數(shù)量,應(yīng)用下視景體剪除效果會(huì)比這個(gè)好很多吧!

            posted @ 2009-09-21 01:00 Little Star 閱讀(1526) | 評(píng)論 (2)編輯 收藏

            2009年9月20日 #



            這個(gè)是我實(shí)現(xiàn)出來(lái)的 roam terrain 地形
            問(wèn)題是,當(dāng)我在地形上漫游時(shí),網(wǎng)格必然會(huì)發(fā)生變化。不同層次的網(wǎng)格之間跳躍的很厲害,讓人覺(jué)得地形很不真實(shí),
            有沒(méi)有人有好的辦法能解決?

            posted @ 2009-09-20 00:29 Little Star 閱讀(475) | 評(píng)論 (0)編輯 收藏

            2009年9月17日 #

            1.測(cè)試一下 flt文件讀取和現(xiàn)實(shí)效果。
            2.測(cè)試下引擎框架,尋找bug。


            這張是剛開(kāi)始時(shí)沒(méi)加載上外部文件時(shí)的



            這張是爆炸效果圖


            這張是著火效果



            被導(dǎo)彈追擊

            最后來(lái)張夜色美景




            誠(chéng)盼牛人指點(diǎn)不足!
            posted @ 2009-09-17 00:35 Little Star 閱讀(1868) | 評(píng)論 (7)編輯 收藏

            僅列出標(biāo)題  下一頁(yè)
            久久国产亚洲高清观看| 国产精品一区二区久久精品无码 | 粉嫩小泬无遮挡久久久久久| 久久九九全国免费| 午夜视频久久久久一区| 日日躁夜夜躁狠狠久久AV| 国产精品成人久久久久久久| 久久99热这里只有精品国产| 久久国产高清字幕中文| 99久久综合国产精品免费| 久久亚洲高清观看| 97精品依人久久久大香线蕉97| 91精品国产高清久久久久久国产嫩草 | 久久精品国产91久久麻豆自制| 伊人久久大香线蕉无码麻豆| 国产V综合V亚洲欧美久久| 思思久久好好热精品国产| 99久久精品费精品国产 | 久久中文字幕一区二区| 人妻无码αv中文字幕久久琪琪布| 欧美久久一区二区三区| 日韩精品国产自在久久现线拍 | 狠狠狠色丁香婷婷综合久久五月| 久久亚洲中文字幕精品一区| 国产精品免费久久久久电影网| 99麻豆久久久国产精品免费| 99久久国产宗和精品1上映 | 久久99国产精品久久99果冻传媒| 伊人色综合久久天天人手人婷| 亚洲精品tv久久久久久久久久| 久久99国产精品成人欧美| 日韩精品久久久久久| 久久天堂电影网| 久久综合狠狠色综合伊人| 粉嫩小泬无遮挡久久久久久| 日产精品99久久久久久| 久久亚洲中文字幕精品有坂深雪| 亚洲国产一成人久久精品| 人妻久久久一区二区三区| 性色欲网站人妻丰满中文久久不卡| 久久国产欧美日韩精品|