3
1

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.

Universal Scene DescriptionAdvent Calendar 2021

Day 23

UsdGeomXform だいたい理解した

Last updated at Posted at 2021-12-22

息切れ気味ですが、最後の記事です(主催者、よくあんなに書けるな...)

UsdGeomXformable

UsdGeomXform はいわゆる Transform ノードで、オブジェクト単位の移動、回転、スケールなどを表現する UsdGeomXformable を実装するスキーマの一つです。

まず UsdGeomXform と UsdGeomXformable の違いが気になるでしょう。スキーマの継承図を見てみます。
image.png

このようになっていて、実質 UsdGeom のほとんどのスキーマクラスは UsdGeomImageable と UsdGeomXformable を継承しています。これはつまり、Mesh や Cube、PointInstancer などもトランスフォームが可能ということです。

たとえば Maya では Transform と Mesh や Camera などの shape が別ノードとして定義されており、Mesh ノードそのものがトランスフォームを持つことはありません。usd では Maya に限らず様々な表現形式の DCC ツールでもインターチェンジを可能にするため、Mesh や Camera など自身も Xformable として作られています。もちろん USD でも運用上 Mesh などにはトランスフォームをいれずに、親の Xform を使うことにする、ということは可能です。

トランスフォーム操作

一口でオブジェクト transform といっても、ツールによって非常に解釈に幅があります。3DCG では一般的に回転・スケールなどと平行移動とを 4x4 行列の積で表しますが、行列の積は可換でないので、変換を適用する順番によって結果が変わります。

実際にはほとんどのツールは Scale/Rotate/Translate の順で適用してくれるのですが、Rotate は X/Y/Z の順であったり、Y/X/Z(Yaw-Pitch-Roll)であったりするし、ジョイントアニメーションなどでは joint pivot や orient といった表現で一時的に平行移動・回転をした後に回転操作を行い再度平行移動する、といった一連の操作を表現しているケースもあります。よく利用されるこれらの座標変換の表現方法には以下のものがあります

XformOp
translate 平行移動
scale 拡大縮小
rotate (X,Y,Z) X,Y,Z 軸での回転、その組み合わせ
orient 任意軸での回転
transform 4x4 行列

レンダラなどは最終的に座標が決まりさえすればよいので、これらの変換を合成して 4x4 行列にして保存しておけばよいのですが、4x4 行列からもとの translate や rotate などの要素に分解するのはそれほど自明な操作でもなく、またアニメーションデータとしてキーフレームが translate や rotate の要素ごとに別々に設定され補間されるようなケースではうまくいきません。そこで UsdGeomXform では、これら複数の変換操作と、その変換操作の適用順をまるごと保持することにより、柔軟性の高いトランスフォーム表現を実現しています。

XformOp

UsdGeomXform にはこれらを実現するために上の表にあるようなトークンを用いて、

float3 xformOp:rotateXYZ = (30, 60, 90)
float3 xformOp:scale = (2, 2, 2)
double3 xformOp:translate = (0, 100, 0)
uniform token[] xformOpOrder = [ "xformOp:translate", "xformOp:rotateXYZ", "xformOp:scale" ]

などと表現します。xformOpOrder は、この順番に(頂点座標から見ると右から)transform 演算が適用されることになります。回転はラジアンではなく度(degree) で表記されます。

translate が複数ある場合などは : をつけて区別できます。

double3 xformOp:translate:first = (0, 100, 0)
float3 xformOp:rotateXYZ = (30, 60, 90)
double3 xformOp:translate:second = (0, 0, 100)
uniform token[] xformOpOrder = [ "xformOp:translate:second", "xformOp:rotateXYZ", "xformOp:translate:first" ]

それぞれ別々にアニメーション(time-sample)が設定できるので、情報の欠損もなく大変便利です。
また同じ変換について逆演算を行う invert オペレーションを !invert! をつけることで表記できます

uniform token[] xformOpOrder = [ "xformOp:translate", "xformOp:rotateXYZ", "xformOp:translate:scalePivot", "xformOp:scale", "!invert!xformOp:translate:scalePivot" ]

!invert! がついた場所は、その変換の逆行列が適用されます。

Maya/FBX transform のエンコード例

Maya/FBX では、Xform は以下のように表現されます(たぶん。shear など一部省略)

X_{world} = X_{parent} * T * R_{offset} * R_{pivot} * R * R_o * {R_{pivot}}^{-1} * S_{offset} * S_{pivot} * S * {S_{pivot}}^{-1}
T Translation
Roffset Rotation offset
Rpivot Rotation pivot
R Rotation
Ro Rotation orientation
Rpivot -1 Inverse of the rotation pivot
Soffset Scaling offset
Spivot Scaling pivot
S Scaling
Spivot -1 Inverse of the scaling pivot

これを UsdGeomXform で表現するとき、逆行列の箇所は !invert! で表現できますので、仮に rotate が XYZ だとすると

uniform token[] xformOpOrder = [
  "xformOp:translate",
  "xformOp:translate:rotateOffset",
  "xformOp:translate:rotatePivot",
  "xformOp:rotateXYZ",
  "xformOp:rotateXYZ:orient",
  "!invert!xformOp:translate:rotatePivot",
  "xformOp:translate:scaleOffset",
  "xformOp:translate:scalePivot",
  "xformOp:scale",
  "!invert!xformOp:translate:scalePivot"
]

このように書けます。

UsdGeomXformCache

最終的には 4x4 行列が一つ欲しいわけですので、これは UsdGeomXform::GetLocalTransformation を使って取得できます。しかし複雑なシーングラフの深い階層構造においてすべてのワールド変換行列を求めるのはかなりのコストになります。
UsdGeomXformCache を使えば一度計算した結果をキャッシュしてくれて、効率よくワールド変換行列を求めることができます。

おわりに

かつて一緒に仕事していた超ベテランCGエンジニア(自分も30年以上のキャリアですが、はるかに上がいる...しかもまだ現役で)も

I am a “shuffle the matrix order till it looks right” kind of guy

と言っていましたが、実務においては 変換行列の掛け算は、結果が合うまで入れ替え続ける のが基本(だよね?)

とはいえ 3D の座標変換を自分でコーディングしたりシリアライズしたことがある人はみな、Scale/Rotate/Translate の順番はほとんど一択なのでいいとしても、Rotate の種類、ピボットの扱いなどで苦労し、もういいや 4x4 行列で!と開き直ると今度は補間で苦労したり、結局 decompose するがフリップしたり、と悩み続けているかと思います。
USD は少なくともフォーマット的にはオーサリング状態をそのまま記述することができるようになっており、チャンネルごとのアニメーションデータもロスレスに記述できます(とはいえインターチェンジの際には各ツールの制約が出てくるわけですが)。とくにピボットの扱いはツールの際が出やすいところなので、これは本当に助かります。

3
1
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
3
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?