基礎教程二(攝影機,陰影和光照處理)

來源:互聯網
上載者:User
先決條件

本教程假定你已經擁有了c++程式設計的知識,並且已經安裝和編譯了一個Ogre的應用程式(如果你在設定你的應用程式中有困難,請參考this guide獲得更詳細的編譯步驟)。這個教程同時也是建立在上一章基礎上的,因此預設你已經瞭解了上個教程的內容。

簡介

在這篇教程裡,我會向您介紹幾個新的構架,同時還會補充一些過去的內容。這篇教程主要介紹如何使用燈光對象以及如何產生陰影。我們還會稍微瞭解一下攝像機的用法。 當你看完這篇教程以後,你應該試著慢慢的添加一些代碼到你自己的工程裡,然後看看結果。你可以參考這個教程裡最終狀態的原始碼。如果你在寫代碼時遇到了困難,也可以和最終工程裡的源檔案對比一下。

從這裡開始

像上一個教程一樣,我們將使用一個先前建立的代碼作為我們出發的起點。我們將增加兩個方法到TutorialApplication class中:createViewport和createCamera。這兩個方法我們已經在基類ExampleApplication中定義了,在這個教程中我們將看到攝像機和視口具體建立和使用。

為這個項目在編譯器中建立一個工程,添加源檔案包含這些代碼:

#include "ExampleApplication.h"class TutorialApplication : public ExampleApplication{protected:public:    TutorialApplication()    {    }    ~TutorialApplication()     {    }protected:    virtual void createCamera(void)    {    }    virtual void createViewports(void)    {    }    void createScene(void)    {        Entity *ent;        Light *light;    }};#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 occurred!", MB_OK | MB_ICONERROR | MB_TASKMODAL);#else        fprintf(stderr, "An exception has occurred: %s/n",                e.getFullDescription().c_str());#endif    }    return 0;}

如果你是在Windows下使用OgreSDK的,請確定添加“"[OgreSDK_DIRECTORY]/samples/include”目錄到這個工程(ExampleApplication.h檔案所在的位置)除了標準包含以外。如果使用的是Ogre的原始碼包,這個檔案應該在“"[OgreSource_DIRECTORY]/Samples/Common/include”目錄中。請確保你可以編譯這段代碼在進入下一階段之前(我們並沒有運行,因為他是會崩潰,直到隨後給他添加更多的東西)。我們稍後將添加代碼來完成這項工作。如果你有問題,請查看這個Wiki頁獲得設定你編譯器的資訊,如果仍然有問題請試著去看看協助欄。

程式控制: 用鍵盤上WASD來移動,用滑鼠觀察四周。Ese鍵退出。

攝像機

Ogre的攝像機

攝像機是用來觀察我們所建立的情境的。攝像機是一個特殊的物體,她的工作方式有點類似於情境節點。攝像機可以設定位置進行位移,滾動,傾斜操作和綁定到任意情境節點上。就像情境節點一樣,攝像機的位置跟父節點有關(就好像尊重長輩一樣)。對所有的移動和旋轉來說,你可以把攝像機考慮成情境節點。

對於Ogre攝像機來說,有一點跟你想象不一樣的是同一個時間內你只能使用一台攝像機(當前)。這就是說,我們不能建立其中一個攝像機去觀察情境的一部分,而用第二個去觀察情境的另一部分,並且僅通過開啟或關閉某一個攝像機就可以來呈現我們想要的一部分情境。不過要完成這一步有一個替代方式就是通過一些建立情境節點來扮演“攝像機控制代碼”。這些情境節點簡單的參加到情境中指向攝像機可能要捕捉的位置。當只需要展示一部分情境時,攝像機只需要簡單的把自己綁定到適當的節點上就可以了。我們會在幀監聽教程裡重溫這項技術。

建立一個攝像機

我們將通過替換預設的ExampleApplication方法來建立攝像機。

找到TutorialApplication::createCamera 方法。首先,我們將建立攝像機。情境管理器裡有內建的攝像機。我們就以此來建立我們的攝像機。添加下面的代碼到建立攝像機方法裡:

          // 建立攝像機       mCamera = mSceneMgr->createCamera("PlayerCam");

這將建立一個叫"PlayerCam"的攝像機。如果你不打算保留它的指標,那麼你可以調用情境管理器的方法 getCamera 通過傳遞它的名字來擷取它。

接下來,我們繼續設定攝像機的初始位置和朝向。我們將設定攝像機朝向座標原點,所以我們需要設定一個恰當的Z軸距離。添加下面的代碼到剛才的後面:

            //設定攝像機位置和方向       mCamera->setPosition(Vector3(0,10,500));       mCamera->lookAt(Vector3(0,0,0));

lookAt方法是靈活的。你能夠設定它面向任何你想要的角度。情境節點也有同樣的方法,能夠通過此方法在任何情況下,很容易的達到你想要的方向。 最後,我設定一個5單位距離的近距離裁剪。通過設定攝像機的裁剪距離,來規定該距離內的事物是你看不到的。設定近距離裁剪後,你能夠在離實體很近的時候,很容易的透過實體看到情境。還有一種很微小的情況就是,當你和某個物體很近時,這個物體將填滿你的整個視野。同樣,你也可以設定遠距離裁剪。這個值的設定能夠讓引擎不去渲染遠於它的物體。這主要在你需要遠距離渲染大情境的時候,用來增加幀速。添加如下代碼,以實現近距離裁剪:

          mCamera->setNearClipDistance(5);

設定遠距離裁剪和上面一樣,只是方法名是這個:setFarClipDistance。當然,在這個例子裡,你沒必要設定遠距離裁剪。

視口

Ogre的視口

當你開始處理多個攝像機時,視口類的概念就變得對你很有用了。我提出這個主題的原因是,我認為瞭解當渲染一個情境時Ogre是如何決定使用哪個攝像機的是非常重要的。在同一個時間內Ogre中可能有多個情境管理器在運行。也有可能把情境分為多個地區,然後用分開的攝像機去渲染情境中不同的地區(例如:思考一下控制台遊戲中兩個人的觀察)。然而我們可能去做這些事情的時候不用考慮他們是如何被做到的,這是進階教程的內容。

為了瞭解Ogre是怎樣渲染情境的,考慮一下Ogre中建立的這些東西:攝像機,情境管理器,和渲染視窗。渲染視窗我們還沒有涉及到,但是她是所有物體呈現的最基本的視窗。情境管理器實體建立攝像機去觀察情境。你必須告訴渲染視窗哪些攝像機去呈現情境,哪部分視窗被渲染進來。你告訴渲染視窗的攝像機所呈現的地區就是視口,並且這裡只有一個視口實體。

在這篇指南中我們將介紹怎樣註冊攝像機來建立視口。我們可以通過運用這個視口實體來設定正在渲染的情境的背景色。

建立視口

我們將重點關注ExampleApplication裡視口的建立,因此找到TutorialApplication::createViewports的成員函數。建立視口的方法通常是簡單的調用渲染視窗中addViewport函數,把她提供給正在使用的攝像機。ExampleApplication類已經通過我們的渲染視窗移到mWindow類裡面了,因此添加這些代碼:

       // Create one viewport, entire window       Viewport* vp = mWindow->addViewport(mCamera);

現在我們擁有自己的視口了,我們可以用他做什麼呢?答案是:不多。最重要的事情我們可以做的是調用setBackgroundColour方法給背景設定任意我們選擇的顏色。因為在這章中我們要處理光照因此我們把背景設定為黑色。

       vp->setBackgroundColour(ColourValue(0,0,0));

注意顏色值分別是紅綠藍三種顏色的值他們的參數在0和1之間。最後也是最重要的事情是我們需要設定攝像機的縱寬比。如果你用的是一些非標準全視窗視口,而未能設定這個的話結果會出現一個非常奇怪的情境圖。我們立刻設定她儘管我們使用的是預設的縱寬比:

       // Alter the camera aspect ratio to match the viewport       mCamera->setAspectRatio(Real(vp->getActualWidth()) / Real(vp->getActualHeight()));    

這就是所有的我們對視口的簡單運用。

這時你應該首先去編譯一下然後運行這個應用程式,儘管什麼也呈現不了只是黑的情境(用Esc來退出)。確保你可以運行這個程式不會崩潰再繼續。

光照和陰影

Ogre支援的陰影類型

Ogre currently supports three types of Shadows: Ogre目前支援3種類型的陰影(目前的版本1.4.x目前已經支援四種陰影類型):

  1. 調製紋理陰影 (SHADOWTYPE_TEXTURE_MODULATIVE) - 是這3種中最節省資源的。她建立一個陰影投射者的黑與白渲染到紋理,然後用於情境中。
  2. 調製模板陰影 (SHADOWTYPE_STENCIL_MODULATIVE) - 這項技術是在所有的非透明體被渲染到情境以後再渲染所有的陰影體來調製陰影,她耗費資源沒有加成模板陰影強,但是也不是非常精確。
  3. 加成模板陰影 (SHADOWTYPE_STENCIL_ADDITIVE) - 這項技術是渲染每一個光源作為分離的部分附加到情境中。 對顯卡來說這是比較麻煩的,因為在情境中每一個附加的光源需要一個附加渲染通路。

Ogre不支援軟陰影作為引擎的一部分。如果你想用軟陰影,你需要寫你自己的頂點和片段語言。注意這隻是一個簡單的介紹。Ogre手冊完整描述了陰影以及他們的用法。

在Ogre中使用陰影

用陰影在Ogre中相對來說是比較簡單的。SceneManager類中有一個setShadowTechnique成員函數,我們可以使用她設定我們想要的陰影類型。這樣只要你建立了一個實體,調用setCastShadows方法來設定這個實體是否投射陰影。 我們現在將設定環境光線全變暗,然後設定陰影類型。找到TutorialApplication::createScene成員函數添加如下代碼:

       mSceneMgr->setAmbientLight(ColourValue(0, 0, 0));      mSceneMgr->setShadowTechnique(SHADOWTYPE_STENCIL_ADDITIVE);

現在情境管理器運用的是加成模板陰影。讓我們建立一個實體然後讓他投射陰影。

       ent = mSceneMgr->createEntity("Ninja", "ninja.mesh");      ent->setCastShadows(true);      mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);

另外,ninja.mesh已經通過ExampleApplication為我們預載入了。我們還需要一些東西來安放Ninja(就是需要一些東西來接收她投射的陰影)。為了做到這個我們需要為此建立一個平面。這並不意味著我們需要用到MeshManager指南,但是我將複習一些更基礎的東西因為我們要用她來建立一個平面。一開始我們需要定義平面實體本身,她需要提供一個法線和一個到原點的距離。(舉個例子)我們可以用平面來組織世界幾何體的一部分,在這種情況下我們需要指定一些東西與原點之間的距離不為0. 現在我們僅僅想要一個平面把y正半軸作為她的法線(這意味著我想讓她面朝上),與原點的距離為0:

      Plane plane(Vector3::UNIT_Y, 0);

現在我們需要註冊這個平面以便於我們可以把她運用到我們的應用程式中。MeshManager類瞭解所有讀到我們應用程式中的模型(例如:她瞭解我們用到的robot.mesh和ninja.mesh)。createPlane成員函數接收一個平面定義通過參數製造的一個模型。這些是註冊平面所用到的:

      MeshManager::getSingleton().createPlane("ground",     ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, plane,     1500,1500,20,20,true,1,5,5,Vector3::UNIT_Z);         

另外,我現在不想探究怎樣用MeshManager的細節(如果你想準確的瞭解每個參數的作用,請參考API)。基本上我們已經註冊了我們的平面尺寸為1500×1500,新模型名字叫“ground”。現在,我們可以通過模型建立一個實體把她放置到情境中:

      ent = mSceneMgr->createEntity("GroundEntity", "ground");     mSceneMgr->getRootSceneNode()->createChildSceneNode()->attachObject(ent);

漂亮吧?哈!對ground來說在結束之前我們還有兩件事情要做。首先是告訴情境管理器我們不想把她設定成投射陰影體直到她被用做陰影體。第二件事是我們需要在上面貼紋理。我們的robot和ninja模型已經擁有了為他們定義的材質指令碼。當我們手動去建立ground模型的時候,我們不需要指定用什麼紋理。我們將用"Examples/Rockwall""Examples/Rockwall"材質指令碼,Ogre包含以下這個例子:

      ent->setMaterialName("Examples/Rockwall");     ent->setCastShadows(false);

現在情境中我已經有一個Ninja和ground,讓我們來編譯和運行這個程式。我們等待...什麼也沒有!!發生什麼事了?上一個指南中我們明明添加了Robots而且她也被呈現出來了。原因是Ninja沒有露出來,因為情境中的環境光線設定的是全黑。因此讓我們添加光源看看會發生什麼。

故障排除

一些人報道在用GCC和code::blocks時他們的程式片段中遇到一些問題。如果收到類似這樣的錯誤:

variable 'vtable for Ogre::MeshPtr' can't be auto-imported. Please read the documentation for ld's --enable-auto-import for details.

You'll need to add the following to your linker options: 你需要添加如下的東西添加到你連接器選項中:

-Wl,--enable-runtime-pseudo-reloc

如果用了codeblocks::ide點擊project name->build options->linker options,在那設定一下。

光源種類

Ogre提供了三種光源類型。

  1. 點光源 (LT_POINT) - 從一個點發出光放射到四周。
  2. 聚光源 (LT_SPOTLIGHT) - 嚴格來說聚光燈工作有點像手電筒。 你在某個地方設定了燈光的起點,然後燈光射向一個方向。你可以告訴光源內錐角度大小和外錐角度大小(你知道手電筒內部會明亮一些。)
  3. 有向光 (LT_DIRECTIONAL) - 有向光類比非常遙遠地方的燈光,在情境中從某個方向照射物體。如果你想要一個夜晚的情境你就需要類比月光。你可以通過為情境設定環境光線來做到這個,但是這並非是真實情境的類比因為月光並不平等地照亮所有物體(日光也不是)。一種方法解決這個是設定一個有向的光源然後指向一個方向,月光就會顯示出來。

光源有一個範圍屬性描述光看上去的樣子。兩個更重要的屬性是光的漫反射顏色和鏡面反射顏色(diffuse and specular color)。每一個材質指令碼定義有多少漫反射和鏡面反射光線由材質反射來,稍後我們將學習如果控制她。

建立光源

在Ogre中建立一個光源需要調用情境管理器中的createLight方法然後提供光源的名稱,就跟我們建立實體和攝像機一樣,光源僅僅有兩個方法setPosition和setDirection(而且並不是像旋轉,位移,滾動一樣有全套的移動函數)。如果你需要移動光源(例如建立一個光源跟隨角色),你需要把光源綁定到情境節點上。

因此,讓我們從基礎的點光源開始吧。首先我們要建立光源設定她的類型和她的位置:

      light = mSceneMgr->createLight("Light1");     light->setType(Light::LT_POINT);     light->setPosition(Vector3(0, 150, 250));

現在我們已經建立好了光源,我們可以設定她的漫射色和鏡面色了。讓我們設定為紅色:

      light->setDiffuseColour(1.0, 0.0, 0.0);     light->setSpecularColour(1.0, 0.0, 0.0);

好的現在編譯和運行這個應用程式。成功了!我們現在可以看見Ninja和她投射的影子了。確定你也可以從前面,側面都可以看到她。有個要注意的事是你看不見光源。你看見產生的光事實上不是光照實體本身。許多Ogre指南裡都添加了一個實體展示光是從哪發射出來的。如果你在應用程式中使用燈光時遇到了困難,你應該考慮建立這些東西這樣你可以準確知道光源的位置。

接著讓我們試試有向光。注意為什麼ninja前面有一些傾斜的黑色?讓我們添加一些黃色的有向光射向她的前面。設定光源和顏色的步驟和設定點光源是一樣的:

       light = mSceneMgr->createLight("Light3");      light->setType(Light::LT_DIRECTIONAL);      light->setDiffuseColour(ColourValue(.25, .25, 0));      light->setSpecularColour(ColourValue(.25, .25, 0));

因為有向光是從無窮遠的距離射來的,因此我們不需要設定她的位置,僅僅是設定她的方向。我們設定光的方向是z正半軸和y負半軸(類似從ninja的45度角正前方射過來):

      light->setDirection(Vector3( 0, -1, 1 )); 

編譯並運行這個應用程式。在情境中我們已經有兩個影子了,因為有向光很微弱,因此影子也比較弱。最後讓我們來試試聚光燈。我們將建立一個藍聚光燈:

      light = mSceneMgr->createLight("Light2");     light->setType(Light::LT_SPOTLIGHT);     light->setDiffuseColour(0, 0, 1.0);     light->setSpecularColour(0, 0, 1.0);

我們仍然需要設定位置和方向來確定聚光源。我們將建立一個聚光來源硬碟旋在Ninja的右側,然後直射下來:

      light->setDirection(-1, -1, 0);      light->setPosition(Vector3(300, 300, 0));

聚光源還允許我們指定光束的寬度。考慮一下手電筒的光束。中心的內光束比周圍光束明亮一些。我們可以設定這兩種寬度通過調用setSpotlightRange方法。

      light->setSpotlightRange(Degree(35), Degree(50));

編譯運行她,紫色的Ninja……好恐怖!

嘗試要做的事情

不同的陰影類型

這個demo裡我們設定的陰影類型僅僅是SHADOWTYPE_STENCIL_ADDITIVE(加成模板陰影)。嘗試設定成其他兩種陰影看看會發生什麼。還有一些其他的與陰影相關的方法在情境管理器類中。試試他們然後看看你能得到什麼。

光源衰減

當離光源越來越遠的時候,光源定義了一個setAttenuation方法允許你控制定義光怎樣漸弱。添加一個函數來調用點光源設定衰減為不同的值,看看會怎樣影響光?

SceneManager::setAmbientLight

實驗一下這個方法setAmbientLight,在mSceneMgr中。

視口背景色

在createViewports方法中改變預設的ColourValue值。在這裡改變顏色也許並不是合適的,但是可以讓你瞭解一下如何改變她。

Camera::setFarClipDistance

在createCamera中我們設定的攝像機距離很近。添加一個函數調用setFarClipDistance,設定參數為500,分別設定模板陰影開啟和關閉並移動攝像機來觀察Ninja。注意到速度變慢沒?

注意:你需要設定mSceneMgr->setShadowUseInfiniteFarPlane(false),否則你將得到一些奇怪的影子(看看這裡)

平面

在本指南中我們並沒有涉及太多關於平面的內容(她不是本文的重點)。在後續章節中我們會重新提到她,但是現在你應該瞭解瞭解createPlane方法試著去用用這些函數。

完整來源

如果有困難,可以看看原始碼

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.