說明:本來已經收藏了,但是有一個擔心如果原帖被刪掉的之後會不會看不到,所以想了想還是拷過來算了..
代碼4-7:手動啟動Ogre應用程式
#include “Ogre.h”
//建立一個沒有設定檔的Root執行個體
Root *root = new Root(“”, ””);
//載入渲染系統外掛程式
root->loadPlugin(“RenderSystem_Direct3D9”);
root->loadPlugin(“RenderSystem_GL”);
//在這裡我們偽裝成使用者已經選擇了OpenGL渲染器
String rName(“OpenGL Render Subsystem”);
RenderSystemList * rList = root->getAvailableRenderers();
RenderSystemList::iterator it = rList->begin();
RenderSystem *rSys = 0;
while(ii != rList->end()){
rSys = * (it++);
if(rSys->getName() == rName){
//設定渲染器,並結束迴圈
root->setRenderSystem(rSys);
break;
}
}
//如果沒有找到一個可用的OpenGL渲染器,就在這裡結束程式。
if(root->getRenderSystem() == NULL){
delete root;
return -1;
}
//root初始化的時候,我們可以傳入一個false值來告知Root不用給我們建立渲染視窗。
root->initialise(false);
//在這裡我們仍然使用預設的參數來建立渲染視窗
RenderWindow *window = rSys->createRenderWindow(
“Manual Ogre Window”, //視窗的名字
800, //視窗的寬度(像素)
600, //視窗的高度(像素)
false, //是否全螢幕顯示
0); //其他參數,使用預設值
//在這之後你就可以向之前所說的一樣創攝像機和視口了。
在上面的代碼中沒有做任何值得讓人覺得驚奇的事情;我們假設已經從其他的代碼片斷中(比如遊戲的顯示控制)得到了參數來構建渲染視窗。如果我們不在參數列表(建構函式最後一個參數)中做特殊設定,那麼視窗名稱和視窗標題(等同於視窗的標題列和系統的元件匣)為相同字串。
注意:RenderWindow(渲染視窗)對象是對RenderTarget(渲染目標)介面的一個實現,它被介面抽象成一個渲染表面(Rendering Surface)。在我們在我們要把情境渲染到沒有幀緩衝(non-frame-buffer)的貼圖之類的渲染目標的時,這種抽象就顯得非常有用。所有的RenderTarget執行個體都可以通過名稱到相應的Factory 方法(Ogre使用的一種設計模式)中來構造,在我們上面調用createRenderWindow()方法的第一個參數就是我們需要的名稱。
在上面的例子createRenderWindow()方法中,所有參數我們都是用了最常用的設定,你可以通過線上API手冊或者直接去看OgreRenderSystem.h檔案去瞭解參數每一項的具體含義(在這裡我建議你先去看看,因為參數中的很多都經常被改變。我在這裡介紹了的一些,可能在不久也會過時。)
如果你希望讓渲染視窗顯示在螢幕的左上方,或者想要讓渲染視窗的名稱和渲染視窗的標題是用不一樣的字串。那麼你就需要用到NameValuePairList類(參數列表)的支援,其實這不過是一個標準模板庫(STL)的map對象,被createRenderWindow方法作為最後一個參數傳入,你只要把你希望改變的屬性寫在這個map中,系統就把會這些設定過濾出來,其他你沒有填入的選項仍採用預設設定(參考代碼4-7)。
NameValuePairList params;
params[“left”] = “0”;
params[“top”] = “0”;
params[“title”] = “Alternate Window Title”;
RenderWindow *window = rSys->createRenderWindow(
“MainWindow”, //渲染目標的名字
800, //視窗的寬度(像素)
600, //視窗的高度(像素)
false, //是否全螢幕顯示
?ms); //其他參數,這次我們在上面已經設定了
上面的代碼建立了一個在螢幕的左上方現實的視窗,名稱為“MainWindow”而標題是“Alternate Window Title”。
到現在為止,還沒有很完美的方法來重新設定渲染視窗或者渲染系統。舉例來說,如果你希望在程式啟動並執行時候從Direct3D渲染系統轉換到OpenGL系統,就只能關閉當前渲染系統,然後初始化一個新的OpenGL的渲染系統。雖然你也可以對渲染視窗做一些簡單的改變,比如改變大小(寬度和高度),移動它在螢幕中的位置。但是如果要改變一些複雜的屬性,比如全屏反鋸齒效果,就需要釋放這個視窗再去建立新的。
正如同我們之前提到的,有些時候你可能需要多個渲染視窗同時運行。在類似關卡編輯器中經常會出現這種情況,提供不同的顯示地區來展示你的情境。這和簡單的使用多個視口來渲染不一樣,可以讓每個視口充滿整個上層Windows。
你也可以把Ogre的渲染視窗插入到一些視窗系統或者組件系統中來(比如Qt或者wxWidgets)。通過RenderWindow的getCustomAttribute()方法來得到當前渲染視窗在系統中的控制代碼。相應的,你也可以讓Ogre使用你提供給它的視窗來作為渲染的父視窗。如下邊的代碼:
//hWnd是一個Win32系統中存在的視窗的控制代碼。
//渲染系統的指標所指的是一個初始化過的D3D9RenderSystem的執行個體。
NameValuePairList opts;
opts[“parentWindowHandle”] = StringConverter::toString(hWnd);
//everything but “opts” is somewhat irrelevant in the context of an
//explicitly parented window
RenderWindow *window = RenderSystem->createRenderWindow(
“WindowName”,
800, 600,
false, &opts);
使用上面的代碼,允許讓你把Ogre渲染視窗插入到一個已經存在的視窗系統中。但同時要注意一些事情,Ogre的視窗訊息處理函數在這裡被忽略了,因此你要手動處理Ogre相關的訊息,比如當使用者點擊關閉按鍵時候,清理Ogre渲染視窗。
攝像機和情境管理(Camera and SceneManager)
在這裡為了避免過多地介紹這兩個對象,我會盡量只介紹那些能足夠展示它們功能的方法。儘管如此,還是希望你能瞭解,是這兩個類渲染了你的情境。
情境管理器(SceneManager)
我們會在下一個章節中詳細介紹這個類,所以我盡量避免在這裡過多的深入。儘管如此,為了目前章節的完整性,我還是需要講一下最基礎的知識,來協助你建立一個應用程式所使用的情境管理系。
在你是用情境管理器之前,你首先需要構建一個相應的執行個體。
SceneManager* sceneMgr = root->createSceneManager(ST_GENERIC, “MySceneManager”);
在我們之前提到的外掛程式裡面,其中有一類是情境管理器的構造器,當Ogre載入它們時,這些構造器就把自己註冊到系統中,並同時綁定到某一種情境管理器類型:
·ST_GENERIC:最簡單的情境管理器的構造器類型,其情境管理器沒有對情境內容和結構做任何最佳化。在極其簡單的情境中(例如菜單介面)中才有其價值。
·ST_INTERIOR:這種情境管理器的構造器所產生的管理器,最佳化了室內近距離的渲染,比較適合高密度的情境。
·ST_EXTERIOR_CLOSE:最佳化了室外情境裡面的中近距離可視體,比較適合用一個簡單模型或者高度場產生的情境地圖。
·ST_EXTERIOR_FAR:Ogre曆史遺留的錯誤,已經不需要在考慮使用它。在需要的時候用ST_EXTERIOR_CLOSE和ST_EXTERIOR_REAL_FAR來代替這個選項。
·ST_EXTERIOR_REAL_FAR:這種類型的情境管理器特別適合那種需要動態載入的地形或者情境。動態載入的地形通常都非常巨大,甚至可能描繪了一個星球的地貌。
在前面的例子中,我們建立了一個ST_GENERIC類型的情境管理器。如果我們要載入一個雷神之錘III(Quake3)的情境,使用ST_INTERIOR來載入情境管理器就沒錯,因為BSPSceneManager外掛程式已經把置身註冊到ST_INTERIOR類型中。如果你希望建立一個以高度場為基礎的地形,你就需要載入TerrainSceneManager(TSM)外掛程式所帶來的情境管理器,我們可以通過ST_EXTERIOR_CLOSE類型來建立。ST_GENERIC類型中並沒有一個特定的情境管理外掛程式,但是當你載入OctreeSceneManager外掛程式之後,它就會接管ST_GENERIC的職責。
攝像機(Camera)
攝像機的概念和真實世界中的一樣,會放在一個合適的位置(這意味著它擁有位置方向的屬性),為你的情境進行“拍攝” 每一幀的工作。同時它是一個不能被渲染的物體,所以就算你的一台攝像機的拍攝範圍你有另外一台攝像機出現,另外一台也不會被“拍攝”渲染出來(參考下面的攝像機視截體3D模型,你會更好理解我說的意思的)。攝像機(以及我們之後要提及的燈光)既可以掛在情境節點上面(這就意味著它可以被動畫控制器操作),也可以直接放到空間中(你可以通過手動的方法改變其位置和方向)。就像前面說的,攝像機有一個“可視地區”的概念,它的形狀是一個頂點在攝像機位置上的稜錐,並被遠截面和近截面所切割成的視截體。如4-2所示。
圖4-2:攝像機視截體
在圖示中,(x, y, z)代表攝像機所在點的座標。X和Y分別代表近截面的寬度和高度,Z代表從攝像機到近截面的距離。X’和Y’代表遠截面的寬度和高度,其中(Z+Z’)的和代表著從攝像機到遠截面的距離。其他的一些資訊有,近截面和遠截面的距離,攝像機的縱寬比率(X/Y),代表視方向和視截體下截面(或者上截面)的夾角W(代表了在Y軸的可視範圍),以及可以由攝像機類計算出來的與地平線的夾角等。
現在假設我們需要建立一個這樣的攝像機:它擁有標準3:4的縱寬比;近截面距離攝像機5單位,遠截面距離1000單位;視線方向和視截體的下平面(以及上平面)擁有30度夾角(換句話說,就是的W等於30°)。下面的代碼實現了以上工作:
//sceneManager是一個已經存在的情境管理器執行個體的指標。
//我們在這裡構建名稱為“MainCam”的攝像機。
Camera *camera = sceneMgr->createCamera(“MainCam”);
//並不需要計算什麼,可以直接從視口中得到這個尺寸
camera->setAspectRatio(1.333333f);
//30度角可以讓我們看到一個長而遠的視野
camera->setFOVy(30.0f);
camera->setNearClipDistance(5.0f);
camera->setFarClipDistance(1000.0f);
系統根據攝像機的設定而產生的視截體,然後再剔除在視截體六個面外面的幾何體(意味著把這些集合體從當前幀的渲染列表中清除)。
渲染模式(Rendering Modes)
我們的攝像機支援3種不同的渲染模式:邊框,實體,“點”(只渲染頂點)。
camera->setPolygonMode(PM_WIREFRAME);
camera->setPolygonMode(PM_POINTS);
camera->setPolygonMode(PM_SOLOD);
PolygonMode mode = camera->getPolygonMode();
這些設定可以一直持續到下一次重新設定的時候(也就是說,不用在每一幀都調用這些函數)。系統預設的參數是PM_SOLOD。
位置和變換(Position and Translation)
攝像機(視截體)是一個MovableObject(使用中的物件)介面的實現,因此也具有這對象的所有方法和特性。MovableObject的大部分屬性都是為了可以讓它掛接到一個情境節點上面,並“背著”攝像機到處“拍攝”可以被渲染的物體。當你想要有一些不同的攝像機“追尾”技術的時候,你就會覺得這是一個不錯的方法,在後面的章節中你會看到一些第三人稱“追尾”技術的實現;不過在這裡,我們首先來瞭解一下它內在用來改變位置和方向的方法。
//確認我們已經有一個指向“Camera”類型執行個體的指標camera。
camera->setPosition(200, 10, 200);
//也可以用一個三維向量來設定攝像機座標,在我們得到情境座標時候這麼做會方便一些
//camera->setPosition(Vector3(200, 10, 200));
上面的代碼把攝像機設定到全局座標系的絕對點(200,10,200)上。這和我們使用move()方以及moveRelative()方法有很大的區別,後面兩種方法會把攝像機移動到當前位置的相對位置上。
//假設攝像機還在我們之前設定的200, 10, 200空間位置上。
camera->move(10, 0, 0); //攝像機移動到210, 10, 200
camera->moveRelative(0, 0, 10); //攝像機移動到210, 10, 210
在這裡要注意一下moveRelative()方法。它關注的是本地座標系,也就是說變換是根據當前攝像機所朝向的方向。在前面的例子中,攝像機仍然垂直地面,面向Z軸正方向。假如我們現在把攝像機向右轉90度角。這時候攝像機本地座標和世界的絕對座標就不同了,如果我們這時候再調用moveRelative(0, 0, 10),在本地座標移動10個z方向,但在全局座標卻是移動x方向,最後攝像機在全局座標系的位置是(220, 10, 200)。
指向,方向,和“著眼點”(Direction,Orientation,and“Look-At”)