PoseMirror開発で詰まった5つの不具合と解決策
投稿日: 2026年5月7日(2026年10月5日更新)
対象読者: Unity WebGL と AI の姿勢推定を組み合わせたアプリを作ろうとしているエンジニア
不具合内容
MediaPipe と Unity WebGL で PoseMirror(写真から3Dデッサン人形のポーズを作るアプリ)を作る途中で、次の5つの不具合に詰まりました。どれも修正時に数値を測って記録してあります。
- Firebase Hosting で、
firebase.jsonに書いたContent-Encodingが返らない - 写真から作ったポーズの肘の角度が、写真の見た目と平均 34° ずれる
- 人形の細い部位(腕・すね・つま先)を、クリックやタップで選べない
- つま先が足首から最大 157° 折れる
- 部位名を日本語にしたら、文字が「□」になる
全体の仕組みは 【Unity WebGL】MediaPipeで3Dポーズを動かす で説明しています。
原因(一言でいうと)
- Firebase Hosting が、大きいファイルの圧縮を自分で決めて返していた
- MediaPipe の3D座標(
worldLandmarks)の奥行きの誤差が大きかった - クリックの判定を、骨の細いコライダーにレイが当たったときだけにしていた
- 足首だけ可動域で角度を戻し、つま先はそのままにしていた
- Unity の文字表示(TextMeshPro)のフォントに、日本語の文字が入っていなかった
解決方法
- Unity の Decompression Fallback を有効にしてビルドする
- 画像上の座標から3D座標を組み直し、奥行きの z だけ 1/3 に縮める
- レイが外れたら、画面上でいちばん近い骨を選ぶ(寛容ピック)
- 可動域で戻したあとの回転を、足首とつま先の両方に使う
- 必要な文字だけを焼いたフォントを作り、元のフォントの予備として登録する
動作環境
| 項目 | バージョン |
|---|---|
| Unity | 6000.0.58f2 (WebGLビルド) |
| MediaPipe | Tasks Vision 0.10.0 (pose_landmarker_heavy) |
| 公開先 | Firebase Hosting |
不具合①:firebase.json に書いたヘッダーが返らない
解決方法の詳細
Unity の Player Settings → Publishing Settings → Decompression Fallback を有効にしてビルドします。ビルドに展開用の JavaScript が入るので、ヘッダーがどう返っても Unity のファイルを読めます。
原因詳細
再現: Unity のファイル(.unityweb)すべてに Content-Encoding: br を付けるよう firebase.json に書き、本番の応答を curl で取りました(2026年10月5日)。
| ファイル | 指定 | 実際の応答 |
|---|---|---|
.wasm.unityweb |
br | br(Firebase が圧縮し直したもの) |
.data.unityweb |
br | gzip |
.framework.js.unityweb |
br | なし |
Firebase Hosting は、指定したヘッダーではなく、自分の判断で圧縮して返していました。Unity が圧縮したファイルをブラウザに展開させる形は、Firebase Hosting ではまだ作れていません。詳しい設定は 【Firebase】Unity WebGLを無料で公開する手順 にまとめています。
不具合②:肘の角度が写真と平均34°ずれる
解決方法の詳細
MediaPipe の画像上の座標(landmarks)をピクセル単位に戻し、腰の中点を原点にして3D座標を組み直します。奥行きの z だけは 1/3 に縮めます。1/3 は、モーションキャプチャアプリ XR Animator の既定値と同じです。
const px = imageLandmarks.map(lm => [lm.x * imgW, lm.y * imgH, (lm.z * imgW) / 3]);
原因詳細
再現: 写真から作ったポーズ20件で、3D人形に付いた肘の角度を写真の見た目と比べました。worldLandmarks をそのまま使うと、平均 +34.1° ずれ、20件すべてで 15° 以上ずれていました。z を 1/3 にすると、平均 +10.3° まで減りました。
人形の書き出しファイル(BVH)から順に角度を計算し直しても同じずれが出たので、Unity 側の計算ではなく、worldLandmarks そのものがずれていると判断しました。x と y は画像上の位置そのものですが、z は1枚の写真から推し量った値なので、誤差が大きくなるのだと考えています。写真で比べた図は MediaPipe Pose 33ランドマーク完全ガイド に載せています。
不具合③:細い部位をクリックで選べない
解決方法の詳細
クリックした点からのレイが骨のコライダーに当たらなかったときは、画面上でクリックした点にいちばん近い骨を選ぶようにしました。拾う距離は画面の高さの4%(1080px の画面で約43px)にしたので、端末の解像度によらず同じ感覚で選べます。
if (Physics.Raycast(ray, out RaycastHit hit))
{
uiController.SelectPartByTransform(hit.transform); // 当たったらその骨
return;
}
PickNearest(screenPos); // 外れたら、画面上でいちばん近い骨
原因詳細
再現: 1080×1080 の画面の全 291,600 点で、実際の選択の処理を通して、どの点で骨が選ばれるかを数えました。骨が選ばれた点は、画面の 4.9% しかありませんでした。手は手首の点だけ、つま先は数ピクセルでした。寛容ピックを入れると 13.3% に増えました。
コライダーを太くする案も測りましたが、半径を2倍にしても 8.5% にしかなりませんでした。寛容ピックと併用すると、太いコライダーが先に当たって近い部位を奪うため、13.3% から 12.8% に減りました。
同じ調査で、腰と胸の判定が左に 43mm と 29mm ずれていることも見つかりました。コライダーの軸を、左側の子の骨(左脚・左肩)だけの方向から決めていたためです。左右の子の平均を軸にして直しました。
不具合④:つま先が足首から最大157°折れる
解決方法の詳細
足首を可動域で戻したあとの回転を、つま先にもそのまま設定します。
Quaternion world = ApplyJointLimit(footPart, ankleBone, rig.Fix(footPart, rotation));
SetBoneRotation(ankleBone, world);
SetBoneRotation(toeBone, world); // つま先にも、戻したあとの回転を使う
原因詳細
再現: 用意してあるポーズ369件すべてで、足首とつま先の角度の差を測りました。右のつま先は最大 157.1°(中央値 30.8°)、左は最大 156.6°(中央値 30.5°)、足から折れていました。修正後は、右が全件 6.4°(モデルの形による一定の値)、左が全件 0.0° です。
MediaPipe には、つま先だけを曲げた情報がありません。そのため足首とつま先には同じ向きを渡していて、本来は同じ回転になるはずでした。ところが、足首だけ可動域の制限を通し、つま先は通していませんでした。制限で戻した分だけ、つま先が足から折れていたわけです。つま先は手で直す部位の一覧から外してあるので、利用者には直せない折れでした。
不具合⑤:部位名を日本語にしたら「□」になった
解決方法の詳細
部位の一覧に使う文字(29文字)だけを、Noto Sans JP と Noto Sans SC(どちらも OFL ライセンス)から焼いたフォントを作りました。それを、元のフォント(LiberationSans SDF)の予備(フォールバック)に登録しています。増えたダウンロードは数十KBです。
文言を増やしたり変えたりしたときは、このフォントを作り直す必要があります。
原因詳細
再現: 部位の一覧を日本語・英語・中国語に切り替えられるようにしたところ、日本語と中国語の文字がすべて「□」で表示されました。
Unity の文字表示(TextMeshPro)の標準フォントには、日本語と中国語の文字が1文字も入っていません。日本語のフォントをまるごと入れるとダウンロードが大きく増えるので、使う文字だけを焼く形にしました。
同じ多言語化で、言語を切り替えるたびに、選んでいた部位が「腰」に戻る不具合も出ました。一覧を作り直すときに、選択位置が先頭に戻っていたためです。作り直す前に選択位置を控え、作り直したあとに戻して直しました。
まとめ
| 不具合 | 原因 | 解決策 |
|---|---|---|
| ヘッダーが返らない | Firebase が圧縮を自分で決める | Decompression Fallback を有効にする |
| 肘が平均34°ずれる | worldLandmarks の奥行きの誤差 |
画像上の座標から組み直し、z を 1/3 にする |
| 細い部位を選べない | 細いコライダーにしか当たらない | 外れたら画面上で最も近い骨を選ぶ |
| つま先が最大157°折れる | 足首だけ可動域で戻していた | 戻したあとの回転をつま先にも使う |
| 部位名が「□」になる | フォントに日本語が無い | 使う29文字だけのフォントを予備に登録する |
選べる範囲が画面の 4.9% しかないことも、つま先の折れが全369件で起きていたことも、数えて初めて分かった規模でした。コライダーを太くする案のように、もっともらしい直し方が、測ると逆効果だったこともあります。
この5つを直した版が、PoseMirror で動いています。
関連記事:
- 【Unity WebGL】MediaPipeで3Dポーズを動かす:実装全体の解説
- 【Firebase】Unity WebGLを無料で公開する手順:Firebase の設定と実測
- 【Unity×MediaPipe】脚や前腕が180°ねじれる原因:骨のねじれの不具合