会社のサイトのトップで three.js で動いている棒人間のマスコットを、Pixal3D で 3D にし、Mixamo で骨と既成の動きを付ける技術検証の3回目です。前回までに重みをいろいろ直しましたが、直すほど崩れが隣へ移り、自動のままの版に最小の直しを足した物でも、待機は良いのに、しゃがむ・跳んで体を縮める・着地する姿勢で崩れました。

第3回は、重みをいじるのをいったんやめて、なぜ深い姿勢で崩れるのかを確かめました。崩れには2種類あり、そのどちらも、重みや混ぜ方では消えませんでした。

結論

  • 崩れは2種類です。しゃがみと着地では、腿がお腹の中へ上がり、お腹の下の面が腿の前に薄い殻として突き抜けます。空中で体を縮めると、背中から脇腹の帯が最大 29 倍に伸びます。
  • 静止の断面を見ると、腿の通り道にお腹の下半分が先にあります。重みも混ぜ方も、今ある面の点をどの骨がどれだけ動かすかを変えるだけなので、この形の問題は消せませんでした。
  • 体積を保つ混ぜ方(デュアルクォータニオン)では伸びがかえって増え、補助の骨では悪くなりました。姿勢で効かせる補正の形(シェイプキー)なら、正面の被さりは消えましたが、横から見ると腰がくびれました。
  • 会社のサイトの three.js は線形の混ぜ方だけで、体積を保つ方式はありません。Mixamo の動きを Web に置くことは利用条件に明記が無くグレーだったので、使いませんでした。

試した環境

  • Blender 5.2.3(背景実行の Python)
  • モデル: 首を足して丸くした体型のマスコットを、Mixamo で自動の骨入れをした FBX(53,784 面)
  • 動き: Mixamo の跳ぶ(58 コマ)
  • 比べたコマ(骨の角度から選んだ): 1 = 立つ、11 = 踏み切り前のしゃがみ(腰が最も低い)、26 = 空中で膝を胸へ引き寄せる、40 = 着地のしゃがみ
  • 比べた版: 自動のまま(Mixamo の重み。UV の継ぎ目の頂点だけ繋ぐ)と、最小の直し(胴の重みを高さに沿って受け渡す・肩の骨の重みを背骨へ移す・お腹の下を 14% すぼめる)

一つつまずいたのが、モデルの前後の向きです。最初は別の流れで使った「正面は -Y」を当てはめ、お腹に当てるはずの補正をお尻の側に当てていました。足先の骨が +Y を向いていることで、このモデルは +Y が正面だと確かめて戻しました。前後を取り違えると、効いていない理由を別の所に探すことになるので、骨の向きで先に確かめるのが安全です。

静止の形と、腿の通り道

まず、静止した姿勢のまま、左の股関節の位置で体を縦に切り、立っているときの腿と、股関節を中心に前へ水平まで上げた腿を重ねました。

左の股関節で縦に切った静止の断面。前へ水平まで上げた腿の大半がお腹の輪郭の内側に入る

測った所身長に対して
股関節の高さ(股の付け根から上)7.7%
腿の長さ(股関節から膝)9.6%
お腹が腿の前の面より前に出る量7.3%

静止では、胴から脚へなめらかにつながり、切れ込みも折り返しもありません。ただ、股関節は洋梨形の胴の下半分の中に深く入っていて、腿は長さより太さ(直径)のほうが大きい形です。腿を前へ水平まで上げても、腿のほとんどはお腹の輪郭の内側に収まります。腿の通り道に、お腹の下半分が先にあるわけです。静止した絵だけを見ていると、これには気づけません。

崩れは2種類

伸びた面を数える

崩れの場所を見分けるために、骨を休めた形と動いた形で面ごとに大きさを比べ、3 倍を超えて伸びた面を数えました。数えた面を、その面の点を最も重く動かす骨ごとに分けると、直す所の当たりも付きます。要所は次のとおりです。

def areas():
    ev = mesh.evaluated_get(dg); me = ev.to_mesh()
    a = np.array([p.area for p in me.polygons]); ev.to_mesh_clear(); return a
arm.data.pose_position = 'REST'; dg.update(); a0 = areas()   # 骨を休めた形
arm.data.pose_position = 'POSE'
bpy.context.scene.frame_set(26); dg.update()
ratio = areas() / np.maximum(a0, 1e-9)
print((ratio > 3).sum(), ratio.max())   # 3 倍を超えた面の数と、最大の伸び

3 倍を超えた面を赤、10 倍を超えた面を黄で塗って見比べると、崩れが2つの種類に分かれました。

しゃがみ・着地: 腿とお腹のめり込み

しゃがみ(11)と着地(40)では、お腹の下の面が、腿の間から椀の縁のような薄い殻になって前へ出ます。この面は伸びておらず、塗り分けても赤くなりません。腿がお腹の中へ上がり、その場に残ったお腹の下の面が、腿の前に突き抜けて見えているのです。断面で見た、腿の通り道にお腹があるという形のとおりです。

空中で縮める: 背中から脇腹の帯の伸び

空中で膝を胸へ引き寄せる 26 では、伸びた面が背中から脇腹の帯(胸の塊と腰の塊の境)に集まり、最大で 29 倍でした。胴が胸(Spine2)と腰(Hips)の2つの固い塊に割られている、自動の重みの形によるものです。

伸びた面を赤と黄で塗った背中側の図。縮める姿勢で背中から脇腹の帯に集まる

自動のままでも、立った姿勢で 188 面が 3 倍を超えていました。お腹の点の中に、腰の骨と肩の骨の両方に付いた点がまとまってあり(51 点と 40 点)、動いていない姿勢でも面が引っぱられています。最小の直しは、しゃがみと着地の伸びを3分の1ほどにしましたが、縮める姿勢では 391 面が残りました。

体積を保つ混ぜ方は効かなかった

重みはそのままで、混ぜ方だけを体積を保つ方式(デュアルクォータニオン。Blender の Armature の Preserve Volume)に替えて比べました。3 倍を超えて伸びた面の数は次のとおりです(全 53,784 面中)。

体積を保つ混ぜ方に替えると、縮める姿勢と着地で伸びた面が増える。最小の直しは、しゃがみと着地で伸びを3分の1ほどにする

伸びは、縮める姿勢と着地でかえって増えました。縮める姿勢では 570 面から 980 面に、最大の伸びは 29 倍から 104 倍になりました。

動いた形で、交わり(頂点を共有しない面どうしが交わる組)も数えました。腕と体が触れる所なども拾う粗い目安です。

版立つ(1)しゃがみ(11)縮める(26)着地(40)
自動のまま1701,8711,4231,324
自動のまま+体積を保つ1331,7641,6991,524
最小の直し1941,6541,3961,264
最小の直し+体積を保つ1531,5691,5541,467

しゃがみでは交わりが 5〜6% 減りましたが、縮める姿勢と着地では 11〜19% 増えました。見た目でも、しゃがみで腿に被さるお腹の縁は、体積を保つ方式に替えても変わりませんでした。被さりが小さくなったのは最小の直しの版で、効いていたのはお腹の下をすぼめる直しのほうでした。

three.js は線形の混ぜ方だけ

そもそも、会社のサイトの three.js(0.183.2)は、骨の動きを重みで足し合わせる線形の混ぜ方だけで(skinning_vertex.glsl.js)、体積を保つ方式の実装はありません。版が古いせいでもなく、確かめた時点の最新版(0.186.1)でも、WebGL 用(skinning_vertex.glsl.js)と WebGPU 用(nodes/accessors/Skinning.js)のどちらも線形の混ぜ方だけで、設定もありませんでした。使うならシェーダーを自前で書き替えることになりますが、試していません。Blender で試した限り崩れは減らなかったので、書き替える理由もありませんでした。

上げた腿がお腹の中に入り、お腹の面が薄い殻のように前に残る様子

補正の形と補助の骨

最初の試しは狙いが外れていた

最初は、補正の形と補助の骨の効く範囲を股関節を中心に決めました。ところが、股関節は胴の内側の深い所にあるので、表面のお腹の点は範囲の端にしかかからず、補正の形で点が動いた量は最大でも身長の 0.19% でした。ほぼ動いていないので、この結果は使いませんでした。効く範囲を骨の位置で決めると、表面に届いていないことがあります。動いた量を先に測っておくと、効かない理由を取り違えずに済みます。

狙い直した物

お腹の下をすぼめると被さる縁が消えることは分かっていたので、最小の直しで 14% すぼめた、お腹の下の帯に当て直しました。どれも最小の直しの版に足しています。

  • 補正の形: お腹の下の帯を胴の軸のほうへ 50% すぼめる形(シェイプキー)。腿の骨の、腰から見た前への回転で効かせる(45° で 0、110° で 1)。最大で身長の 7.7% 動き、1% を超えて動く点は 5,215 点。効き方は、立つ 0・しゃがみ 1・縮める 0.68・着地 1。
  • 補助の骨: 左右の股関節に、腿の回転を半分だけ追う骨(Copy Rotation、影響 0.5)を足し、同じ帯の前半分の点の胴の重みを、最大 0.6 までそちらへ移す(0.3 を超える点は 1,403 点)。
  • お腹を常にすぼめる: 最小の直しの 14% を、30%・50% に強める(形の側で直す場合の当たり)。

補正の形の効き方は、最初は背骨と腿の骨の回転の差(Blender のドライバーの ROTATION_DIFF)で決めましたが、腿のねじれも拾ってしまい、縮める姿勢で 0 になりました。腿の前への回転だけで決め直すと、狙った姿勢で効きました。

結果

お腹を常にすぼめると、しゃがみと着地の被さる縁はすぼめるほど小さくなり、50% でほぼ消えました。ただ、立ち姿は、30% までは丸さがおおむね残り、50% では腰がくびれて丸さが減りました。

補正の形は、しゃがみと着地の被さる縁がほぼ消え、立ち姿は補正が 0 なので最小の直しと同じ丸い形のままでした。

深い姿勢を正面から比べた図。補正の形の列で、腿に被さるお腹の縁がほぼ消える

ところが横から見ると、深い姿勢で腰がくびれ、脇に放射状のしわが出ました。お腹の帯を一様にすぼめる形なので、正面の被さりは消えても、横に副作用が出ます。

深い姿勢を横から比べた図。補正の形の列で腰がくびれ、脇にしわが出る

補助の骨は、被さる縁が残り、お腹に縦の折れ目が出て、立ち姿にも筋が入りました。悪くなりました。

版伸び しゃがみ伸び 縮める伸び 着地交わり しゃがみ交わり 縮める交わり 着地
最小の直し983911321,6541,3961,264
+補正の形75224801,5581,5031,340
+補助の骨1193761461,6181,3011,442
常に 30% すぼめる69244831,6371,5421,494
常に 50% すぼめる54199641,6011,4271,361

交わりの数は、見た目の被さりと合いませんでした。被さりが消えた版でも大きくは減りません。体の内側に隠れた交わりも数えるためです。被さりは数ではなく絵で見る必要があります。

見立てと、改善に要りそうなこと

重みも混ぜ方も、今ある面の点をどの骨がどれだけ動かすかを変えるだけで、腿の通り道にお腹があるという形の問題は消せません。体積を保つ方式と補助の骨が効かなかったのは、この見立てと合います。一方、形を変えると被さりは消えました。常に形を変えると立ち姿が変わりますが、姿勢で効かせる補正の形なら、立ち姿を保ったまま、しゃがみと着地の被さりを消せました。このマスコットは Pixal3D に正面の絵1枚を渡して作ったので、胴の前後の厚み(身長の約 30%)、お腹が腿の前の面より前に出る量(身長の 7.3%)、洋梨形のお腹の下の膨らみは、どれも絵に描かれていない奥行きを Pixal3D が補った形です。その膨らみが作り違いかどうかは、意図した横の形と比べていないので確かめていません。

スクリプトで重みや形を直すやり方では、ここが限界でした。スクリプトで作った一様なすぼめは横から見ると腰をくびれさせるので、補正の形を、姿勢ごとに横の形まで作り込む必要があります。改善に要りそうなことは次のとおりで、どれも確かめていません。

  • 補正の形を、姿勢ごとに横の形まで手で彫る。
  • 体を一枚の面でなく、曲がらない部品(頭・胴)と曲がる部品(腕・脚)に分けて作り、付け根は重ねて隠す。お腹の面を骨の混ぜで曲げない。
  • 横の形も絵で渡す(正面と横の絵を Pixal3D に渡し、お腹の前と下の膨らみを絵で決めてから作る)。
  • 体型に合った動きを使う(腿を胸まで上げない動き、2〜3頭身向けに作られた動き)。今回は借りた動きを書き換えない前提で進めたので、試していません。
  • 直すときは、自動のままの版を土台に1か所ずつ足し、毎回同じコマで自動のままと並べて比べる。
  • three.js の形を混ぜる仕組み(モーフターゲット)を、腿の角度からコードで効かせる。仕組みとしてはできるはずですが、この流れで three.js で動かしてはいません。

Mixamo の利用条件(Web に置けるか)

サイトに入れるには、Mixamo の動きを Web に置いてよいかも確かめる必要がありました。

  • 公式の FAQ: キャラクターと動きは、個人・商用・非営利のプロジェクトにロイヤリティフリーで使える。例はイラスト・3D 印刷・映画・ビデオゲーム。Web に置く場合や、元のファイルを配ることには触れていない。
  • Adobe のフォーラム: プロダクトマネージャーの回答に「素材を配り直すのは利用条件に反する」。社員ではない人が載せた FAQ の写しに「できないのは、元のキャラクターと動きのファイルを配ることだけ」「客やチームの外の人にファイルを渡してはいけない」。

サイトに置くと、動きのデータ(FBX や GLB)は、ページを開いた誰でも取れる形になります。それが元のファイルを配ることに当たるかは、どこにも明記が無くグレーでした。グレーなので、使いませんでした。

まとめ

  • 深い姿勢の崩れは、腿とお腹のめり込み(しゃがみ・着地)と、背中の帯の伸び(縮める)の2種類だった。
  • 腿の通り道にお腹がある形の問題なので、重み・体積を保つ混ぜ方・補助の骨では消えなかった。
  • 姿勢で効かせる補正の形は正面の被さりを消したが、一様なすぼめでは横に副作用が出た。
  • 伸びた面を数えると場所の当たりは付くが、被さりの良し悪しは絵で見る。

確かめていないこと

  • 補正の形を姿勢ごとに横の形まで作り込んだら、横のくびれとしわを出さずに被さりを消せるか。
  • three.js でモーフターゲットを腿の角度から効かせて、Blender と同じに見えるか。
  • お腹の下を削る・脚の付け根を胴の外へ出すなど形を変えたときに、可愛い比率を保てるか。

おわりに

第3回で分かったのは、崩れの元が重みの付け方ではなく、腿の通り道にお腹があるというマスコットの形(正面の絵1枚から Pixal3D が補った、お腹の前と下の膨らみ)にあるらしい、ということです。ただ、これは見立てです。次回は、普通の人の比率のマネキンで同じ手順を試し、人の比率なら崩れないのかを確かめます。

※ three.js の実装、Blender の機能、Mixamo の FAQ とフォーラムの内容は執筆時点の情報で、いまは変わっている可能性があります。