布シムな話(後編)
究極の髪にも応用可
Write: 05/12/16 UpDate: 08/05/25

それでは当初の目的通り、スカートを作ってみましょう。
スカートを作るためには、布シムに足との衝突判定を組み込む必要があります。
大量の格子点(重り)と足との接触を判定しなければならないので、なるべく計算の楽な方法を取る必要があります。
今回は、重りを点、足をカプセルとみなして判定を行うことにします。
カプセルとは円柱の両端に半球をくっつけたものです。
カプセルは線分と半径rで定義されます。線分からの距離がr以内の部分がカプセルになるわけですね。
キャラクタはボーンでアニメーションをつけます。
腰から膝までのボーンと、膝からかかとまでのボーンを使って足を動かすことにします。
ボーンを円柱の軸とみなせば簡単にカプセルの位置が計算できます。
アニメーション関係のソースはこちらの記事のものを流用しています。
衝突している重りに対しては足にめり込まないように足の外に押し出す力をかけます。
物理シム業界ではペナルティ法と呼ばれる方法です。
円柱の表面と重りとの間にバネを入れるだけで実現できます。
要はめり込んだ重りをバネの力で押し返すわけです。
ついでに重りのエネルギーを削るためにダンパを入れておくと動作が安定します。
ペナルティ法を使うことで、衝突判定が入る以外は今までの布シムの枠組みをそのまま流用できます。
カプセルと点の衝突判定についてWeb上に良い資料が見つからなかったので、補足しておきましょう。
衝突は円柱状の部分と両端の半球部分に分けて考えます。
衝突判定する点を点xとします。(以下、a,b,x,yは3次元ベクトル、sはスカラー)
点xから線分に下ろした垂線の足yを求めます。
線分の両端の座標をa,bとします。
点yはa,bを通る線上にあるのでy=a+s(b-a)として表されます。
垂線はその線と直交しているので、
(y-x)・(b-a)=0 (・は内積)を満たす必要があります。
未知数はsだけなので、この式は簡単に解けます。
sが分かれば点yが求まり、これで円柱との判定ができます。
点yが線分上に存在して、かつ点xと点yの距離r'がrより小さければ円柱と衝突しています。
r-r'が円柱表面から点xまでの距離になるので、この値をペナルティ法で使わせてもらいましょう。
円柱と衝突していなかった場合は、点と球との判定を行います。
こちらは点a,bと点xの距離がr以内であるかという判定をするだけなので簡単です。
蛇足ですが、カプセルと球(半径r'')との判定も同じ原理です。
上記のrをr+r''に置き換えるだけで済みます。
この辺のお手軽さが、衝突判定においてカプセルが使われる理由なのでしょう。
というわけで、今回のソースです。
足のシルエットが布越しに見える感じはなかなか良いんじゃないでしょうか?
とは言え、スカートと言うには少し布がビロビロしている感じです。
正直、パラメータの設定が難しいですね。
この布は重りの上下左右方向と対角方向の2種類のバネしか入れていないので、
もしかすると3種類目を入れると改善するのかもしれません。
スカートの形状が足に密着した長い円柱形状というのも宜しくないです。
現実世界では、そんなタイトロングなスカートでは足を大きく動かせません。
円錐形状にするか、膝の辺りまでスリットを入れるかすると、もう少しリアルになるかと。
プログラムを簡単にするために、一本のバネについて同じ計算が2度行われています。
実際にゲームで使う際には改良しておいたほうが無難でしょう。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。

# Microsoft DirectX SDK (March 2008) にでコンパイルし直した、バイナリ&ソースです。
# プロジェクトはVisualStudio2008用になります。

布シムな話(中編)
やっと使い物になりました
Write: 05/12/11 UpDate: --/--/--

前回はオイラー法を使って布シムを作ってみました。
オイラー法は誤差が大きいのでパラメータ設定を誤るとすぐに布が千切れ飛んでしまいます。
誤差が出るのは仕方のないこととして、陽的公式でなるべく正確に計算しようとする努力がなされてきました。
2次のルンゲ・クッタ法(RK2)、4次のルンゲ・クッタ法(RK4)と呼ばれる方法が有名です。
数学的に言うと、ある関数をテイラー展開した式をいかに近似するか、という話になります。
大学で理数系に進めば一年生のときに出てくる話です。
オイラー法ではテイラー展開の1次項まで、RK4ではテイラー展開の4次項までの近似になります。
布シムが4次まで微分可能なのかという疑問については…???です。
とりあえずRK2を使うよりはRK4を使う方が良いようです。
まぁ難しい話は脇に置いて、シム業界ではRK4を使っておけば文句を言われないという不文律があるので従っておきましょう。
RK4はオイラー法と同等の計算を4回行なって、それらの結果を足し合わせて用います。
一見計算量が4倍になるように思えますが、計算精度が高いため、
時間刻み幅のdtを大きくすることができ、総合的にはRK4が勝つと言えます。
かくしてとりあえずRK4を使っとけという風潮があるわけです。
しかし、オイラー法が使い物にならないというわけではありません。
前回紹介した論文『Decomposing Cloth』によると
オイラー法と後退オイラー法を同時に使う、FBオイラー法という方法があるそうです。
速度を求めるときはv(t+dt) = v(t)+a(t)dt のように現在の加速度に対してオイラー法を適用します。
位置を求めるときはx(t+dt) = x(t)+v(t+dt)dt のように未来の速度に対して後退オイラー法を適用します。
後退オイラー法を使っているとはいえ、v(t+dt)は既にオイラー法により計算されているので、
現在の状態のみから計算できる陽的公式の一種といえます。
試してみるとこれが、意外と使えます。
dtをRK4と同じくらいに大きくしても比較的安定した計算結果が得られます。
それでいて計算が簡単というのはなかなか美味しいですね。
難しく言うと1次のシンプレクティック法ということらしく、オイラー法に比べてエネルギーが保存されやすいんだとか。
それなら、2次のシンプレクティック法を使えばもっと良い結果が得られるのでは?ということで、
これまた前回紹介した『システム数理IV』を紐解くと、リープフロッグ公式というものが紹介されています。
こちらのサイトの方が説明が簡単かもしれません。
幾つかの式変形が見られますが、FBオイラー法そっくりな形が含まれています。
実に面白いです。時間の進め方の微妙な違いが精度に効いているんですね。
今回の布シムようにダンパを入れている場合にはシンプレクティック法の
エネルギーが保存するという特長はあまり発揮されませんが、
ダンパ定数を小さくし過ぎた場合(究極的には定数=0)に、エネルギーがどんどん増加するという傾向が少ない分、
長時間計算しても布が千切れ飛びにくい(多分…)ので便利な方法だと思います。
シムの専門家ではないので、真偽のほどは分かりません。

というわけで、今回のソース
オイラー法とFBオイラー法、RK2、RK4、リープフロッグを切り替えたり、
パラメータを書き換えたりしてお楽しみください。
リープフロッグについては、速度と位置の計算結果が時間的に互い違いに算出される関係で、
空気抵抗の計算にイカサマが入っています。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。
いずれにしろ各計算方法の理解に怪しい部分があるので、参考程度ということで宜しくお願いします。
間違っていたら、こっそりご指摘ください。

布シムな話(前編)
古典的実装です
Write: 05/12/11 UpDate: --/--/--

ふと、Cloth simulation(布シム)を作ろうと思い立ったよ企画。
ボーンベースのアニメーションだと柔らかい物体を表現するのが難しいので、
どうしてもゲームキャラはタイトな服をまとう事が多くなります。
しかし、野村XXさんはロングスカートがふわふわひらひらするのが大好きなご様子。
大量のボーンをスカートに仕込んで手付けでアニメーションさせるという手もありますが、
これは人力にものを言わせた荒業と言えるでしょう。
他には3Dソフト上で布シムを計算して、結果をもらって来るという手もあります。
この場合布を構成する頂点数 x フレーム数のモーションデータを全てメモリにおいておくというのも大変ですし、
他のモーションに遷移する際に、モーションをブレンドするのも計算量が馬鹿になりません。
両者を合わせた技も考えられます。
ボーンを仕込んだスカートと、事前に計算した布シムの結果を照らし合わせて、
後者の結果に近づくようなボーンのパラメータを求めるという方法です。
これは、大雑把な近似としてはうまく動きそうな気がします。
ゲームで使う分には十分かもしれません。
しかし、事前に計算されたモーションでは、風向きが変わるというような環境の変化に対応し切れません。
やはり風にはためいてこそのロングスカートです。
前置きが長くなってしまいました。今回は、定番のバネマス系の布シムの話です。
格子状に配置した重りをバネでつないでゆらすと、ビヨンビヨンする様が布に似ているという手法になります。
まとまった説明としては、こちらのサイトが分かりやすいです。
論文の一つでも読んでやろうかという剛毅な方はこちらとかこちらなんかどうでしょうか。
バネの仕込み方がミソで、1: 重りの上下左右に接続するバネと、2: 重りの対角方向(右上、右下、左上、左下)、
3: 重りを一つ飛ばして上下左右に接続するバネ(1のバネの二倍の距離で接続される)、の3つを用います。
論文に言わせるとバネの強さは 1 > 2 >= 3 といった所です。
最低、1と2を使っておけば、布っぽく見えます。
3に関してはweak nonlinear bendといっているのでフックの法則に従わないバネを想定しているようです。
ちなみにフックの法則は
f=k(x-xo)
f: バネに加わる力 k: バネ定数 x: 伸びたバネの長さ xo: 伸びる前のバネの長さ
ですね。
重りとバネを仕込み終わったら、シミュレーション開始です。
シミュレーションとは、要するに時間を進めてあげるということです。
布にまったく力がかからなければ、時間をいくら進めても変形しません。当たり前ですね。
布にかかる力としては、重力、空気、他の物体との接触、布同士の接触、etc...と色々あります。
とりあえず前者2つを考えましょう。
重力は常に一定の力を重りに与えます。
f = mg
f: 重りに加わる力 m: 重りの重量 g: 重力加速度
です。
世の中にはエネルギー保存の法則なるものが存在するので、重りとバネと重力だけでは、永遠に振動し続けます。
本物の布はそんな動きはしないので、どうにかして振動を止めなければなりません。
そこで空気抵抗(ダンパ)を導入します。
ダンパは速度の逆向きに働く力です。
f = -dv
f: 抵抗力 d: ダンパ定数 v: 速度
バネ自身にダンパ項を導入するモデルも存在します。
その辺りはお好みでといった感じです。これで、布に加わる力は分かりました。
ある重りに対してバネと重力と空気によって働く力の総和をFとすると、
ニュートンの運動方程式から加速度が計算できます。
a = F/m
a: 加速度 m: 重りの重量
加速度が分かれば、加速度の積分が速度、速度の積分が位置であることから、
速度と位置が計算できます。
中学の授業で習ったはずです。
dv/dt = a
dx/dt = v
t: 時間 v: 速度 x: 位置

ここで、深遠な疑問にぶち当たります。
『どうやってその積分を求めるの?』という問題です。
これが実に難しい。あまりに難しいので偉い学者さん達が何年も研究しています。
まとまった資料としてはこちらがお勧めです。
基本になるのは、オイラー法と呼ばれる手法です。
ある時刻tにおける位置と速度をx(t),v(t)とすると、次の時刻t+dt(dtは非常に短い時間間隔)に
おける位置はx(t+dt) = x(t)+v(t)dtで計算できるという考え方です。
道を歩いている場合、次の一歩がどの辺りに着地するかというのは、
現在歩いている速度と、現在の位置からおおよそ推測が付くでしょ、ということですね。
このように現在の状態の値を使って未来の状態を計算する方法を陽的公式といいます。
一方、後退オイラー法と呼ばれる、x(t+dt) = x(t)+v(t+dt)dt のように速度の未来の状態を使って計算する方法もあります。
こちらの一派は陰的公式と呼ばれます。
大雑把に言うと、未来の状態を未知数とした方程式を解くことになります。
しかし、計算が大変になるので、リアルタイムで処理をするのには向きません。
陽的公式には少しずつ未来を計算していくうちに誤差が溜まるという問題があります。
例えば、今日の自分の延長で明日の自分は予測が付きます、
明日の自分から明後日の自分も大体予測が付きます。
これを繰り返していけば10年後の自分だって予測が付くはずです。
とは言え、まず間違いなくその予測は当たらないでしょう。
布シムの場合、陽的公式では計算誤差で激しく揺れまくるという結果が出がちです。
ちなみに陰的公式ではそのような羽目になりづらいという利点があります。

というわけで、今回のソース
色々とパラメータを書き換えてお楽しみください。
見た目を楽しくするために、布に対して風っぽいものが吹いています。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。
オイラー法の精度の関係から、dtをかなり小さくしなければならず、動作は遅いです。
これを実用的にするにはどうすれば良いのかという話が次回に続きます。

続BEEPでMIDIな話
果たして利用者はいるのか?
Write: 05/11/24 UpDate: --/--/--

乗りかかった船ということで、BEEPでMIDIなドライバを書きました。基本的な部分はVorbis再生のドライバVoxと互換です。
なんとなく名前もVoxに合わせて、BoM(BEEP on MIDI)になりました。
相変わらず英語的には微妙…
下の記事で5チャンネルくらいなら鳴らせるよね、と書いていますがチューンの結果、16チャンネル全て鳴らせるようになりました。
ついでに95系のWindowsもサポートしました。
95系はVirtualPCで動作確認しただけなので、実機だとちゃんと動くのか分かりません。
試してみた方は、是非ご一報下さい。
というか、WinXP上でPCエミュレータを走らせて、Win95を動かし、そこで鳴っているBEEP音は
サウンドカード上でエミュレートされていて、ピコポコ音のMIDIを奏でているという謎の状態が笑えます。
タイミング調整をもう少し真面目に行うようにしたのと、曲中でテンポが切り替わるのにも対応したので、
大抵のMIDIはそれなりに聞けると思います。
WindowsがマルチタスクOSである以上、タスク切り替えが発生するので、再生中に音が途切れるのは諦めてください。
サウンドカードのようにバッファリングして鳴らせるわけではないのでコレばかりはどうにもなりません。
ソースはこちら。ヘルプはVectorのサイトから拾ってください。
単に、BEEP再生を聞いてみたいだけの方には、超シンプルなプレイヤーを用意しました。
こちらのプレイヤーと聞き比べてみると、再生方法のポリシーの違いが垣間見られて、
楽しいかもしれません、私だけ?

BEEPでMIDIな話
なんかこう涙を誘う音色です
Write: 05/11/14 UpDate: --/--/--

私の友人がここ数年に渡ってPC9801のエミュを書いていて、たまたまBEEP音楽の話が出てきました。
PC88とかPC98ではお世話になった人も多いんじゃないでしょうか。
FalcomとかT&Eあたりは結構、まともな音楽に聞こえていたような気がします。
まぁ、かく言う私はMSXユーザだったので、PSGの澄んだ音色の世界に居たわけですが。
分かる人にしか分からないというか、分からない人にはまったく分からない、
BEEP音楽の世界にいざなおうというのが今回の趣旨です。
こちらにすでに立派なソフトがあるので、単にこの世界に浸りたいだけなら一発終了です。
何かのソフトに組み込みたいという貴方は、引き続きお読み下さい。
レトロ風味なゲームを作る際には、役立つんじゃないでしょうか。
音ネタはMIDIを使います。
初めてMIDIフォーマットを調べてみたのですが、さすがに80年代の規格だけあって、微妙に貧乏臭いです。
数値を可変長フォーマットで記述したり、連続して同じコマンドが発行される場合は、
ステータスバイトを省略したりして、ファイルサイズの圧縮を図っています。
そんなことをしても数十KByte程度のデータがちょっと小さくなるだけなんですけどね。
MIDI規格については、こちらこちらに分かりやすく書かれています。
ビッグエンディアンであることにさえ気をつければ、フォーマットの解析は楽勝です。
色々コマンドはありますが、今回必要なものはノートオンとノートオフ、あとはテンポの指定だけです。
音色なんか指定されても、所詮BEEPなので再現のしようがありません。
ノートオンで音を発生させ、ノートオフで音を止めます。
ノートオンではノートナンバーが指定されます。
この数字は60がC3、つまり"3オクターブ目のド"を表します。
ネットで調べるとC4の場合もあるとかいう記述を見かけましたが、そんな適当で良いんでしょうか?
それはさておき、"ドレミファ" で記述されているMIDIをBEEPで鳴らすために、周波数に変換しなければなりません。
A3(ラ)が440Hzとして、これを基準に音階を作ります。
西洋音楽世界では1オクターブ上がると周波数が2倍になります。
1オクターブは12音階で作られるのですが、今回は1オクターブを等比で12分割してあげます。
いわゆる平均律という奴ですね。
調律には色々な主義主張があるので、暇な人はこのあたりのサイトでお勉強してみるのも良いかと。
これでMIDIを鳴らす準備はできましたが、ここで問題になるのは和音の扱いです。
BEEP自体は単音しか鳴らせません。
そこで、単音を細かく区切って、音階を変えながら鳴らし続けると、なんとなく和音ぽく聞こえるというテクニックを使います。
"ドミソ"の和音を"ドミソ"の3連符に置き換えるようなものですね。
原理的には細かく区切れば区切るほど良いはずですが、 Windowsでそんなにシビアな時間管理はできません。
この手のテクニックの究極、1bitDAによるWav再生なんて古典テクニックは、Windowsではまず再現不能でしょう。
大雑把に試してみたところ、単音一つ当たり10ms程度が妥当のようです。
5重和音まで出そうとすると、50ms必要になり、4分音符がざっと500ms相当なので、16〜32分音符程度までは表現できます。
MIDIは16チャンネルまで再生できる規格なので、5重和音では表現力が足りないものの、
メロディーラインは大抵、数字の低いチャンネルに入っているので、数字の低い順に5チャンネル分再生することにします。
そんなこんなで、今回のソース
コマンドラインから
MIB test.mid
のように 実行してください。
BEEPを鳴らすのに用いている関数が、NT系でないとまともに動作しないのでWin95やWin98は不可です。
ちなみにMIBはMen In Blackでは無くて、MIDI In BEEPの略です。
英語が破綻しているのでは?というツッコミは無視です。
いつものことながら、エラーチェックは最低限であり、
このサンプルの使用によるいかなる不幸も当方では責任を負いかねます。

 


戻る