Hi,大家好,好久沒有寫過東西了.最近在研究實體引擎,在網上搜尋了一下,發現相關的技術文章特别少,于是我心血來潮,決定給有興趣向這方面發展的朋友寫一篇入門教程,希望有所幫助。
如果你是一名超級遊戲愛好者,那想必你會聽說過PPU。要是你不知道什麼是PPU,那也不要緊,但至少你要知道什麼是“實體加速卡”。
Ageia是PhysX實體晶片的開發商,一家名不見經傳的公司,成為敢吃螃蟹的第一人。說不定不久的将來,我們的計算機裡會出現CPU,GPU,PPU三足鼎立的局面,而實體程式設計,也将成為遊戲程式員的必修課程。
一、安裝
在國際上,出名的實體引擎有Havok,Vortex,ODE,Novodex,Takamak等等,其中ode是一個免費開源的實體引擎,而Novodex就是PhysX的前身,被Ageia收購之後,改名為PhysX,是一個可以免費用于非商品用途的引擎。在這裡選用PhysX來作為入門教程,主要是因為,它的幫助比較豐富,而且開發包可以免費獲得。
關于PhysX sdk的安裝.首先要進入http://support.ageia.com下載下傳SDK,網站http://support.ageia.com下載安裝檔案.請注意的是Ageia的SDK隻對注冊使用者開放下載下傳。注冊是免費的,但好像要經過稽核才會開通,不過一般都會通過的。我注冊的時候好像是第二天才收到開通郵件。有兩個安裝檔案是必須下載下傳的System Software.exe和PhysX 2.3.3 SDK Core.exe前一個是底層驅動,後一個是程式核心,最新的SDK是2.4.1,但是隻針對商業客戶開放。對于初學者來說,最好把PhysX 2.3.3 SDK Training Pragrams.exe也一起下載下傳,裡面包含了從初級到進階的一系列教程,對學習這個引擎很有幫助。把所有東西下載下傳下來之後,接着是安裝了,安裝很簡單,一路next下去就可以了,但是為了讓VC中設定友善一點,建設把PhysX 2.3.3 SDK Core.exe的安裝路徑改短一點,例如我的就是安裝在D:/PhysX中。
安裝好了之後,後開始對VC編譯環境進行設定。
首先,在Tools→Options→Directories→Inclund Fik中加入以下目錄.
D:/PhysX/SDKS/Physics/include
D:/PhysX/SDKS/Founddation/include
D:/PhysX/SDKS/PhysXLoader/include
然後在…àLibrary Fiks中加入以下目錄:
D:/PhysX/sdks/LIB/Win32
以上用到的“D:/PhysX”指的是sdk安裝目錄,以你機器中的安裝路徑為準,本教程的示例程式用到了opengl和glut作為渲染引擎,你的計算機如何沒有安裝glut庫,那也請先到www.opengl.org上www.opengl.org下載下傳一個安裝上去。在這裡就不打算深入讨論glut了,沒有基礎的朋友可以先自學一下。
二.、PhysX概述
首先來介紹一下PhysX程式設計的幾個術語以及它們之間的互相聯系。
1. Scene場景:就像演員表演都需要一個舞台一樣, PhysX的所有實體運動都在這個scene中進行。
2. Actor角色:在場景中,所有參與運算的實體都是一個角色或許我這樣表達不是很正确,大家慢慢體會吧!
3. bosy剛體:用來記錄物體之間世界互動的各種系數,如速度,阻尼等.
4. shape形狀:描述和表達某一角色的外形,PhysX中提供4種基本形狀,盒子,球,膠囊以及平面。

從上面圖可以看到,PhysX程式設計其實很簡單,首先,定義各種不同的角色(actor),然後指定每個角色的形狀(shape)屬性和剛體(body)屬性,最後是把這些角色都加入到場景(scene)空間中去,這樣就可以構造出一個完整的實體世界。下面我将較長的描述程式設計的步驟.
三.程式設計實作
1.建立scene,
NxsceDesc sceneDesc:
SceneDesc.grauity = gDefaultGravity;//指定重力加速度(-9.81f)
SceneDesc.broadphase = NX_BROADPHASE_COHERENT;
SceneDesc.collisionDetection= true; //是否開啟碰撞檢測
Gscene =gPhysicsSDK→createScene(sceneDesc);
首先我們要建立一個場景的描述(Descriptor),PhysX SDK就利用這個場景描述結構來建立生成一個場景執行個體.
描述(Descriptor)在整個SDK程式設計過程中,會被廣泛地使用。描述其實就是一個資料結構,主要是用來儲存各種在建立實體時所需要的相關資訊。你可以調整描述體中各種參數來達到不同的效果,當然你可以不作任何修改,這樣的話實體在建立時會使用描述體的預設值。
在本例子中,我們建立一個指定了重力加速以及碰撞檢測算法的場景執行個體。PhysX SDK中提拱了三種碰撞檢測算法提拱給大家選擇.這裡選用的是“broad phase-coheret collison detoction”。
2.給場景(scene)增加實體材質(Materials)
實體材質指的是某一具體物體的表面屬性和碰撞屬性,這些屬性可以确定一個物體和另一個物體發生碰撞時,是如何在該的物體上反彈,滑動或者滾動的。
你可以給場景中的所有物體指定一個相同的預設實體材質。
//建立預設材質
Nxmaterial* defaultMaterial=gscene → getMaterialFromIndex(0);
Default Material→setRestitution(0.9);//還原系數為0的時候沒有還原.
DefaultMaterial→setStaticFriction(0.5);//靜摩擦系數.
DefaultMaterial→setDynamicFricfion(0.5);//動摩擦系數.
以上材質的系數最小值都是0,最大值是1,如果要實作一個物體落在地上會自動彈跳,那就得把還原系數設得大一點。
3.建立地面
在本程式例子中,隻有兩個角色實體,地面和盒子.我們首先來看如何建立地面.
NxPlane shapeDesc planeDesc;
NxActorDesc actorDesc;
actorDesc.shapes.pushBack(&phane Desc);
gscene→createActor(AcforDesc);
建立一個地面角色,這可能是角色建立的最簡單的方法了,隻用到了四行代碼,首先分别建立一個平面形狀描述和角色描述,兩個描述都不作任何修改,也就是使用它們的預設值.平面的中心位于世界坐标原點(0,0,0)處,而法線則是指向y軸的正方向。
第二步,把平面描述添加到角色描述中的形狀清單中去,從這裡我們也可以看到,一個角色是可以包含多個形狀物體的。
第三步,就是把角色加到場景(scene)中去,也許你會留意到,前面我們所說的一個角色實體必須包括形狀描述和剛體描述,兩大部份,為什麼這裡隻有形狀描述呢?其實,剛體描述也是存在的,當你沒有為它指定的時候,角色建立時會自動生成一個預設的剛體描述。一個剛體的預設值是這樣的:它不會移動但是會把與它發生碰撞的物體反彈回去。因為它的品質是無限大的。
4、 建立盒子
前面介紹了如何建立一個地面,這是場景中最簡單的一個角色了,下面我們将要建立一個稍為複雜一點的角色,一個盒子。
Int size=5
NxBodyDesc BodyDesc;
BodyDesc.angularDamping=0.5f;
BodyDesc.linearVelocity=NxUec3(0.0f,0.0f,0.0f)
NxBoxShapeDesc BoxDesc;
BoxDesc.dinesions=NxUec3(float(size),float(size),float(size));
NxActorDesc BoxActorDesc;
BoxActorDesc.shapes.pushBack(&BosDesc);
BosActorDsec.body= &BodyDesc;
BoxActorDesc.desity=0.10f;
BoxActorDesc.globalpose.t=NxVec3(0.0.20.0.0.0);
Gscene→createActor(BoxActorDesc)→userData=(viud*)size;
這裡我們建立了一個叫“Box”的場景角我。我們可以看到,盒子角色完整地包含了形狀和剛體兩大部份。和建立平面角色不同的是盒子角色描述中多了“desity”,“globalpose”兩個分量,分别指的是密度和初始位置,SDK會根據密度和體積來自動計算角色的品質。
“globalpose”指的是在世界位标中的相對位置,值得注意的是:
PhysX中,與坐标尺寸相關的數值,其機關都是“米”(m)。
5.繪制與運動
完成了以上的準備工作之後,接下來便是檢驗成果的最後沖刺了.
Whik(nbActors--)
{
NxActor*actor=*actors++;
If(!actor->userData) continue;
glpushMatrix();
float glamat[16];
actor->getGlobalPose().getColumnMajor44(glmat);
glColor4f(1.0f,1.0f,1.0f,1.0f);
glMultMatrix(glmat);
glutWireCube(float(int(actor→userData))*2.0f);
glPopMatrix();
}
上面是繪制場景的程式,這裡因為不需要繪制地面,是以第一行跳過平面角色,直接繪制盒子.
OK,現在我們可以讓程式運作起來了,在視窗可以看見生成的一個立方體盒子.但是為什麼那個盒子不會落下來,不會運動呢?這是因為我們還沒有加入實時運算函數。在繪制盒子之前加入以下三行:
Gscene→fetchResults(NX_RIGID_BODY_FINFSHED);
gsceng→Simulate(1/60.0f);
gscene→flushstream();
這樣,盒子就會産生自由落體運動,其中simulate(1/60.0)是一個積分函數,用來求位移.這裡用到了固定間隔時間1/60.0秒,其實最好是使用一些系統時間函數,來計算上一次刷屏到現在的時間,這樣會讓物體運動更加逼真。
四.總結
這是一個PhysX實體引擎的Hello World入門程式,為了讓大家更清晰地看到程式總體架構,我把程式的功能盡量寫得簡單。在接下來的一段時間裡,我會寫一些複雜的相關教程,希望各位網友友持。當然,我也是一邊學一邊寫,難免會出現錯差,如果你們發現我的文章有問題的話,請E-mail:[email protected] 告訴知我,也歡迎在這方面有共同興趣的朋友來信交流.
特别感謝我身邊一個朋友的支援!
五、源代碼
// A minimal Novodex application test.
// 以下代碼,先安裝好PhysX SDK,及按要求配置好路徑之後才能編譯。
// 建義用使用VC2003以上版本,VC6.0在我這裡有一個“return”錯誤,把“return”去掉就可以編譯通過。
// 運作的時候如果提示缺少DLL檔案,請在<PhysX SDK>/bin/win32 目錄中找到相應的DLL檔案把它拷貝到工程檔案夾中,
// 或者拷貝到系統systems32/ 檔案夾中
// NxBoxes by Pierre Terdiman (01.01.04)
// author: [email protected]
#define NOMINMAX
#ifdef WIN32
#include <windows.h>
#include <GL/gl.h>
#include <GL/glut.h>
#elif LINUX
#include <GL/gl.h>
#include <GL/glut.h>
#elif __APPLE__
#include <OpenGL/gl.h>
#include <GLUT/glut.h>
#elif __CELLOS_LV2__
#include <GL/glut.h>
#endif
#include <stdio.h>
// Physics code
#undef random
#include "NxPhysics.h"
//#include "ErrorStream.h"
#pragma comment( lib, "PhysXLoader.lib" )
static bool gPause = false;
static NxPhysicsSDK* gPhysicsSDK = NULL;
static NxScene* gScene = NULL;
static NxVec3 gDefaultGravity(0.0f, -9.81f, 0.0f);
static float gRatio=1.0f;
static void InitNx()
{
// Initialize PhysicsSDK
gPhysicsSDK = NxCreatePhysicsSDK(NX_PHYSICS_SDK_VERSION, 0, NULL);
if(!gPhysicsSDK) return;
gPhysicsSDK->setParameter(NX_MIN_SEPARATION_FOR_PENALTY, -0.05f);
// Create a scene
NxSceneDesc sceneDesc;
sceneDesc.gravity = gDefaultGravity;
sceneDesc.broadPhase = NX_BROADPHASE_COHERENT;
sceneDesc.collisionDetection = true;
gScene = gPhysicsSDK->createScene(sceneDesc);
NxMaterial * defaultMaterial = gScene->getMaterialFromIndex(0);
defaultMaterial->setRestitution(0.9f);
defaultMaterial->setStaticFriction(0.1f);
defaultMaterial->setDynamicFriction(0.1f);
// Create ground plane
NxPlaneShapeDesc PlaneDesc;
PlaneDesc.d = -5.0f;
NxActorDesc ActorDesc;
ActorDesc.shapes.pushBack(&PlaneDesc);
gScene->createActor(ActorDesc);
//CreateCube(NxVec3(0.0,20.0,0.0),5);
// Create body
//
int size = 5;
NxBodyDesc BodyDesc;
BodyDesc.angularDamping = 0.5f;
// BodyDesc.maxAngularVelocity = 10.0f;
BodyDesc.linearVelocity = NxVec3(0.0f,0.0f,0.0f);
NxBoxShapeDesc BoxDesc;
BoxDesc.dimensions = NxVec3(float(size), float(size), float(size));
NxActorDesc BoxActorDesc;
BoxActorDesc.shapes.pushBack(&BoxDesc);
BoxActorDesc.body = &BodyDesc;
BoxActorDesc.density = 0.10f;
BoxActorDesc.globalPose.t = NxVec3(0.0,20.0,0.0);
gScene->createActor(BoxActorDesc)->userData = (void*)size;
}
static void RenderCallback()
{
// Clear buffers
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Setup camera
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(60.0f, 1.0, 1.0f, 10000.0f);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
gluLookAt(0.0, 5.1, 50.0, 0.0, 0.0, 0.0, 0.0f, 1.0f, 0.0f);
gScene->fetchResults(NX_RIGID_BODY_FINISHED);
gScene->simulate(1/60.0f);
gScene->flushStream();
// Keep physics & graphics in sync
int nbActors = gScene->getNbActors();
NxActor** actors = gScene->getActors();
while(nbActors--)
{
NxActor* actor = *actors++;
if(!actor->userData) continue;
glPushMatrix();
float glmat[16];
actor->getGlobalPose().getColumnMajor44(glmat);
glMultMatrixf(glmat);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glutWireCube(float(int(actor->userData))*2.0f);
glPopMatrix();
}
glutSwapBuffers();
}
int main(int argc, char** argv)
{
// Initialize Glut
printf("PhysX, Hello World!");
glutInit(&argc, argv);
glutInitWindowSize(512, 512);
glutInitDisplayMode(GLUT_RGB | GLUT_DOUBLE | GLUT_DEPTH);
int mainHandle = glutCreateWindow("PhysX, Hello World!");
glutSetWindow(mainHandle);
glutDisplayFunc(RenderCallback);
glutIdleFunc(RenderCallback);
// Setup default render states
glClearColor(0.3f, 0.4f, 0.5f, 1.0);
glEnable(GL_DEPTH_TEST);
glEnable(GL_COLOR_MATERIAL);
glEnable(GL_CULL_FACE);
glEnable(GL_LIGHTING);
// Physics code
InitNx();
// ~Physics code
// Run
glutMainLoop();
if(gPhysicsSDK && gScene) gPhysicsSDK->releaseScene(*gScene);
gPhysicsSDK->release();
return 0;
}
Huawenguang版權所有
E-MAIL: [email protected]