PhysX物理引擎(编程入门)

发表于2016-06-02
评论0 8.7k浏览
  PhysX物理引擎(编程入门)
  --PhysX,Hello World!
  Author: 华文广  E-MAIL: huawenguang@sina.com  DATE:06/7/20
  Hi,大家好,好久没有写过东西了.最近在研究物理引擎,在网上搜索了一下,发现相关的技术文章特别少,于是我心血来潮,决定给有兴趣向这方面发展的朋友写一篇入门教程,希望有所帮助。
  如果你是一名超级游戏爱好者,那想必你会听说过PPU。要是你不知道什么是PPU,那也不要紧,但至少你要知道什么是“物理加速卡”。
  Ageia是PhysX物理芯片的开发商,一家名不见经传的公司,成为敢吃螃蟹的第一人。说不定不久的将来,我们的计算机里会出现CPU,GPU,PPU三足鼎立的局面,而物理编程,也将成为游戏程序员的必修课程。本文是PhysX编和的入门教程。

一、安装
  在国际上,出名的物理引擎有Havok,Vortex,ODE,Novodex,Takamak等等,其中ode是一个免费开源的物理引擎,而Novodex就是PhysX的前身,被Ageia收购之后,改名为PhysX,是一个可以免费用于非商品用途的引擎。在这里选用PhysX来作为入门教程,主要是因为,它的帮助比较丰富,而且开发包可以免费获得。
  关于PhysX sdk的安装.首先要进入http://support.ageia.com下载SDK,注意的是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:PhysXSDKSPhysicsinclude
D:PhysXSDKSFounddationinclude
D:PhysXSDKSPhysXLoaderinclude
  然后在…àLibrary Fiks中加入以下目录:
D:PhysXsdksLIBWin32
  以上用到的“D:PhysX”指的是sdk安装目录,以你机器中的安装路径为准,本教程的示例程序用到了opengl和glut作为渲染引擎,你的计算机如何没有安装glut库,那也请先到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,

1
2
3
4
5
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)
  物理材质指的是某一具体物体的表面属性和碰撞属性,这些属性可以确定一个物体和另一个物体发生碰撞时,是如何在该的物体上反弹,滑动或者滚动的。
  你可以给场景中的所有物体指定一个相同的默认物理材质。

1
2
3
4
5
6
7
8
9
//创建默认材质
 
Nxmaterial* defaultMaterial=gscene → getMaterialFromIndex(0);
 
Default Material→setRestitution(0.9);//还原系数为0的时候没有还原.
 
DefaultMaterial→setStaticFriction(0.5);//静摩擦系数.
 
DefaultMaterial→setDynamicFricfion(0.5);//动摩擦系数.

  以上材质的系数最小值都是0,最大值是1,如果要实现一个物体落在地上会自动弹跳,那就得把还原系数设得大一点。
3、创建地面
  在本程序例子中,只有两个角色实体,地面和盒子.我们首先来看如何创建地面.

1
2
3
4
5
6
7
NxPlane shapeDesc planeDesc;
 
NxActorDesc   actorDesc;
 
actorDesc.shapes.pushBack(&phane Desc);
 
gscene→createActor(AcforDesc);

  创建一个地面角色,这可能是角色创建的最简单的方法了,只用到了四行代码,首先分别创建一个平面形状描述和角色描述,两个描述都不作任何修改,也就是使用它们的默认值.平面的中心位于世界坐标原点(0,0,0)处,而法线则是指向y轴的正方向。
  第二步,把平面描述添加到角色描述中的形状列表中去,从这里我们也可以看到,一个角色是可以包含多个形状物体的。
  第三步,就是把角色加到场景(scene)中去,也许你会留意到,前面我们所说的一个角色实体必须包括形状描述和刚体描述,两大部份,为什么这里只有形状描述呢?其实,刚体描述也是存在的,当你没有为它指定的时候,角色创建时会自动生成一个默认的刚体描述。一个刚体的默认值是这样的:它不会移动但是会把与它发生碰撞的物体反弹回去。因为它的质量是无限大的。
4、创建盒子
  前面介绍了如何创建一个地面,这是场景中最简单的一个角色了,下面我们将要创建一个稍为复杂一点的角色,一个盒子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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、绘制与运动
  完成了以上的准备工作之后,接下来便是检验成果的最后冲刺了.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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,现在我们可以让程序运行起来了,在窗口可以看见生成的一个立方体盒子.但是为什么那个盒子不会落下来,不会运动呢?这是因为我们还没有加入实时运算函数。在绘制盒子之前加入以下三行:

1
2
3
4
5
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:huawenguang@sina.com 告诉知我,也欢迎在这方面有共同兴趣的朋友来信交流.
  特别感谢我身边一个朋友的支持!

五、源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
// A minimal Novodex application test.
 
// 以下代码,先安装好PhysX SDK,及按要求配置好路径之后才能编译。
 
// 建义用使用VC2003以上版本,VC6.0在我这里有一个“return”错误,把“return”去掉就可以编译通过。
 
// 运行的时候如果提示缺少DLL文件,请在/bin/win32 目录中找到相应的DLL文件把它拷贝到工程文件夹中,
 
// 或者拷贝到系统systems32/ 文件夹中
 
// NxBoxes by Pierre Terdiman (01.01.04)
 
// author: huawenguang@sina.com
 
 
 
#define NOMINMAX
 
#ifdef WIN32
 
#include
 
#include
 
#include
 
#elif LINUX
 
#include
 
#include
 
#elif __APPLE__
 
#include
 
#include
 
#elif __CELLOS_LV2__
 
#include
 
#endif
 
 
 
#include
 
 
 
// 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;
 
}

如社区发表内容存在侵权行为,您可以点击这里查看侵权投诉指引