Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
Help us understand the problem. What is going on with this article?

PhysXを試してみる

初めに

 この記事はPhysX4.1を四苦八苦しながら導入&使い方を試した記事です。他の人の為にもこの記事を書くことにしました。ビルド環境はVS2019(x64)のC++17です。

PhysXとは

 物理演算を実行してくれるエンジンです。導入手順などはこの記事を見ながらしたので、詳細は記事を見て下さい。

使い方

 軽い使い方は恐らく上記の記事の書かれた方がPDFとしてBoothで売られているので、それで解決するかとは思います。自分もそれを参考にしているので、著作権的にも実際のコードは載せません。
 ですがこの記事の意味がなくなりそうなので手順だけ書いています。といっても、リファレンスを見れば済む気がしますが(笑)

初期化

  1. physx::PXFoundationクラスの作成(バージョンはPX_PHYSICS_VERSIONを指定してください)
  2. physx::PxPvdクラスことPVD(PhysX Visual Debugger)の作成(PVDのダウンロードはこのサイトから1
  3. physx::PxPvdTransportクラスの作成
  4. physx::PxPvdとの接続(3で作成したクラスを使います)
  5. physx::PxPhysicsクラスの作成
  6. 拡張ライブラリを初期化(PxInitExtensions)
  7. physx::PxDefaultCpuDispatcherクラスの作成(物理演算に使うCPUのスレッド数を指定します)
  8. physx::PxSceneDescの設定
  9. physx::PxSceneの作成(重力・7のクラス・physx::PxDefaultSimulationFilterShader)
  10. physx::PxPvdSceneClientの作成
  11. physx::PxPvdSceneClient::setScenePvdFlag関数の設定(eTRANSMIT_CONSTRAINTS, eTRANSMIT_CONTACTS, eTRANSMIT_SCENEqUERIES のフラグをtrueに設定して下さい)
  12. physx::PxTolerancesScaleの設定(恐らくシーン上の単位を管理(メートルは1、センチメートルは100))
  13. physx::PxCookingクラスの作成

以下はGPUで物理演算をさせる時に設定する項目です。

  1. 8の時にphysx::PxCudaContextManagerDescをphysx::PxSceneDesc.cudaContextManagerに設定
  2. physx::PxSceneDesc.flags |= physx::PxSceneFlag::eENABLE_GPU_DYNAMICSに設定
  3. physx::PxSceneDesc.broadPhaseType = physx::PxBroadPhaseType::eGPU;に設定

更新

  1. physx::PxScene::simulate関数を呼ぶ(この更新頻度が高いと精度は上がりますが、負荷も上がります)
  2. physx::PxScene::fetchResults関数を呼ぶ(1の関数で物理シミュレーションを開始し、この関数で結果の完了を待ちます2

ジオメトリ

 作成出来るジオメトリの種類です。詳細はリファレンスを見てほしいのですが、軽く説明をします。

image.png
クラスはphysx::PxSphereGeometryです。

カプセル

image.png
クラスはphysx::PxCapsuleGeometryです。

ボックス

image.png
クラスはphysx::PxBoxGeometryです。

プレーン

image.png
クラスはphysx::PxPlaneGeometryです。

凸メッシュ

image.png

そもそも凸メッシュの説明をこのサイトの言い方で言うと

凸メッシュとは凹みや穴を持たない多面体です. 頂点座標を指定することで自由な形を作成することができます.

になります。このオブジェクトを作成しようとして、色々と穴にハマったので作り方の一例を紹介します。

main.cpp
    namespace px = physx;

    static const px::PxVec3 convexVerts[]{ px::PxVec3(0,1,0),px::PxVec3(1,0,0),px::PxVec3(-1,0,0),px::PxVec3(0,0,1), px::PxVec3(0,0,-1) };

    physx::PxConvexMeshDesc desc;

    mesh.setToDefault(); // 初期化

    desc.points.count = 5; // 頂点の個数
    desc.points.stride = sizeof(px::PxVec3); // 頂点のバイト単位のオフセット
    desc.points.data = convexVerts; // 頂点配列の先頭のポインター
    desc.flags = px::PxConvexFlag::eCOMPUTE_CONVEX;

    assert(desc.isValid());

    px::PxDefaultMemoryOutputStream buf;
    px::PxConvexMeshCookingResult::Enum result;

    assert(cooking->cookConvexMesh(desc, buf, &result));
    assert(result == px::PxConvexMeshCookingResult::Enum::eSUCCESS);

    px::PxDefaultMemoryInputData input(buf.getData(), buf.getSize());
    px::PxConvexMesh* convexMesh{ physics->createConvexMesh(input) };
    assert(convexMesh);
    px::PxConvexMeshGeometry mesh{};

    mesh.convexMesh = convexMesh;

    px::PxShape* mesh_shape{ physics->createShape(mesh, physics->createMaterial(0.5f, 0.5f, 0.5f)) };
    px::PxRigidDynamic* rigid_static
    { px::PxCreateDynamic(*physics, physx::PxTransform(physx::PxIdentity), *mesh_shape, 10.f) };

    scene->addActor(*rigid_static);

リファレンスをみているとaConvexActorってなに? となったので、直ぐに出来なかったり...。
デモにあるはずの関数がなかったりして、色々と苦労しました。

三角形メッシュ

image.png
 三角形メッシュと分かりにくい言い方ですが、.Fbxや.Objなどのファイルを読み込んで物理演算をさせる時に使うのがこのメッシュです。
これも同じく苦労したので、作り方を紹介します。というより作り方が凸メッシュと非常に似ているのですが(笑)

main.cpp
    namespace px = physx;

    physx::PxTriangleMeshDesc desc;

    desc.setToDefault(); // 初期化

    desc.points.data = /*頂点配列の先頭へのポインター*/;
    desc.points.count = /*頂点の個数*/;
    desc.points.stride = /*頂点のバイト単位のオフセット*/;

    desc.triangles.data = /*三角形配列の先頭へのポインター*/;
    desc.triangles.count = /*三角形の個数*/;
    desc.triangles.stride = /*三角形のバイト単位のオフセット*/;

    desc.flags = px::PxMeshFlags();

    assert(desc.isValid());

    px::PxDefaultMemoryOutputStream write_buffer{};
    bool status{ cooking->cookTriangleMesh(desc, write_buffer) };
    assert(status);

    px::PxDefaultMemoryInputData read_buffer(write_buffer.getData(), write_buffer.getSize());
    px::PxTriangleMesh* triangle_mesh{ physics->createTriangleMesh(read_buffer) };
    assert(triangle_mesh);

    px::PxTriangleMeshGeometry mesh{};

    mesh.triangleMesh = triangle_mesh;

    px::PxShape* mesh_shape{ physics->createShape(mesh, physics->createMaterial(0.5f, 0.5f, 0.5f)) };
    px::PxRigidStatic* rigid_static{ px::PxCreateStatic(*physics, physx::PxTransform(physx::PxIdentity), *mesh_shape) };

    scene->addActor(*rigid_static);

ハイトフィールド

image.png
そもそもハイトフィールドの説明をこのサイトの言い方で言うと

ハイト・フィールドは非常に多くの小さな3角形によって凹凸のある面を表現するものである。 凹凸の形状は濃淡画像(またはパレット・インデックス)を高さに変換することによって生成される。 Paint Shopなどの画像ソフトを使って、地形図を等高線で色分けして実際の地形を表現するといった使い方もできる。

になります。詳細はリファレンスを見て下さい。3

アクターと物理演算

 PhysXで扱うオブジェクトの事をカッコつけて「アクター」と呼んでいます。

動的・静的なアクター

 この欄はUnityなどを触っている方なら、分かり易いと思います。

  • Dynamic Actor: 物理演算を行うアクターです。通常はこのアクターを使用することになると思います。
  • Static Actor: イメージ的には物理演算を行わないアクターです。正確には物理演算を行いますが、一切動かないアクターになるので、通常は壁や地面になると思います。
  • Kinematic Actor 物理演算を行うアクターです。ただし、Dynamic Actorに対して一方的に影響を与え、自身については影響を受けません。

追加

 ジオメトリはphysx::PxCreateDynamicなどで作成しただけでは自動で物理演算してくれませんそのためにはphysx::PxSceneに追加しなければなりません。逆に言えば、あらかじめ作成しておいて後でシーンに追加するといったことも可能です。

情報のセット

Dynamic Actorに対し
+ 速度: setLinearVelocity
+ 加速度: setAngularVelocity
+ 位置+角度: setKinematicTarget変更する前にsetRigidBodyFlagKinematic Actorに変更する必要があります

setGlobalPoseについて
この関数は強制的に座標を変更する関数で、コメントにも書いてある通り、使用については非推奨です。これは、静的アクターの場合はパフォーマンスが低下する場合があり、動的アクターの場合は挙動がおかしくなる(他のアクターに重なった時やジョイント部分を引き離した時)からです。

情報の取得

  • 速度: getLinearVelocity
  • 加速度: getAngularVelocity
  • 位置+角度 getkinematicTargetgetGlobalPose

無効状態

 Dynamic Actorを無効化させることで、処理負荷を大幅に軽減させることが出来ます。無効化状態だと完全に静止状態になり、他のActorとの衝突があるまで処理が無効化されます。
+ 無効化: putToSleep
+ 有効化: wakeUp

削除

 ジオメトリを削除するにはphysx::PxSceneで削除することで自動で削除出来ます。PhysXのメモリー管理は参照カウントを使っているためです。ですが、参照カウントにはあまりいい思い出は無いので、出来る限り手動で削除したいとは思いますが...。

終了処理

 かならず、初期化で作成した全てのクラスは削除する(releaseを呼ぶ)ようにしてください。特にphysx::PxPhysicsは削除した際に、シーンに存在する全てのジオメトリを削除するのですが、トラブルを避ける為にもPhysX側に任せるのではなく、事前にジオメトリの削除を自分で実行した方が良いと思います。

最後に

 物理演算エンジンは入れたいけど難易度が高そうと思っている方がいるかもしれませんが、導入さえしてしまえば使うのは意外と難しくありません。他に難しくしている要素は日本語の記事がほとんどない事でしょうか?その問題もGoogle先生にかかれば割とどうにかなるので、試しに導入してみるのもありだと思います。


  1. PVDは物理演算の結果を視覚的に確認する事が出来ます。そもそもPhysX自体は物理演算をするだけなので、描画は一切関与していません。なので、正しく描画出来ているかを確認するソフトが必要なわけです。 

  2. このような形にしているのは非同期で物理演算を実行するからです。 

  3. 実際に使うことになったらコードを記述したいと思います。 

HnniTns
ゲームAIに興味がある専門学校生のゲームプログラマー。 主にC・C++でプログラムをしています(少しだけPython)。 ゆるく気ままにメモを兼ねて投稿していきます。 人間メモしないと忘れますし(笑) RecastNavigationをもっと広めたい!!
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away