基礎教程三(天空,地面和霧化處理)

來源:互聯網
上載者:User
簡介

在這篇教程裡,我們將會一起探索OGRE中的天空,地面和霧化處理。通過這篇教程,您應該明白天空盒(Skybox),天空穹(Skydome)和天空面(Skyplane)的用法和區別。您還會瞭解不同種類的霧化效果,以及它們的使用方法。

從這裡開始

和以往一樣,我們將使用已經寫好的代碼作為模版。在您的編譯器裡建立一個新的工程,然後把下面的代碼添加進去:

#include "ExampleApplication.h"class TutorialApplication : public ExampleApplication{protected:public:    TutorialApplication()    {    }    ~TutorialApplication()     {    }protected:    void chooseSceneManager(void)    {    }    void createScene(void)    {    }};#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32#define WIN32_LEAN_AND_MEAN#include "windows.h"INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR strCmdLine, INT )#elseint main(int argc, char **argv)#endif{    // Create application object    TutorialApplication app;    try {        app.go();    } catch( Exception& e ) {#if OGRE_PLATFORM == PLATFORM_WIN32 || OGRE_PLATFORM == OGRE_PLATFORM_WIN32        MessageBox( NULL, e.getFullDescription().c_str(), "An exception has occured!", MB_OK | MB_ICONERROR | MB_TASKMODAL);#else        fprintf(stderr, "An exception has occured: %s/n",                e.getFullDescription().c_str());#endif    }    return 0;}

假如您能夠成功編譯這段代碼,您在啟動並執行時候可以用WASD鍵移動,滑鼠來轉鏡頭,ESC鍵來退出程式。

根對象和情境管理器的建立

根對象

首先我們需要讓OGRE建立一個地面。我們必須將情境管理器設為地面的情境管理器,而不是ExampleApplication裡預設的那個。將下面的代碼添加到chooseSceneManager函數裡:

        mSceneMgr = mRoot->createSceneManager(ST_EXTERIOR_CLOSE);

根對象是OGRE的核心對象,您可以在這裡看到OGRE對象的UML圖。到目前為止, 您已經見到過除了RenderSystem以外大部分的對象了。在上面的代碼中,我們告訴根節點我們需要一個ST_EXTERIOR_CLOSE類型的情境管理器。緊接著,根對象會要求情境列舉程式尋找您要的情境管理器然後將它返回。

等你的程式設定好了之後,你幾乎不會再接觸根對象了,你也不會直接調用情境列舉程式。

情境管理器的建立

我想先講一下情境管理器的建立和管理,以免您在後面產生誤會。情境管理器不是單例,您想建立多少就建立多少。與情境節點/燈光等不同的是,您可以直接使用 "new SceneManager()" 這種語句來直接建立它(而不必使用Root的createSceneManager方法,但你的確應該這麼用)。你可以有多個情境管理器,來同時裝載多個獨立的幾何體和實體。你可以通過重建視口的方式在任何時間交換這些情境管理器(這部分內容詳見中級教程4)或者使用多視口方法同時顯示多個情境管理器(這會在進階教程中有所涉及)。

我們為什麼使用createSceneManager函數,而不是手動地建立我們的情境管理器對象呢?當我們使用單一情境管理器時,Ogre的外掛程式系統給我們以巨大的靈活性。在SceneType枚舉類型中僅定義了幾種情境類型。直到現在,ExampleApplication常式還一直使用ST_GENERIC作為我們的情境管理器。你也許認為這裡用的是作為基礎的SceneManager類,但是只要你沒有修改plugins.config檔案,那麼你用的就不是它。如果你使用了OctreeSceneManager外掛程式的話,OctreeSceneManager會將自身註冊為ST_GENERIC類型,並且覆蓋作為基礎的SceneManager類。OctreeSceneManager使用了揀選不可見對象的系統,因此它比標準的SceneManager要快)。如果你從plugins.cfg檔案中去除了OctreeSceneManager外掛程式,那麼當你請求ST_GENERIC類型時,你才可能會使用基礎的SceneManager,也許會根據你的外掛程式設定使用更好的情境管理器,這就是Ogre系統的優美所在。

迄今為止我們只用了Root對象最基本的一些方法來建立情境管理器。實際上,我們可以使用一個字串而不是SceneType枚舉來請求一個情境管理器,Ogre靈活的SceneManager工廠允許你你定義任意數量的情境管理器類型並在需要的時候建立和銷毀它們。例如我們使用了一個外掛程式,允許你建立叫做"FooSceneManager"類型的情境管理器,我們這樣來建立一個:

       // do not add this to the project      mSceneMgr = mRoot->createSceneManager("FooSceneManager");

這會建立這樣一個情境管理器並給它一個預設名稱,雖然我在這個教程裡不會這麼用,你始終應該使用createSceneManager的第二個參數來給情境管理器命名。例如我們要建立兩個不同名稱的情境管理器可以這麼做:

      // do not add this to the project      mSceneMgr1 = mRoot->createSceneManager("FooSceneManager", "foo");      mSceneMgr2 = mRoot->createSceneManager("FooSceneManager", "bar");

通過給情境管理器命名,我們就不再需要再保留指標來跟蹤它們。Root對象會替我們搞定,之後你要使"foo"和"bar"情境管理器的時候,這樣做就行了:

      // do not add this to the project      SceneManager *foo = mRoot->getSceneManager("foo");      SceneManager *bar = mRoot->getSceneManager("bar");

當你徹底用不再使用一個情境管理器時,使用Root的destroySceneManager來刪除它釋放記憶體。

儘管我們不會通過教程來講解,你同樣可以通過繼承SceneManagerFactory類定義一個自己的SceneManager工廠。如果你建立了自己的情境管理器,或者想在你的程式使用一個標準的情境管理器之前對它做一些修改(比如建立攝像機,建立光照,載入幾何體等等),這將會非常有用。

地形

在情境中添加地面

現在我們已經搞清楚了根對象和情境管理器,是時間來真正建立一個地面了(當然是在OGRE裡)。情境管理器的基類定義了一個叫做setWorldGeometry的方法,使衍生類別便於建立情境。當使用地面情境管理器(TerrainSceneManager)時,它需要一個檔案名稱來載入地面屬性。在createScene函數中添加這行代碼:

        mSceneMgr->setWorldGeometry( "terrain.cfg" );

編譯運行你的程式,稍微調動一下鏡頭就可以看到OGRE產生的地面了,簡單吧?

"terrain.cfg" 檔案

terrain.cfg檔案包含了許多產生地面的選項,和以往一樣,我只會講一些基礎的東西,更詳細的內容可以在/*這裡*/找到。需要注意的是,地面情境管理器設計的時候包含了分頁的功能,只不過還沒有被實現。不過OGRE有一個外掛程式目前可以實現這個功能:Paging Scene Manager。

地面情境管理器使用高度圖(Heightmap)來產生地面。你可以通過更改Heightmap.image的參數來改變高度圖。你可以通過更改WorldTexture來更改地面的帖圖。你還可以通過更改DetailTexture來使地面看得更逼真。你可以在/Media/materials/textures裡找到terrain.cfg預設的幾個圖片。

照亮地面

在上篇教程裡我們剛學過如何使用燈光和陰影,不幸的是他們很難用在地面上。使用已經處理過光照效果的帖圖會比真的光照容易許多。在霧化處理那一節,我們還會討論如何使用“偽黑暗”。假如你想用真正的光照效果,你應該考慮一下Paging Scene Manager,他有很好的光照支援。

天空

OGRE提供三種天空:天空盒,天空穹和天空面。我們會逐個學習他們,現在你必須在chooseSceneManager函數中添加以下代碼:

        ResourceGroupManager::getSingleton().initialiseAllResourceGroups();

天空盒

天空盒實際上是一個包含了情境裡所有對象的巨型立方體。耳聞不如眼見,將下面代碼添加到createScene裡,自己體會什麼是天空盒吧:

        mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox" );

編譯運行你的程式。怎麼樣,效果不錯吧?(你可以更改高像素的貼圖使它看起來更逼真。) 當我們調用setSkyBox的時候我們可以設定幾個有用的參數。第一個代表是否啟用天空盒。假如你什麼時候想要取消天空盒你只須調用 mSceneMgr->setSkyBox( false, "" ); 第二個參數是天空盒使用的材質指令碼。

setSkyBox的第三個和第四個參數並不是十分重要。第三個參數設定了天空盒與攝像機的距離,第四個參數決定了天空盒是在其他對象之前渲染還是其他對象之後。我們來試試將第三個參數從預設的5000改為一個很小的值然後看看有什麼效果:

        mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox", 10 );

什麼都沒改變!這是因為第四個參數的預設值是true,也就是說天空盒會最先渲染,其它的東西會渲染到天空盒之上,從而使天空盒看起來像是在背後。(注意你不應該讓第三個參數小於攝像機的近剪下面距離,否則他會不可見!)其實天空盒並不應該最先渲染,因為你會渲染他的全部。假如你最後渲染他,OGRE只會渲染可見的部分,從而提高一定的運行速度。所以,我們來試一下最後渲染天空盒:

        mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox", 5000, false );

現在它看起來好像一樣沒有任何改變,但是天空盒的不可見的部分不會被渲染。有一點要注意,當你把天空盒設定得很近時,部分的幾何會被裁剪掉,試一下這個:

        mSceneMgr->setSkyBox( true, "Examples/SpaceSkyBox", 100, false );

看到了嗎?地面看上去穿過了天空。這肯定不是你想要的結果(假如你還正常的話)。假如你要在你的程式中使用天空盒你必須決定如何使用它。當你最後渲染天空盒是,程式的運行速度會提高一些,但是你必須小心它不要遮住你的幾何。總的來說,將第二個參數之後的所有東西都設為預設是最安全的。

天空穹

天空穹和天空盒很相像,你需要使用setSkyDome來建立天空穹。他會建立一個包含了情境中所有對象的巨型立方體,最大的區別是貼圖被用球體的方法投影到立方體上。你看到的其實還是一個立方體,只不過它的貼圖看上去像貼到了一個球體上一樣。這種天空有一個很大的缺陷,就是立方體的下面沒有任何貼圖,所有你必須要有一個類似地面的東西來遮蓋下面。

OGRE的樣本貼圖將讓你很明顯地看到這個缺陷。刪掉setSkyBox然後在createScene中添加下面代碼:

        mSceneMgr->setSkyDome( true, "Examples/CloudySky", 5, 8 );

當你運行時,將鏡頭移動到地面的中間,然後是鏡頭幾乎和地面平行。按R鍵切換到網格狀態,你會發現你看到的仍然是一個立方體(沒有底的),但是那些雲彩看起來卻像覆蓋在球體上一樣。(注意雲彩的變換是在"Examples/CloudySky"指令碼裡定義的,並不是所有的天空穹都有這樣的效果。)

天空穹的前兩個參數和天空盒一樣,第三個參數是天空穹的彎度。OGRE的API建議使用2到65之間的數值,降低第三個參數會有更好的距離感,增加第三個參數會降低扭曲度從而達到更平滑的效果。試著將它設為2然後再設為65來觀察它們的區別,下面可以很好的展示天空的彎曲效果,這是當第三個參數為2時的效果:

http://www.idleengineer.net/images/beginner03_2.png

這是當第三個參數為64時的效果:

http://www.idleengineer.net/images/beginner03_64.png

第四個參數是貼圖重複的次數,你需要根據你貼圖的大小來設定它。但是注意這個參數是Real類型(浮點數)的而不是整形。第五第六個參數分別是距離和渲染順序,與天空盒的相同。

天空面

天空面與前兩種天空有相當大的區別。我們用一個平面來替代正方體。(注意下面所有的天空面的屬性都是盡量靠地面中間)刪掉createScene裡所有天空穹的代碼。我們要先建立一個平面,然後是他朝下。setSkyPlane與前兩個函數不同的是他沒有距離參數。相應的,我們在Plane的d變數裡設定它的距離。

        Plane plane;       plane.d = 1000;       plane.normal = Vector3::NEGATIVE_UNIT_Y;

現在我們定義了平面,接下來就可以建立天空面了。注意第四個參數是天空面的大小(在這裡是1500x1500個單位)第五個參數是他重複的次數:

       mSceneMgr->setSkyPlane( true, plane, "Examples/SpaceSkyPlane", 1500, 75 );

編譯運行你的程式。咱們建立的天空面有兩個缺陷。第一個是由於貼映像素低,重複的時候不好看。你自己畫一個高像素邊緣處理好的貼圖就行了。最主要的缺陷是當你注視水平線的時候,你能看到天空面的結尾。即使你有一個好的貼圖,他看起來並不會很逼真假如你能看到天空的結尾。天空面的這種用法只能用在盆地或者四周都是牆的情境。不過他對顯卡的要求會比天空盒和天空穹要低。

幸運的是天空面的功能還不僅是這些。第六個參數跟天空盒和天空穹的渲染順序很相似。第七個參數允許你設定天空面的彎曲度,這樣一來平面就變成弧形的了。同時我們還需要設定x和y的線段數量(天空面是一個巨大的正方形,但是假如我們想讓他彎曲那麼它就要變成許多小正方形)。第八個和第九個參數就是x和y的線段數量了:

       mSceneMgr->setSkyPlane( true, plane, "Examples/SpaceSkyPlane", 1500, 50, true, 1.5f, 150, 150 );

編譯運行你的程式。現在這個天空面好看多了,你還可以用雲彩的那個材質:

       mSceneMgr->setSkyPlane( true, plane, "Examples/CloudySky", 1500, 40, true, 1.5f, 150, 150  );

編譯運行你的程式。雲彩的移動以及他重複的方法比天空穹還差一點,尤其是當你接近地面的邊緣往水平線的方向看去時。

還有一點,你可以通過 mSceneMgr->setSkyPlane( false, Plane(), "" ); 來取消天空面。

用哪個好?

究竟用哪種天空完全取決於你的程式。假如你需要看到周圍所有的東西,包括負y的方向,那你可能只有天空盒這個選擇了。假如你有一個地面或者類似地板的東西可以遮住負y方向,那麼使用天空穹會有一個更真實的效果。假如你看不到海平線(比如一個峽穀,一個監獄,或者城堡中間的院子),天空面會有一個很好的效果同時只有很低的GPU使用量。我們會在下一節中來解釋用天空面的最重要的一個原因,是因為它能和雲霧很好地結合在一起。

這些只是建議。當你自己寫程式的時候應該把這幾種都試試然後看看哪種最好再來決定最終用哪個。

霧化效果

入門

霧化效果在OGRE裡非常簡單。但是在你使用它之前要注意,當你使用地面管理器(TerrainSceneManager)的時候,一定要在調用setWorldGeometry函數之前調用setFog函數。(別的情境管理器裡沒關係的)。根據你調用這兩個函數的順序,不同的頂點程式會被調用。(注意在OGRE 1.0.0 裡有一個Bug,當你使用正確順序調用這些函數的時候,指數霧化不會被渲染。這個Bug在1.0.1的時候就已經被修複了)。

在我們開始之前,清除掉createScene函數裡除了setWorldGeometry之外的所有內容。

你需要知道關於設定霧化效果的很重要的一點是他並不像你想象的那樣在空的地方建立“霧”的實體。實際上霧只是當你觀看物體時的一個濾鏡。當你面前沒有任何物體時,你是看不見霧的。事實上,你只會看到視口(viewport)的背景色。所以想要使霧逼真,我們必須將視口的背景色設成霧的顏色。

霧分兩種:線性和指數的。線性霧“線性”增濃,而指數霧“指數的”增濃(每個距離單位霧的濃度都會比上一個單位增加得更多)。 你自己看他們效果的差距比這樣講更容易理解,所以我們來看幾個例子。

霧化類型

我們將看到的第一種霧的類型是線性,這也是最容易理解的類型。在調用setWorldGeometry後首先要設定視口的背景顏色。我們可以用createViewport方法(象用在上一個指南)一樣,但是有時我們並不需要調用視口。代碼如下:

        ColourValue fadeColour(0.9, 0.9, 0.9);       mWindow->getViewport(0)->setBackgroundColour(fadeColour);

如果視口不唯一的話,你可以用getNumViewports元函數來得到視口的數量然後不斷的操作她,但是通常這種情況是很少見的(目前為止我們知道我們僅用了一個視口),我們可以直接得到視口。設定完背景色以後我們就可以建立霧了。記住,這段代碼必須出現在setWorldGeometry被調用之前。

        mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 50, 500);

第一個參數設定霧的類型(這裡我們設的是線性)。第二個參數是我們用到的霧的顏色。第三個參數線上性霧裡面是不用設定的。第四個和第五個參數是設定霧慢慢變濃的範圍。這裡我們從50開始結束於500。這就意味著在攝像機的0到50個單位內是沒有霧的。從50到500單位內霧慢慢線性變濃。在500單位以外全是霧了。編譯運行這個應用程式。

另一種霧的類型是指數霧。和設定霧的起點和終點不一樣的是,我們要設定霧的密度(第四個和第五個參數不需要設定)。代碼如下:

      mSceneMgr->setFog(FOG_EXP, fadeColour, 0.005);

編譯運行這個應用程式。如果你用的是DirectX渲染器你會發現完全在霧的外面,在setWorldGeometry之前調用setFog來修正它。OpenGL渲染器會按照文檔裡說的那樣做,這能產生另一種產生霧的效果。這裡還有一種更厲害的指數霧函數(與前一種比較起來,離攝像機越遠它的霧更濃)。注意,如果你使用FOG_EXP2會得到更加濃密的霧。將上面的代碼替換成下面的:

     mSceneMgr->setFog(FOG_EXP2, fadeColour, 0.003);

再一次編譯和運行這個程式。基本上Ogre提供的這三種霧函數是可以互換的。你應該都實驗一下這些函數,來看看哪一種在你的應用程式裡效果更好。

霧與天空面

當你試圖將霧與天空盒/天空穹一起使用時,會遇到些有趣的問題。因為天空盒/天空穹是方形的,而霧效是以球形的方式工作的。清空createScene方法裡的內容,如果巧妙地調整天空穹和霧的參數,我們就能明顯地發現這個問題。

       ColourValue fadeColour(0.9, 0.9, 0.9);       mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 50, 515);       mWindow->getViewport(0)->setBackgroundColour(fadeColour);       mSceneMgr->setWorldGeometry("terrain.cfg");       mSceneMgr->setSkyDome(true, "Examples/CloudySky", 5, 8, 500);

編譯運行程式。如果你轉動攝像機,你會發現天空穹的某些部分會探出來(藍色部分從邊緣探出來,而不是中央):

http://www.idleengineer.net/images/beginner03_fogbox.png

這顯然不是我們想要的。另一種選擇就是使用天空面。在createScene裡用以下代碼替換:

       ColourValue fadeColour(0.9, 0.9, 0.9);       mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 0, 130);       mWindow->getViewport(0)->setBackgroundColour(fadeColour);       Plane plane;       plane.d = 100;       plane.normal = Vector3::NEGATIVE_UNIT_Y;       mSceneMgr->setWorldGeometry("terrain.cfg");       mSceneMgr->setSkyPlane(true, plane, "Examples/CloudySky", 500, 20, true, 0.5, 150, 150);

這樣看起來就對了。如果我們往上看,就能看見天空(真實情況也是這樣),而不會滑稽地從別處探出來。不管你是否使用彎曲,這都能解決當你往地平線上看的時候出現的不正常現象。

還有一種辦法能讓霧效不涉及整個天空,但需要對材質指令碼進行修改。這已不是本課的內容,為了今後參考,它就是材質裡禁用霧效的那個參數。

黑暗中的霧

當你設定霧的時候,可能不想使用天空,因為霧已經濃到你無法看見天空了。上面講的霧效的一些技巧在某些情境非常有用。不把霧設定成明亮的顏色,而是設成非常暗,看看有什麼效果。(注意,我們把天空面設定成離攝像機只有10個單位距離,在設定霧之前):

       ColourValue fadeColour(0.1, 0.1, 0.1);       mWindow->getViewport(0)->setBackgroundColour(fadeColour);       mSceneMgr->setFog(FOG_LINEAR, fadeColour, 0.0, 10, 150);       mSceneMgr->setWorldGeometry("terrain.cfg");       Plane plane;       plane.d = 10;       plane.normal = Vector3::NEGATIVE_UNIT_Y;       mSceneMgr->setSkyPlane(true, plane, "Examples/SpaceSkyPlane", 100, 45, true, 0.5, 150, 150);

編譯運行程式。這就是我們得到的:

http://www.idleengineer.net/images/beginner03_darkness.png

不算太糟糕。當然,一旦你會使用光照,你就不必用這種技巧了。但這的確展示了霧效的靈活性,以及用這個引擎你能做一些有趣的事情。如果你正寫一個第一人稱遊戲,使用黑色霧能夠製造“失明”或“符咒”這樣的特效。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.