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

小星星的天空

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

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

Dog

Vptr1

Cute::i

Vptr2

Pet::j

Dog::z


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

      好了,既然編譯器幫我們自動(dòng)完成了不同父類的地址轉(zhuǎn)換,我們調(diào)用虛函數(shù)的過程也就跟單繼承統(tǒng)一起來了:通過具體對象,找到vptr(通常指針的起始位置,因此Cute找到的是vptr1,而Pet找到的是vptr2),通過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)常使用基類的指針來引用派生類的對象,如果是動(dòng)態(tài)創(chuàng)建的,對象使用完后,我們使用delete來釋放對象。但是,如果我們不注意的話,會(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ù)沒有調(diào)用,memory leak!

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

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

posted @ 2009-10-20 21:36 Little Star 閱讀(393) | 評論 (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í)間。對于虛函數(shù)的底層機(jī)制很多資料都有較詳細(xì)闡述,但對我個(gè)人而言,這次的調(diào)試讓我感觸頗深。

先來看一段代碼,在繼承的類Advance之中,有很多屬性字段,我希望將其清成0或NULL,于是在構(gòu)造函數(shù)中我通過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ā)生訪問沖突

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

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

函數(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è)不可見的數(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)用。

這里的問題,就出在

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

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

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

2009年10月13日 #




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



shadow map算法原理很簡單,先簡單介紹下算法給新人:
1.以光源所在位置為觀察點(diǎn)渲染場景(可以只渲染需要產(chǎn)生陰影的物體)將渲染后的深度值保存深度圖(一張事先準(zhǔn)備好的紋理)。
在此步需要注意的是 此次渲染用到的模型觀視投影矩陣(以后簡稱mvp)需要保存一下,下一步要用到。
2.正常渲染場景,將頂點(diǎn)坐標(biāo)乘以步驟一時(shí)候的mvp,將坐標(biāo)變換到以光源為觀察點(diǎn)的坐標(biāo)系里,比較z值和從深度圖中讀出來的
值得大小判斷遮擋,有遮擋的話將輸出顏色減弱或者換成別的隨筆你了。其中有個(gè)地方需要注意,如何從深度圖紋理中讀數(shù)據(jù),
這個(gè)我是這么解決的:float2 suv = ((spos.xy/spos.z))//其中spos是變換到光源坐標(biāo)系下的頂點(diǎn)數(shù)據(jù),得到的suv經(jīng)過處理后可以
當(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í)渲染場景的最深值  
            //增加陰影
            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的失真問題,當(dāng)光源照射場景稍大的時(shí)候失真現(xiàn)象就會(huì)很嚴(yán)重,有些改進(jìn)算法,但都覺得治標(biāo)不治本。
如果說我整個(gè)場景有很多到處跑的人,那他們的陰影效果要怎樣做呢???

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

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



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



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


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




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

2009年10月12日 #


最近的工作:

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

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

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



感覺水面反射加了樹的倒影反而變得怪怪的!!

再來幾張前幾天截得圖


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




小河流水 嘩啦啦……





去掉地形的效果,感覺還蠻漂亮的,呵呵

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

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

2009年9月25日 #

normal map



Parallax Mapping



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



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

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

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

2009年9月24日 #



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



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

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

2009年9月21日 #



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

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

2009年9月20日 #



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

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

2009年9月17日 #

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


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



這張是爆炸效果圖


這張是著火效果



被導(dǎo)彈追擊

最后來張夜色美景




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

僅列出標(biāo)題  下一頁
青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <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>
            国产丝袜一区二区| 亚洲精品午夜精品| 欧美成人精品三级在线观看| 亚洲欧美国产精品va在线观看| 麻豆精品传媒视频| 蜜臀av一级做a爰片久久 | 亚洲欧美偷拍卡通变态| 在线亚洲电影| 香蕉久久夜色精品国产使用方法 | 亚洲美女中文字幕| 亚洲一区二区伦理| 久久黄金**| 欧美插天视频在线播放| 亚洲乱码视频| 亚洲欧美在线免费观看| 美女精品网站| 国产精品国产a级| 亚洲第一页自拍| 亚洲综合首页| 免费成人在线视频网站| 亚洲一区二区在| 欧美精品国产一区| 狠狠久久综合婷婷不卡| 日韩视频免费观看高清在线视频 | 久热精品视频在线| 国产精品白丝jk黑袜喷水| 亚洲电影网站| 久久久精品一品道一区| 中文av一区二区| 欧美日韩色综合| 亚洲卡通欧美制服中文| 欧美成人自拍| 欧美成人免费小视频| 一区二区三区在线视频免费观看| 亚洲一区欧美激情| 一区二区91| 国产精品婷婷| 欧美一区二区三区在线播放| 中国成人黄色视屏| 国产精品一区=区| 久久久av毛片精品| 久久久久国产免费免费| 欲色影视综合吧| 亚洲三级影片| 国产精品草草| 久久久久久伊人| 欧美理论视频| 欧美一区午夜精品| 欧美国产视频日韩| 亚洲欧美日韩第一区| 久久精品亚洲乱码伦伦中文 | 香蕉成人久久| 亚洲电影第1页| 亚洲性夜色噜噜噜7777| 一区在线视频观看| 亚洲青涩在线| 136国产福利精品导航| 亚洲欧美日韩成人| 久久国产直播| 亚洲欧美三级伦理| 欧美精品国产一区| 久久综合狠狠综合久久综青草 | 玖玖国产精品视频| 欧美乱大交xxxxx| 欧美aⅴ一区二区三区视频| 国产精品www.| 一区二区三区国产在线| 最新中文字幕一区二区三区| 亚洲女爱视频在线| 亚洲一区激情| 国产精品视频免费观看| 日韩亚洲欧美在线观看| 日韩午夜电影av| 一区二区三区久久精品| 欧美久久久久中文字幕| 亚洲国产婷婷香蕉久久久久久| 国产精品揄拍500视频| 午夜精品网站| 欧美中日韩免费视频| 国产一区二区三区四区| 欧美伊人久久久久久久久影院| 久久香蕉国产线看观看av| 亚洲电影下载| 国产精品久久久久久妇女6080 | 久久久久www| 在线看片欧美| 欧美视频在线观看视频极品| 亚洲欧美亚洲| 最新69国产成人精品视频免费| 国产一区二区三区免费观看| 欧美在线二区| 国产亚洲成精品久久| 麻豆av一区二区三区久久| 亚洲精品一二| 久久伊人亚洲| 亚洲视频你懂的| 国产亚洲美州欧州综合国| 欧美激情综合五月色丁香小说| 国产美女一区二区| 欧美成人午夜激情视频| 亚洲欧美日韩在线不卡| 一本色道久久综合亚洲精品不| 欧美在线观看视频在线| 亚洲一区二区日本| 最新日韩精品| 亚洲电影专区| 国产一区二区高清| 国产日韩欧美亚洲| 国产欧美日韩视频| 国产精品区一区二区三| 欧美性大战久久久久久久蜜臀| 美女视频黄a大片欧美| 久久免费视频网| 蜜桃久久av一区| 久久经典综合| 开心色5月久久精品| 欧美成人综合网站| 欧美激情精品| 国产精品vip| 在线看片成人| 一本大道久久a久久精二百| 亚洲深夜福利网站| 欧美一级黄色网| 欧美激情导航| 亚洲日韩欧美视频一区| 亚洲一区免费网站| 久久综合网色—综合色88| 欧美视频网站| 在线精品福利| 欧美一级电影久久| 亚洲高清网站| 亚洲欧美在线x视频| 久久字幕精品一区| 亚洲国产老妈| 欧美中文字幕不卡| 欧美色欧美亚洲高清在线视频| 国产精品综合视频| 亚洲精品在线免费| 久久久久久久网| 午夜精品理论片| 欧美精品九九99久久| 国产一区二区日韩| 午夜视频在线观看一区二区| 亚洲国产高清在线| 久久青草福利网站| 国产亚洲一区二区三区在线观看| 亚洲图片在线| 亚洲视频图片小说| 国产精品久久久久国产a级| 亚洲精选在线| 亚洲国产一区二区a毛片| 欧美国产精品中文字幕| 日韩亚洲不卡在线| 亚洲久久一区二区| 国产精品日韩一区二区三区| 一区二区三区黄色| aa日韩免费精品视频一| 国产精品拍天天在线| 西西裸体人体做爰大胆久久久| 亚洲乱码视频| 狠色狠色综合久久| 亚洲福利视频网| 国产精品女人毛片| 欧美成人精品h版在线观看| 男女精品视频| 欧美一级理论片| 欧美国产在线电影| 欧美伊人久久大香线蕉综合69| 久久久999成人| 亚洲手机视频| 欧美高清自拍一区| 欧美一区二区三区在线看| 狼人社综合社区| 久久av资源网站| 欧美人与禽性xxxxx杂性| 免费欧美高清视频| 午夜天堂精品久久久久| 久久福利资源站| 亚洲欧美另类久久久精品2019| 免播放器亚洲| 久久久噜噜噜久久| 国产精品热久久久久夜色精品三区| 美女尤物久久精品| 激情婷婷久久| 免费观看在线综合| 国产一区在线免费观看| 亚洲乱码国产乱码精品精可以看| 日韩午夜剧场| 国产精品视频免费观看| 亚洲欧洲一区二区天堂久久 | 欧美电影免费观看| 亚洲一卡二卡三卡四卡五卡| 毛片一区二区三区| 欧美一级淫片aaaaaaa视频| 亚洲精品一区二区三区av| 国产婷婷97碰碰久久人人蜜臀| 欧美国内亚洲| 一区二区精品| 蜜臀91精品一区二区三区| 一本色道久久综合亚洲精品不 |