Skip to content

第10回 スキンメッシュ基礎

前回: 第09回 影表現の基礎 / 次回: 第11回 行列と座標変換1

キャラクターは どう変形しているか

メッシュを 骨格に合わせて動かす技法

頂点変形の 基本の理屈

前回の振り返り

影は深度比較で作られていました

  • 第09回では、ライトから見た深度を Shadow Map として保存し、 カメラから描画するときに影判定へ使いました
  • Shadow Map はライト視点の Depth Buffer のようなもので、 現在の点がライトから見えるかどうかを比較します
  • Bias、Resolution、Shadow Distance は、影の安定性と品質に影響しました
  • 今回は、描画される前の頂点がどのように変形し、 キャラクターの動きになるのかを扱います

今回の授業で扱うこと

キャラクター変形の基本構造を理解する

  • スキンメッシュとは何か
  • ボーン、ジョイント、階層構造
  • ボーンウェイトと頂点の影響度
  • バインドポーズの役割
  • スキニング計算の考え方
  • Unity の SkinnedMeshRenderer
  • Shader Graph をキャラクターへ適用するときの注意
  • メッシュ、ボーン、マテリアルの関係を切り分ける

今回の授業内容

スキンメッシュの基礎

  • 静的メッシュとスキンメッシュ
  • ボーンと階層構造
  • ウェイトとウェイトペイント
  • バインドポーズ
  • スキニング計算の概要
  • Unity での SkinnedMeshRenderer
  • Shader Graph とキャラクター適用
  • 授業内課題

スキンメッシュとは

ボーンに合わせて変形するメッシュ

スキンメッシュシェーダーの比較
スキンメッシュの例 ボーンの動きとウェイトを使い、メッシュを変形する

  • Skinned Mesh は、ボーンの動きに合わせて 頂点位置が変化するメッシュです
  • キャラクターの腕、足、顔、服などは、 多くの場合スキンメッシュとして扱われます
  • 1つの頂点は、1本のボーンだけではなく 複数のボーンから影響を受けることがあります
  • その影響度を使って、関節周辺をなめらかに 曲がるように見せます

静的メッシュとの違い

頂点位置が毎フレーム変わる

種類主な用途頂点の扱い
Static Mesh背景、小物、床、壁基本的に形が変わらない
Skinned Meshキャラクター、服、変形する体ボーンに合わせて変形する
  • 静的メッシュは、モデルの頂点位置をほぼそのまま描画できます
  • スキンメッシュは、アニメーション中のボーン姿勢に合わせて 頂点を変形してから描画します
  • シェーダーから見ると、キャラクターは 毎フレーム形が変わるメッシュ として扱われます

なぜボーンを使うのか

すべての頂点を手で動かすのは難しい

  • キャラクターの全頂点を、アニメーションごとに直接編集すると データ量も作業量も大きくなります
  • ボーンを動かし、頂点がどのボーンへ追従するかを決めれば、 少ない制御点で大きな形状変化を作れます
  • ボーンは画面に表示するための形状ではなく、 メッシュを変形するための制御構造です
  • リギングでは、メッシュ、ボーン、ウェイトの関係を作り、 アニメーションで動かせる状態にします

ボーン

メッシュを動かすための骨格

  • Bone は、メッシュを変形するための Transform です
  • ボーンは単独で置かれるのではなく、 通常親子関係を持った階層として作られるます
  • たとえば肩を動かすと、上腕、前腕、手首、指も一緒に移動します
  • それぞれのボーンは位置、回転、スケールを持ちます
  • この変換の連鎖は、次回扱う行列と座標変換につながります

ジョイントとボーン

関節の位置を意識する

  • DCCツールやエンジンでは、ジョイント、ボーン、Transform という 言葉が文脈によって使い分けられます
  • 授業では、頂点へ影響を与える骨格要素をまとめて ボーンと呼びます
  • 大事なのは名前の違いではなく、 どのTransformが、どの頂点を、どのくらい動かすか です
  • 関節位置がずれていると、肘や膝が不自然に潰れたり伸びたりします

ボーン階層の例

子は親の変換を受ける

text
Root
  Hips
    Spine
      Chest
        Shoulder_L
          UpperArm_L
            LowerArm_L
              Hand_L
  • Hand_L の最終的な位置は、手首だけでなく 腰、背骨、肩、腕の変換にも影響されます
  • このような階層変換を正しく扱うために、 3Dグラフィックスでは行列が使われます
  • 今回は計算の全体像に触れ、詳細は次回扱います

ボーンウェイト

どのボーンにどれだけ従うか

左腕のボーンウェイト表示
左手のボーンの影響度 赤いほど影響が強く、青いほど影響が弱い

  • Bone Weight は、頂点が各ボーンから受ける 影響度です
  • 1本のボーンに完全に追従する頂点もあれば、 複数ボーンの動きを混ぜる頂点もあります
  • たとえば肘の近くの頂点は、上腕ボーンと 前腕ボーンの両方から影響を受けます
  • 影響度の合計は、通常 1.0 に なるように扱います

ウェイトの例

合計が 1.0 になる影響度

頂点上腕前腕結果
肩に近い頂点1.00.0上腕に完全追従
肘の中央0.50.52本の動きを半分ずつ混ぜる
手首に近い頂点0.01.0前腕に完全追従
  • ウェイトが急に切り替わると、関節が折れたように見えます
  • ウェイトをなめらかに変化させると、曲がり方もなめらかになります
  • ただし広く混ぜすぎると、動きがぼやけて形が締まりません 通常は、1頂点あたり最大4本程度のボーン影響を扱います

ウェイトペイント

変形の品質を作る作業

  • Weight Paint は、頂点ごとのボーン影響度を 色やブラシで編集する作業です
  • 赤や白に近いほど影響が強く、青や黒に近いほど 影響が弱い表示がよく使われます
  • 関節、肩、腰、指、顔の周辺は、 ウェイトの調整で見た目が大きく変わります
  • シェーダーだけでなく、モデルデータ側の品質も キャラクターの見た目に直結します

よくある変形の問題

原因はシェーダーだけとは限らない

  • 肘や膝が潰れる
  • 肩を上げると胴体まで引っ張られる
  • 指を曲げると隣の指も動く
  • 服や髪が体に追従しすぎる、または追従しない
  • アニメーション中にメッシュが消える

これらは、ウェイト、ボーン階層、Bounds、マテリアル、 シェーダー設定のどこかに原因がある可能性があります。

バインドポーズとは

メッシュとボーンの初期状態

Tポーズのバインドポーズ
典型的なTスタンスでの バインドポーズの例

  • Bind Pose は、メッシュをボーンへ関連 付けた最初の基準姿勢です
  • スキニングでは初期状態でボーンとメッシュを 対応させ、相対的な位置関係を記録します
  • アニメーションでの変形は、初期状態から どう移動したかで計算されます
  • たとえば腕を上げた姿勢でバインドされた モデルなら、そこから腕がどのように回転した かの相対変化を使って頂点位置を求めます

バインドポーズが必要な理由

ボーンの現在位置だけでは足りない

  • 頂点は、バインドポーズ時点でのボーンとの位置関係を持っています
  • 現在のボーン行列だけを使うと、 元々どの位置にあった頂点なのかを正しく扱えません
  • そのため、逆行列でバインドポーズを一度打ち消してから、 現在のボーン変換を適用するというやや面倒な計算が必要になります
  • この「基準から現在への変換」が、スキニング計算の中心になります

スキニング計算の考え方

ボーンの変形結果を重み付きで加算

  • 各頂点は、影響するボーンごとに変形されます
  • 変形後の位置にウェイトを掛け、 それらを足し合わせた結果が最終的な頂点位置 になります
  • 一般的には、1頂点あたり最大4本程度のボーン 影響を扱うことが多いです
  • 右は頂点シェーダーのHLSLコードの例です ウエイトの数だけループして、変形を加算して います
csharp
v2f vert(appdata v)
{
    // ボーンの重み付けによる頂点の変換
    float4 skinnedPos = float4(0, 0, 0, 0);
    float3 skinnedNormal = float3(0, 0, 0);

    float weightSum = 0;

    for (int i = 0; i < _NumOfWeights; i++)
        weightSum += v.boneWeights[i];

    float weightScale = 1.0 / weightSum;

    for (int i = 0; i < _NumOfWeights; i++)
    {
        int index = (int)v.boneIndices[i];
        float weight = v.boneWeights[i] * weightScale;
        skinnedPos += mul(_BoneMatrices[index], float4(v.vertex, 1.0)) *
            weight;
        skinnedNormal += mul((float3x3)_BoneMatrices[index], v.normal) *
            weight;
    }

    skinnedNormal = normalize(skinnedNormal);

    v2f o;
    o.pos = mul(UNITY_MATRIX_VP, float4(skinnedPos.xyz, 1.0));
    o.normal = float4(skinnedNormal, 1.0);
    o.color = float4(skinnedNormal, 1.0);
    return o;
}

法線も変形される

ライティングの見え方に影響する

  • スキンメッシュでは、頂点位置だけでなく法線も変形されます
  • 通常は頂点と同じく、ボーンの影響を受けて回転した後合算し、 正規化によって整えて使用されます
  • 法線が正しく変形されないとライティングが不自然になりますが 実際には物理的に正しい変形とは限らないため、曲率の高い箇所では 手動での調整が必要になる場合もあります

SkinnedMeshRenderer

スキンメッシュを描画するコンポーネント

  • Unity では、スキンメッシュの描画には SkinnedMeshRenderer が使われます
  • MeshRenderer と違い、ボーン、ウェイト など変形に必要な情報を扱います
  • キャラクターモデルに Shader Graph のマテリアルを適用する場合も、 実際に描画するのは SkinnedMeshRenderer です
  • 頂点の変形は SkinnedMeshRenderer が行い、ユーザーが実施する必要はありません Shader Graph は変形後の頂点に対してライティングなどを行います

SkinnedMeshRenderer が特別な理由

計算結果を再利用したい

  • スキンメッシュは、ボーンの変形に合わせて頂点位置が毎フレーム変わります 頂点位置をシェーダーで計算することになるわけですが、 頂点数が多いキャラクターでは負荷が大きくなります
  • Unity では、シャドウの計算などで同じモデルを複数回描画する必要があります その際に都度スキンメッシュの計算を行っていては負荷が増すため、 Compute Shader で事前に計算し、その結果をフレーム内で再利用しています
  • このため Unity ではユーザーは自分でスキニング計算を行う必要はなく、 逆にスキニングをカスタマイズする必要がある場合は全てを自己解決 しなければならならなくなります

Bounds

変形後の大きさを見積もる範囲

  • Bounds は、Renderer がそのメッシュを描画対象として扱う範囲です
  • カメラ外にあると判断された Renderer は、描画を省略されることがあります
  • スキンメッシュはアニメーションで形が変わるため、 Bounds が小さすぎると、まだ見えているはずの部分が消えることがあります このため、ある程度の変形を見込んで余裕をもった Bounds が設定されます
  • ただし 大きすぎる Bounds は余計な描画を増やすため、 適切な設定が必要です

Shader Graph を適用する

基本は通常のマテリアルを扱える

  • Lit Shader Graph のマテリアルは、 キャラクターの SkinnedMeshRenderer にも適用できます
  • Unity 側がスキンメッシュの変形を処理するため、 通常の色、テクスチャー、法線マップ、フレネル表現等が利用可能です
  • ただし頂点位置はスキニング完了後の値が渡されることになります あまり無いケースですが、メッシュの直接の座標が必要な場合は 別途工夫が必要になります

キャラクター向け表現の例

これまでの内容を組み合わせる

  • 第03回のテクスチャーとUVで、キャラクターの色や模様を扱う
  • 第04回の Alpha Clip で、髪、まつげ、服飾パーツを切り抜く
  • 第06回のノイズで、魔力、ダメージ、汚れ、発光の揺らぎを作る
  • 第08回のフレネルで、輪郭強調やバリア表現を作る
  • 第09回の影設定で、キャラクターの接地感を調整する

スキンメッシュは、これらの表現を 動くキャラクターへ適用するための土台です。

実習

キャラクターに Shader Graph を適用する

変形と見た目を 分けて確認する

実習1

スキンメッシュを確認する

  1. Humanoid または Generic のキャラクターモデルをシーンに配置する
  2. SkinnedMeshRenderer を持つ GameObject を探す
  3. MeshRoot BoneBonesMaterials の項目を確認する
  4. アニメーションを再生し、メッシュがボーンに追従することを確認する
  5. MeshRenderer ではなく SkinnedMeshRenderer が使われている理由を説明する

実習2

ウェイトの影響を観察する

  1. 関節が大きく曲がるアニメーションを再生する
  2. 肘、膝、肩、腰など、変形が目立つ場所を確認する
  3. 変形が自然な場所と、不自然な場所を比較する
  4. 不自然に見える場合、ウェイト、ボーン位置、法線のどれが原因に近いかを考える
  5. スクリーンショットを取り、原因の仮説をメモする

実習3

Shader Graph マテリアルを適用する

  1. キャラクター用の Lit Shader Graph を作成する
  2. Base Color、Normal Map、Smoothness などを調整する
  3. キャラクターの SkinnedMeshRenderer にマテリアルを適用する
  4. アニメーション中も質感が破綻しないか確認する
  5. 影を受けるか、影を落とすかを確認する

実習4

リムライトを追加する

  1. Shader Graph に Fresnel Effect または Dot Product ベースのリムライトを追加する
  2. リムライトの色と強さをプロパティ化する
  3. カメラを回転し、輪郭側で効果が変わることを確認する
  4. アニメーション中のシルエットで見え方を確認する
  5. 強すぎる場合は、Power や強度を調整する

実習5

Bounds と表示範囲を確認する

  1. キャラクターを大きく動かすアニメーションを再生する
  2. Scene View で Renderer の Bounds を確認する
  3. メッシュの一部が突然消える場合、Bounds が小さすぎないか確認する
  4. 影やカリングに影響していないか確認する
  5. 表示が安定する範囲を考える

授業内課題

アニメーションする キャラクター用マテリアルを 作成してください

動いている状態で 見た目を確認します

課題の条件

以下を満たしてください

  • SkinnedMeshRenderer を持つキャラクターモデルに適用している
  • アニメーション再生中にメッシュが正しく変形している
  • Shader Graph で Base Color または Texture を設定している
  • リムライト、フレネル、ノイズ、Alpha Clip のいずれかを使っている
  • 影を受ける、または影を落とす状態を確認している
  • スクリーンショットまたは短い動画で、動作中の見た目が確認できる

確認観点

見た目の問題を切り分ける

  • キャラクターの変形は自然か
  • 関節周辺に強い潰れや引っ張られがないか
  • マテリアルはアニメーション中も破綻していないか
  • リムライトやノイズが意図した場所に出ているか
  • 影、透明、Bounds の問題が起きていないか
  • 問題がある場合、モデル、Renderer、Shader Graph のどこを疑うか

この回の到達目標

  • スキンメッシュがボーンで変形するメッシュであることを説明できる
  • ボーンウェイトが頂点ごとの影響度であることを説明できる
  • バインドポーズが変形前の基準姿勢であることを説明できる
  • SkinnedMeshRenderer が扱う情報を確認できる
  • Shader Graph マテリアルをキャラクターへ適用し、変形と見た目を分けて確認できる

今回のまとめ

スキンメッシュは頂点変形の仕組み

  • スキンメッシュは、ボーンの動きに合わせて頂点を変形するメッシュです
  • ボーンウェイトは、各頂点がどのボーンからどれだけ影響を受けるかを表します
  • バインドポーズは、メッシュとボーンを結び付けたときの基準姿勢です
  • Unity では SkinnedMeshRenderer がメッシュ、ボーン、ウェイトを扱います
  • キャラクター表現では、モデルデータ、Renderer、Shader Graph を分けて確認することが重要です

おつかれさまでした!

次回予告 第11回 行列と座標変換1

ローカル、ワールド、ビュー とはなんぞや