前回までの勉強範囲で工業製品のほとんどの部品の動きをカバーできるようになったと思います。
しかし、生き物の脂肪部分の揺れや、紙や旗のたなびく様子、糸のたわみやゴム紐の伸縮など
まだまだ表現できないことは多々あります。
今回のイラストはソフトボディということで、星姫にもソフトなボリュームを与えてみました。
はいはい、こんなどうでもいいことは放っておいて本題に戻ります。
ソフトボディについて学びますが、これまでの物理計算と比べて計算コストがかなり増大します。
メッシュを切る粗さなどに気を配らないとリアルタイムレンダリングは難しくなるでしょう。
そんなことを念頭に、今回はソフトボディの実装方法について勉強します。
いきなりソースを読むには予備知識が足りないと思ったので、概要にちょっぴり触れてから始めようと思います。
ドキュメントを読んだ限りでは、ソフトボディを扱うにはこれまでの力学世界では不十分とのことでした。
ここでは btSoftRigidDynamicsWorld を使う必要がある、とあります。
また、btSoftBody クラスをオブジェクトとして取り扱わなければなりません。
これまでの剛体と大きく違う点は演算結果の情報が頂点ごとに求められるという点です。
そのほか、頂点ごとに mass(質量)を設定できたり、力を加えたりすることができるようになります。
指定方法はノード(頂点)のインデックスを指定するやり方のようです。
概要はこれくらいにして、さっそく具体的な実現方法というものを見てみましょう。
読んでみてどうでしたか?(今までと比べて、ちょっとコード量が多いですね、疲れたと思います。)
デモを実行した方は、いろいろなシミュレーションが行われることを確認したと思います。
以下はデモを実行したときのイメージです。
すべてを示しきれていないのですが、こうしたデモがローテーションで流れます。
では、一つ一つどのように実装しているのか確認していきましょう。
まずは初期化部分、ソフトボディを扱う力学世界の作り方を見てみましょう。
これまでの力学世界の作り方同様、通信係、大域処理、解法、衝突設定を指定して作成しています。
特に難しいことはなく btSoftRigidDynamicsWorld クラスを力学世界にすることでソフトボディを扱えるようになるようです。
デモはローテーションしている訳ですからそのローテーションしているコードを順に追っていきましょう。
で、みなさんもお気づきの通り次の関数名配列がローテーション一覧です。
では早速 Init_Cloth デモから順番に確認していきましょう。
デモの様子が以下です。
上から剛体が10個ほど降ってきて、広げたマットのようなもので受け止めます。
それだけなのですが、実装は以下の通りです。
btSoftBodyHelpers にはソフトボディを作成するいくつもの関数が用意されています。
今回のマットのような長方形の布は CreatePatch 関数を使って作れるようです。
特に頂点・インデックスバッファを用意する必要なく
4隅の頂点の座標とメッシュを分割する数、点が固定されているかどうかのフラグを指定して作成しています。
作成関数の最後の引数は bool gendiags とありますが、…説明が不足していますね。何の事だかわかりません。
とりあえずデモの通り true でいいのかな?
あと、4隅の頂点が固定されているか指定する方法だけど
これビットフラグですから、1,2,4,8,16,32 と2の累乗で指定する必要があります。
(どこにも説明が無いけど…せめて API リファレンスに書いておいてほしいですね。)
ああ、それと剛体が上から降ってくる件ですが、これは Ctor_RbUpStack 関数がやっています。
実装はこんな感じ↓、興味ある方は確認してみてください。
デモの様子
水風船みたいなものが登場し、その後、階段をのんびり転がります。
どんな実装なのでしょう、早速確認してみましょう。
さっき説明していなかったけど、 btSoftBodyHelpersの関数の最初の引数に
ソフトボディの世界情報を渡しています。
クラス名は btSoftBodyWorldInfo です。
重力加速度や、空気の密度、海面位置や水の密度などを指定する必要があります。
この辺はデモを参考にすると良いと思います。
CreateEllipsoid が楕円体を作成する関数です。(事前にメッシュを登録する必要はありません)
第2引数が中心位置、第3引数がXYZ軸方向の半径です。(今回は球体の設定ですね)
第4引数がメッシュの粗さを決めます、512頂点はちょっと多いかも
128頂点でも十分水風船のような振る舞いができていました。
設定しているマテリアルの説明ですが、m_kLST は粘性?を変化させました。
0から1の範囲で小さくするとサラサラ、中間値でトロトロ、大きくするとベタベタといった感じです。
btSoftBody のマテリアルにはこのほかに m_kALT, m_kVST がありますが
今回のデモには特に影響を与えないようです。
m_cfg.kDF は摩擦係数? 0でヌルヌルになり滑るようになります。1でペトペトする感じ(滑りません)
m_cfg.kDP 小さい値にすればするほど、ポヨンポヨン跳ねるようになります。
ダンピングの略なのかな? 1 にすると、空気抵抗が強すぎるのか空中にとどまります。
m_cfg.kPR これが圧力(プレッシャーの略?)だと思います。大きい値にすると膨らむし、小さい値にするとしぼみます。
単位は hPa か?
ここで説明した係数だけで、サッカーボールやバスケットボールなどを表現できると思います。
跳ねる、弾む楕円体を表現したい場合は参考になるデモになるでしょう。
しっかし、設定係数の説明文を書いておいてほしいなぁ。
略字になってて説明が無いなんてあり得ないでしょ!(どうやって調べればいいのさ!)
追記:見つけました、ソースコードにコメントとして添えられていました。
うーん Doxygen のルールに従って書いてくれればリファレンスに詳細が載って助かるのに…
デモの様子です。さっきのものとほぼ同じに見えますが何が違うんでしょう?
早速コードを追ってみましょう。
m_cfg.kVC は体積保存の係数のようで、0 から無限大まで取れますが、一定値より大きくすると爆発します。
setPose の第一引数で体積保存のフラグをオンにしている模様。
ゲームで弾性体の体積が気になるときは、このやり方を参考にテストしてみようと思います。
デモの様子です。
いくつものロープがそれぞれ別のわたみ方を見せています。
どうやって実装するのでしょうか、確認してみましょう。
btSoftBodyHelpers::CreateRope 関数でロープを作成します。
両端の初期位置を決めて、ロープの長さが決まります。
その間をいくつに分割するかを第4引数で指定します。
第5引数は端を固定する場合のビットフラグです。
ソースを読むことでわかってくることが増えてきました。(もっと早い段階で気付くべきだったよ…)
LST → Linear stiffness coefficient。剛性ということで、値が低いと伸びます。高いと引っ張っても全然伸びません。
(0を指定するとどこまでも伸びていくという面白い現象が見られます。)
m_cfg.piterations → Positions solver iterations ということで
より柔軟なロープを表現したければ小さい値を取れば良い模様。
糸、ロープ、ゴム紐の表現にはうってつけのソフトボディですね!
使う場面は多そうです。
デモの様子です。
今度はオブジェクトに糸をくくりつけていますね。どうやっているのでしょうか?
早速確認してみましょう。
注目すべきは btSoftBody::appendAnchor 関数です。
ボックスの開始位置とロープの端点の位置から、剛体に対してくくりつけるイメージです。
剛体とロープが離れていても大丈夫、しっかりアタッチします。
ブランコやつりさげた鉄球など、使われる場所が多そうな機能ですね。
覚えておこうと思います。
デモの様子です。
布が剛体にアタッチされています。
btSoftBody::appendAnchor 関数については、一つ上で解説しました。
参考にしてください。
布である以外、特に変わったところは無いですね。
デモの様子です。
見た感じ、垂直に伸びたロープを敷き詰めた剣山にボールを落下させているようです。
詳細はどうなっているのでしょうか?
上に伸ばしたロープであることには変わりはありませんが、注目すべきは
btSoftBody::generateBendingConstraints 関数です。
引数にX,Y,Z軸のビットフラグを指定するというかなり特殊な引数指定方法ですが
これでピンと直立するロープが完成します。
そのほかは特に目新しいものはありません。
林や毛の生えた場所にボールなどを放り込んだときのシミュレーションに使う感じでしょうか。
用途がぱっと思いつきませんが、覚えておこうと思います。
デモの様子です。
イカリングの鎖のような、ソフトボディのデモです。
TorusMesh.h に pVertices とgIndices として、あらかじめ用意されている頂点バッファとインデックスバッファがあります。
このメッシュ情報からトーラス形状のソフトボディを作成し、generateBendingConstraintsで固さを調整します。
重要なのが psb->m_cfg.collisions|=btSoftBody::fCollision::VF_SS; とハイライトした行です。
この行の宣言がないと、ソフトボディメッシュ同士の当たり判定が行われません。
まじめな話、女性の乳房の左右をそれぞれ独立したソフトボディとして作成したとします。
片方の乳を寄せたときに、もう一方にめり込んでしまったら興醒めです。
ソフトボディ同士の当たり判定を有効にする方法があるということを覚えておきましょう。
デモの様子です。
有名なスタンフォードバニーメッシュが3体上空から降ってきます。
トーラスがバニーに変わっただけの話ですね。
デモの様子。
四角い布が降ってきます。布と布がこすれる様子がリアルにシミュレートできています。
モデル作成が CreatePatch に変わった差分デモです。
m_cfg.collisions |= btSoftBody::fCollision::VF_SS;
のフラグ設定がソフトボディ同士の当たり判定に必要であるということが良くわかりました。
デモの様子です。
キューブが降ってきて、ロープの付け根に当たります。
ソフトボディといえど、付け根には剛体があるってことでしょうか?
m_cfg.kCHR=0.5; と設定していますが、これは剛体との接触係数でして
当たった時の跳ね具合を調整できます。
ソフトボディとリジッドボディとの当たりの調整ができることを覚えておこうと思います。
デモの様子です。
天使が羽ばたいた後の羽が舞う様子や、新聞小僧が号外をまき散らしたような光景を彷彿させます。
いくつも紙が舞っているシミュレーションです。実装はどうなっているのでしょうか?
一見ただの布の作成に見えるのですが、ハイライトしている部分に注目してください。
エアロモデルとあり、紙が舞う振る舞いを指定できるようになっています。
どんなものがあるか enum 定義も併記しておきました。
こういったシーンに納得がいかない場合は、いろいろ調整してみるのもありかも知れませんね。
デモの様子です。
カーリングのように一斉にスライドさせて、摩擦係数の低いものがどんどん
滑り続けているといったデモのようです。
Dynamic friction coefficient 略して kDF だそうです。
ソフトボディに振れた時にヌルヌルしているのか、ペトペトしているのか設定できます。
デモの様子です。
トーラスのソフトボディデモです。
特に新しい点はありませんが、下記のデモと見比べるのに役立ちます。
デモの様子です。
さっきと比べて、トーラスの弾力表現が違います。
何をどう変えたのか確認してみましょう。
なるほど、対応する頂点間の距離の総和を最小とするように変形前の形をマッチングさせて
マッチング後の頂点位置へ頂点が向かうように復元力を与えるという、弾性体表現の手法ですね。
巷では Shape Matching と騒がれている、高速に解けるが物理的に正しくないという代物です。(たぶん)
固くしたい場合は kMT = 1 を指定し、柔らかくしたい場合は kMT 値を小さくします。
高速に弾性体を表現したい場合に利用する価値があるでしょう。
デモの様子。
スタンフォードバニーですね。
特に新しいことはないはずです。
次の Shape Matching 手法と比較するために用意されている模様。
デモの様子です。
スタンフォードバニーが降ってきます。
前回のデモと比べて、変形の様子が異なります。
kMT の係数を設定することで、Shape Matching 法が有効になるようですね。
単なるスタンフォードバニーの Shape Matching デモです。
kMT 値を十分低くすると体積が保存されないといった
物理的に正しくないという Shape Matching 法のデメリットが露骨に見えてくることでしょう。
デモの様子です。
クリックすると、クリックした箇所の布が千切れます。
詳細を確認してみましょう。
カットフラグを立てると、次の関数を通るようになります。
この refine 関数の第3引数に true を指定すると指定した場所が千切れるようです。
m_impact と m_result については SoftDemo.cpp を見渡してみないとわからないです。
布が千切れる、破ける表現は 3D ゲームの自由度を上げる要素として、とても重要です。
必要になったら、ここを参考に実装してみようと思います。
デモの様子。
トーラスが出てきます。これまでのトーラスとの違いがわからない…のが良いことなのか?
m_cfg.collisions にフラグを設定する方法ですが、効果など資料が不足していて何も言えません。
とは言いつつも、変形の様子がよりリアルになった?(適当で申し訳ないです。)
デモの様子。
うーん、これまでの布のデモとの違いがわからないなぁ。
m_cfg.collisions にフラグを設定している件について布バージョンといった感じでしょうか?
詳細は不明です。(処理速度についても特に変化は感じられない?)
デモの様子です。
イカリングの鎖…、これまでのデモとの違いがわかりません。
ソフトボディ同士の当たり判定ができている様子をみると、結構すごいことができているようです。
資料不足のため、これ以上のことは言えません。
デモの様子です。
板とトーラスがくっついているデモです。
詳細を確認してみましょう。
剛体とソフトボディのトーラスが連結されています。
ハイライトしている行の appendLinearJoint 関数にて連結していることがうかがえます。
連結部の位置も指定できるようですね。これは便利!
デモの様子。
トーラスと板が妙な連結の仕方をしている…という感想しか出ませんでした。
詳細は次の通り
Ctor_ClusterTorus 関数はもういいですね、何度も書きましたから。
ハイライトしている行の appendAngularJoint 関数にて、いわゆるヒンジ結合を実現しているようです。
あまりこの連結は安定していないような印象を受けました。
デモの様子です。
車軸に沿って2つのトーラスが連結しています。
どうやって実現しているのか確認してみましょう。
ここまで見てきたヒンジ結合とリニア結合で、車軸連結と車輪の自由回転連結を実現しています。
なるほど、組み合わせることでこんな連結ができるようになるのですね。
参考にしておきたいと思います。
デモの様子。
車のデモです。
車輪に引き続き、どんな風に結合して実現しているのでしょうか?
何のことは無く、スタンフォードバニーに対して車輪結合を車輪の数だけ行っているコードでした。
階段と障害物を用意して実行しています。
(簡単なレースゲームシミュレーションならこれで十分?1フレーム5msくらいかかっていました。)
デモの様子。
マーズパスファインダーを思わせる3輪機構の台車です。
タイヤが球体と、なるほどロボットですか。
新しいなと思うところは、これまでのフラグ設定ではなくて
ハイライトしているように generateClusters(1) と関数で指定してクラスター?の設定をしている点です。
appendLinearJoint を使用して、台とタイヤの位置関係を一定に保とうとする結合方法はこれまでと変わりません。
デモの様子。
上から何個もトーラスが降ってきます。
ソフトボディ同士の当たり判定はしっかり行えていました。
コードについては特に付け足すところはありません。
デモの様子。
剛体とソフトボディのミックス当たり判定テストといったとことです。
正しく衝突がシミュレートできていました。
コードについては特に付け足すところはありません。
デモの様子。
こんにゃく?妙に柔らかそうなボックスの落下デモです。
これまでの物と比べて、中身がギュッとつまった物体の印象を受けました。
これを実現しているのがハイライトしている CreateFromTetGenData 関数なのでしょうか?
詳細は不明です。
デモの様子。
さっきのコンニャクみたいなの同様、今回はスタンフォードバニーのコンニャクです。
いや、本当にコンニャクのようなんだって!
今回もハイライトしている行が注目する箇所です。
引数で渡しているデータですが、あらかじめ用意されたバッファのようなもので
値を見ただけでは何なのかわかりませんでした。詳細は不明です。
今回は SoftDemo を通して様々なソフトボディの作成例を見てきました。
紙、布、肉、脂肪、糸、ゴム紐、ゴムボール、タイヤなど
基本的なソフトボディの作り方が具体的になったと思います。
次回はここまで学んだ Bullet Physics の基礎を使って簡単な物理ゲームを作ってみましょう。
それでは次回をお楽しみに!
2011/09/11 初記。
2011/09/19 更新。