PhysX(1)
By sssa2000
今天正式開始接觸PhysX,關於這個東西還是簡單說幾句。這是個很NB的物理引擎,Xbox360和PS3都使用了這個東東。
PhysX這個引擎很好用,上手十分簡單。簡單說句,現在在他的官方首頁上已經無法獲得SDK了 ,必須要先註冊提交申請,通過後才能下載。目前官方的最新版本似乎是2.5版本。我使用了以前下載的2.4.4版。
值得一提的是PhysX的文檔相當的詳細,從路徑設定到Tutorials的代碼解釋都有,大家有空還是看看吧。我這也是照抄文檔而已。
1、 安裝和設定
首先安裝好後,要設定編譯器路徑:
Include檔案:
SDKs\Foundation\include",
"SDKs\Physics\include"
"SDKs\PhysXLoader\include
"SDKs\NxCharacter\include"
"SDKs\Cooking\include"
Lib檔案:
"SDKs\lib\win32\"
Ok,現在就可以正式的使用這個引擎的SDK來開發了。
2、概念
首先解釋幾個關於PhysX的概念:
要使用這個物理引擎類比真實的物理現象,需要以下幾個東西:
NxPhysicsSDK:Physics SDK對象,這個是進行物理類比的最基礎的東西。它提供了建立物理系統的介面,是個純虛類。
NxScene: 情境。可以把它看成是物理概念上的世界,負責管理所有的物體以及世界的重力等等。一個程式可以維護多個情境。
Descriptor:一個專門負責初始化的類。你可以用這個類初始化任何該物理引擎要用到的東西,當然Descriptor也有很多種,有專門負責Scene的Descriptor,有專門負責初始化各個物體的Descriptor等等 。
NxMaterial:材質,描述物體的表面特徵。
Actors: 就是情境中的物體。也就是類比的對象。
Joints: 用於串連的物體的物體。
當然還有很多概念,例如液體,等等。但是對於剛開始的來說已經足夠了。
另外,值得一提的是時間概念:和所有即時渲染的圖形庫一樣,時間在這個即時的物理引擎中的作用也是不可缺少的。物理引擎將在指定的時間片內對需要的物理計算進行運算然後傳遞給圖形介面繪製計算結果。
3、構建物理世界
NxPhysicsSDK* gPhysicsSDK = NULL;
NxScene* gScene = NULL;
NxActor* groundPlane = NULL;
NxActor* box = NULL;
首先建立SDK對象並初始化,然後分別是情境,物體。
void InitNx()
{
1、 使用NxCreatePhysicsSDK()建立SDK對象,並初始化全域變數。
2、 建立NxSceneDesc,也就是描述Scene的描述對象,將初始化的值賦給它,例如重力,風。
3、 使用NxSceneDesc建立Scene:gPhysicsSDK->createScene(sceneDesc);
4、 建立並初始化NxMaterial
5、 建立2個NxActorDesc,並使用它們建立2個NxActor。
6、 擷取目前時間
7、 開始物理計算
}
羅列代碼我就不做了,如果需要SDK的可以聯絡我,或者誰能提供一個空間放上去更加好。
值得注意的是建立2個NxActor的過程,因為NxActor有眾多的參數需要設定。]
4、進行物理類比
我們需要在每次渲染之前進行物理運算然後將結果取出交給圖形部分渲染。
使用引擎進行物理類比很簡單,我們初始化完世界以及設定好物體之後,我們只需要2句話就能讓物理引擎自動進行相應的物理運算:
void StartPhysics()
{
// Update the time step
NxReal deltaTime = UpdateTime();
// Start collision and dynamics for delta time since the last frame
gScene->simulate(deltaTime);
gScene->flushStream();
}
//=============================================================
void GetPhysicsResults()
{
// Get results from gScene->simulate(deltaTime)
while (!gScene->fetchResults(NX_RIGID_BODY_FINISHED, false));
}
注意使用fetchResults函數的時候最後傳遞的參數是false,這表明程式將以非阻塞的方式調用。如果使用阻塞方式調用的話,就一定會等到simulate有結果才返回。
所以每次渲染前先調用這2個函數然後再進行繪圖:
void RenderCallback()
{
...
if (gScene && !bPause) //是否暫停 或者是否需要繪製情境
{
GetPhysicsResults(); //擷取上次類比結果
ProcessInputs(); //處理輸入
StartPhysics(); //根據輸入進行新的運算
}
…
}
5、動態施加力
目前為止,程式已經能類比在重力環境下的物理運動,以及和地面的碰撞監測。我們同樣可以動態施加任意方向的力給物體,只需要一句話即可:
NxVec3 gForceVec(0,0,0); //全域變數,用於描述力的方向和大小
NxReal gForceStrength = 150;
gForceVec = ApplyForceToActor(box,NxVec3(0,0,1),gForceStrength);//Z方向施力
這樣處理按鍵的代碼就顯而易見了:
switch (i)
{
// Force controls
case 'i': { gForceVec = ApplyForceToActor(box,NxVec3(0,0,1),gForceStrength); break; }
case 'k': { gForceVec = ApplyForceToActor(box,NxVec3(0,0,-1),gForceStrength); break; }
case 'j': { gForceVec = ApplyForceToActor(box,NxVec3(1,0,0),gForceStrength); break; }
case 'l': { gForceVec = ApplyForceToActor(box,NxVec3(-1,0,0),gForceStrength); break; }
case 'u': { gForceVec = ApplyForceToActor(box,NxVec3(0,1,0),gForceStrength); break; }
case 'm': { gForceVec = ApplyForceToActor(box,NxVec3(0,-1,0),gForceStrength); break; }
// Return box to (0,5,0)
case 't': { box->setGlobalPosition(NxVec3(0,5,0)); break; }
}
6、畫出物體
目前所有的物體的資訊都位於Actor中。所以我們需要擷取所有的Actor。用迴圈分別畫出。
void RenderActors(bool shadows)
{
NxU32 nbActors = gScene->getNbActors(); //number
NxActor** actors = gScene->getActors(); //Actor的數組
while (nbActors--)
{
NxActor* actor = *actors++;
DrawActor(actor);
if (shadows) // 處理陰影
{
DrawActorShadow(actor);
}
}
}
7、清理工作
void ReleaseNx()
{
if (gScene)
{
GetPhysicsResults(); // 確保沒有fetchResults在等待運行結果
gPhysicsSDK->releaseScene(*gScene);
}
if (gPhysicsSDK) gPhysicsSDK->release();
}
void ResetNx() //重設
{
ReleaseNx();
InitNx();
}
End
我覺得其實搞清楚物理引擎的工作流程比理解其中細節更加重要,其實PhysX案例中附帶的圖形部分的代碼也很有意思,不過重點不在那罷了。
有一點我覺得還是不太理解,整個程式的物體的資訊都是儲存在Sence中的Actor裡面的,包括最後的渲染都需要從Actor裡面取得,如果遊戲中的幾何物體很複雜而且很多的話我覺得還是不太妥當,當然這僅僅是第一次接觸PhysX,應該會有更加好的方法來組織資料。
初次接觸,錯誤難免,望指正。不勝感激!
2006-10-18