0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

glTF 2.0 のスケルトンの分類

Posted at

glTF 2.0 でのスケルトンて?

glTF に限らず、ヒューマノイド(人間) 型の 3D モデルは、三角形から成る目に見える メッシュ の他に、モデルの 骨格 を表す スケルトン も持っている。

スケルトンがあれば、モデルは「動く」ことができる。
モデルが動くとき、スケルトンの 骨(ボーン) または 関節(ジョイント) の動きに応じて 頂点スキニング を行うことで、曲がった関節のメッシュも破綻することなく滑らかに繋げて描画することができる。

ここで、個人的に混乱したこと。

  • glTF でスケルトンを定義するのはメッシュノードのツリー? それともスキンが持つジョイントノードのツリー?
  • スキンが skeleton プロパティを持ってるみたいだけどこれは何? ……え? 無視すべきなの?

考察

先に、文章中で使用する glTF 用語(?)の意識合わせを簡単に。

  • 「メッシュノード」…… mesh プロパティを持つ node
  • 「スキン」……nodeskin プロパティ。skin プロパティを持つ node は、必ず mesh プロパティも持っていなければならない。
  • 「ノード変換」……node の TRS プロパティ(translation, rotation, scale)または matrix プロパティで定義される「変換(transform)」。
  • 「ジョイントノード」…… skin.joints が参照する node 。「スケルトンノード」とも言う。

二種類のスケルトンがある

glTF 2.0 でモデルのスケルトンを定義するものが何であるかは、メッシュノードがスキンを持っているか持っていないかで異なる。 これはなかなか気付かなかった。

  1. スキンがない場合:メッシュには、そのメッシュノードのノード変換が適用される。
  2. スキンがある場合:メッシュには、スキンのジョイントノード のノード変換が適用され、メッシュノードのノード変換は無視される。

これは、同じ glTF モデルであっても、メッシュノードにスキンがあるかないか次第で、スケルトンの作り方がまったく違う ということを意味している。

ここでは仮に、1. を「メッシュスケルトン」、2. を「スキンスケルトン」とでも称して分類することにする。

glTF 2.0 の仕様だけだとこの 2 パターンのスケルトンを混在させることもできてしまうが、ここでは言及しないでおく。

なお、この仕様の副作用として、スキンスケルトンを使うモデルの場合、メッシュを持つノードの変換が無視されるため、メッシュノードを glTF アセットの Nodes 内のどこに置いても構わなくなる。

実際、スキンスケルトンを使う Alicia Solid の公式 VRM ファイルでは、以下のように、メッシュノードを配置するための専用のルートノードを作って整理しているようだ。

; メッシュノード
nodes[0], Name='mesh'
 nodes[1], Name='body_top'. Meshes[0], Skins[0]
 nodes[2], Name='body_under'. Meshes[1], Skins[1]
 nodes[3], Name='cloth'. Meshes[2], Skins[2]
 nodes[4], Name='cloth1'. Meshes[3], Skins[3]
 nodes[5], Name='cloth2'. Meshes[4], Skins[4]
 nodes[6], Name='cloth_ribbon'. Meshes[5], Skins[5]
 nodes[7], Name='eye'. Meshes[6], Skins[6]
 nodes[8], Name='face'. Meshes[7], Skins[7]
 nodes[9], Name='flonthair'. Meshes[8], Skins[8]
 nodes[10], Name='neck'. Meshes[9], Skins[9]
 nodes[11], Name='other'. Meshes[10], Skins[10]
 nodes[12], Name='other02'. Meshes[11], Skins[11]
; 以下、スキンスケルトン用のノード
nodes[13], Name='Root'
 nodes[14], Name='Hips'
  nodes[15], Name='LeftUpLeg'
   nodes[16], Name='LeftLeg'
    :
    :

この例では、nodes[0~12] のノード変換は完全に無視されるので、TRS プロパティや matrix プロパティを記述する必要はない。

スキンの jointsskeleton て?

glTF 2.0 では、「スケルトン」や「ジョイント(関節)」といった用語は、大抵「スキン」に関する文脈で登場する。かつ、これらの情報は skin の中にある。そのため自分は、「スケルトンの定義にはスキンも含まれる?」という 誤解 を抱いてしまった。

便宜上 スキンスケルトン などと称してしまったが、スキンスケルトンはスキンの定義による影響を一切受けない。 スキンからスケルトン(またはそのジョイントノード)への一方向の参照があるのみである。

例えば Alicia Solid の公式 VRM ファイルでは、以下のように、骨格として繋がりようがない左右のジョイントノードが交互に並んでいたりする。ここからも、ジョイント配列がスケルトンを形作っているわけではないということが分かる。

skins[0], Skeleton=Nodes[14]
  joint[0] = Nodes[48] ('Spine3')
  joint[1] = Nodes[49] ('LeftShoulder')
  joint[2] = Nodes[115] ('RightShoulder')
  joint[3] = Nodes[50] ('LeftArm')
  joint[4] = Nodes[116] ('RightArm')
  joint[5] = Nodes[51] ('LeftForeArm')
  joint[6] = Nodes[117] ('RightForeArm')
    :

スキンスケルトンを使う場合、メッシュの頂点は、スキンスケルトンの一部を表しているとある skinjoints 配列の中から、最大 4 つのジョイントノードを正しい配列インデックスで選ぶ。そのインデックスさえ正しければ、skin.joints 配列に格納される順番には特に決まりがないようだ。

(ただし、一緒に使われそうなジョイントは近くに集めていた方が、実装次第では、シェーダーリソースの節約になるかも知れない。また、skin.jointsskin.inverseBindMatrix との数と順番は一致させる必要がある。)

skin.skeleton の存在意義

そして、glTF 2.0 仕様によれば、skin.skeleton プロパティが示す情報(ジョイントノードの共通の親となるノードのインデックス)は、スキニングには使用されず、ただスキンの「ピボットポイント」として使うこともできる、と記されているのみである。

個人的に思うに、1 つの glTF ファイルには複数のモデルを含めることができるので、あるスキンがどのモデル(のスキンスケルトン)に属するのかを示すのが skin.skeleton の由来だったのかなと思う。

しかし、将来的には廃止されそうだという情報もあるため、skin.skeleton の設定は無視することが望ましいかと考える。

0
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?