USD なんかいろいろ用語多かったりしてめんどい...
DCC(データ交換フォーマット)として USD を使いたいとき用のデータ構造のメモです.
関連情報
自前 AR アプリとかで USDZ を直接読むための USDC file format のメモ
https://qiita.com/syoyo/items/dbfecbc16468e6108d0d
UsdSkel のメモ
https://qiita.com/syoyo/items/94b9a43a6a6e3c251664
用語
https://graphics.pixar.com/usd/release/glossary.html
一般
def Mesh "MyMesh" ( ) {
point3f[] position = [(0.0, 0.0, 0.0), ...]
float2[] primvar:st = [(0.0, 0.0), (1.0, 0.0), ...] (
interpolation = "vertex"
)
}
def
Prim データ型
名前
でノードを定義します.
Prim データ型は指定しない場合 GPrim
(Generic primitive? 汎用のプリミティブ型)になります
()
でメタ情報を付与できます.
これは Stage(.usd ファイル先頭), GPrim と, primvar:uv
のような Attribute の両方で付与できます.
メタ情報には, customData
でカスタムデータを付与できます. ただこの場合使える型(float, point3f, ...)は限定されているようです.
.usd(.usdc or .usda)のファイルは Stage と呼ばれるようです.
上記 GPrim でのメタで, 他の .usd を参照するみたいなこともできます(Composition Arc)
スキーマ
データ構造はスキーマ(schema)として, これも USD(USD ASCII) で定義されています
(JSON とかのほうがありがたいのう)
スキーマのファイルは USD の repo にあります.
e.g.
<USD>/pxr/usd/usdGeom/schema.usda
ただこれらは C++ コードの include を含んでいます.
ここから usdGenSchema で生成されたものが generatedSchema.usda
としてあります.
Shader 関連
Shader 関連は usdImaging(Hydra?) に分類され, コアとして schema は定義されていません(ややこしいですね).
PrimvarReader などのシェーダの schema は
USD/pxr/usdImaging/plugin/usdShaders/shaders/shaderDefs.usda
にあります.
モジュール構成
DCC としてメインの要素
- usdGeom : 形状データ
- usdLux : ライト
- usdShade : マテリアル・シェーダ(ただし概念的なコンテナとしての定義のみ)
- usdImaging : 実際のシェーダ, テクスチャ定義関連
- 最近は usdShaders にリネーム?
- usdSkel : スキニング, Blendshapes
非コア的な要素
- usdVol : ボリュームデータ
- usdHydra : Hydra(レンダラへのインターフェース)
- usdRender : レンダリングの設定とか
- usdUI : GUI 要素?
- usdMedia : 音声など(現状は spatial audio のみ)
- usdPhysics : 物理
- usdUtils :
大まかな構成
ノード + プロパティ(変数)という感じで, 基本 glTF 的な感じです.
Prim
ノードに相当します. Property や子のノード(Prim)を持つことができます.
Specifier
Prim には三種類の Specifier があります.
def
: Prim の定義(通常はこれ)
over
: Prim 定義の上書き. 上書き対象(def
. class
も?)がない場合はなにも Stage には追加されない.
class
: ユーザー定義の抽象 Prim の定義. 継承されない限り Prim にはならない. class Xform
みたいに既存の Prim 型に class をつけてもエラーにはならない(どう処理したらいいじゃろうか)
Model
Prim とかの上位概念っぽい. Model から Group が作れたりする.
Kind
Prim(ノード)のカテゴリを Prim のメタデータで指定します. 基本は "component"
?
- model : ノードの基本型. 抽象的な型なので実際の USD データには出てこないっぽい?)(
def
で型なしが model に該当するのかしらん) - group : ほかの model(assembly, component, subcomponent?)のグループとなる. https://graphics.pixar.com/usd/release/glossary.html#usdglossary-modelhierarchy Model Hierarchy に従う
- assembly : これもグループとなるノード.
group
との関連がよくわからずややこしい. Prim や references を子に持つことができる,kind = group
のノードも子に持つことができる. - component : Kind の "leaf"(末端)となるノード. 子には
subcomponent
しか持つことができない. GPrim は基本これかしらん. - subcomponent : component の子であることを imply するようなもの? GeomSubset ノード(GeomMesh の子である)などがこれに該当するのかしらん?
Active
Prim を有効, 無効にする. deactivate でも, シーングラフ上にはデータとしては存在するまま. Maya でいう Hide(invisible) みたいな感じ.
PrimSpec
Prim specifier の略カナ?
Prim と PrimSpec の関係がややこしいですが, PrimSpec を compose(コンポジット)して最終的にシーングラフ要素として出来上がったのが Prim になる感じです.
(Prim になる前の部品(仕様)の状態が PrimSpec)
したがって Composition arc(コンポジット処理)は PrimSpec に対して行われます.
Maya で言えば, たとえば Sphere プリミティブを作ったときの Construction history の最初の状態(仕様)が PrimSpec という感じでしょうか.
Opinion
意見? たとえば Attribute で float radius
とあれば radius
は float 型の変数と言い張っている.
コンポジットとかで名前が同じでも型がちがったりするとき, LIVRPS ルールに従って解決する.
Property
Attribute or Relation のベースクラスになります.
Property は
- Relation(
rel
) : material のアサインなど(e.g.rel material:binding = </path/to/mat>
)-
rel myrel
みたいにターゲットのパスがないカラの定義もできはする(基本的にはこのような定義はコメントのようなもので, 無視してもかまわない(はず))
-
- Attribute
-
float myval = 1.0
: (スカラー?)値のアサイン -
float myval.timeSamples = ...
: TimeSamle 値のアサイン -
float mycal.connect = </path/to/mat>
: ターゲット先のパスの値を参照する(.connect
では Path の配列も指定可能(ただあんまり使うケースはないもよう)) -
float outputs:rgb
: Attribute の定義だけ(シェーダなどで.connect
先を作るのに使われる)
-
に分類されます.
.connect
は仕様上は循環参照ができてしまいます(詳細は後述)
データ型
color3f など, 中身のデータ型は同じであるが, 役割がことなる Role type というのもあります.
(実際としては, たとえば頂点カラーは color3f or color4f 型とすることで, より適切な型を指定することで多少わかりやすくする程度の効果だけかとは思われる)
Token
"mytoken"
のようにダブルクオーテーションで囲います.
識別子のような文字列に使います(string
型との違いは, 改行などを持つことができない, 特殊記号など使える文字に限定がある)
float
, xformOp:translate
, などがトークンになります.
String
Token と同じように "strval"
のようにダブルクオーテーションで囲います.
さらに, Ascii では, Python のように """
で無変換の(改行を持つ)文字列を扱うことができます.
string myval = """
bora
dora
"""
USDC(Binary)では triple quote と single quote で違いはありません.
Attribute
いくつかの Attribute は, timeSamples としてアニメーションデータ(時間とデータのリスト)とすることが可能です.
デフォルトの値と .timeSamples
とで両方持つことができます.
uniform(Variability)
uniform
が付くのは主に時間軸で変化しないアトリビュートであるのを指示する.
(スキーマレベルでは定義されるが, 効率のためにコア(実装側)ではヒントとして解釈される程度っぽい)
たとえば token visibility
は uniform
が付かない(時間軸で可視不可視できる)が, uniform token purpose
には付いたりする.
Attribute block
none
を指定するところでブロックすることができます.
TimeSamples
TimeSamples も None
を持つことができます
None
: 指定した時間軸にはデータが無いことを指定する(blocked).
また, Block は reference したものに対して特定の領域だけ上書きというのはできない.
def Sphere "BigBall"
{
double radius.timeSamples = {
101: 1,
102: 2,
}
}
def "DefaultBall" (
references = </BigBall>
)
{
double radius.timeSamples = {
101: None,
}
}
この場合 DefaultBall.radius
は全部 blocked となる.
(BigBall の [-inf, 101)
だけを block するのでは無い)
delete
ListOp でサンプルを消すのも指定できたような?
Attribute
primvar(primitive variable)とその関連ステートを持ちます.
実際にはこの Attribute(primvar)がそれぞれのノード(GeomMesh, Xform, Material, ...)を構成するコアとなります.
References
xref みたいなものです. 基本は他の usd ファイル(ステージ?)を読み込む機能な感じです.
Relationship
JSON pointer みたいなものですかね.
texture mapping のときの prim 側のテクスチャ座標がどれかを指定するときなどに使います.
Connection
Relationship と近いのに Connection
があります.
Connection は, Attribute では変数に .connect
で指定しています. シェーダグラフのように型が決まっているものなどに使います.
Relationship だと型なし(typeless)なので参照されている先を評価しないと型がわからなかったりします.
PrimVar
Primitive Variable の略です. USD のコアとなるタイプで, 変数みたいなものになります.
慣例として? テクスチャ UV などには primvars
ネームスペースが付きます.
(Schema で定義されているわけではないが, Schema のコメントで対応しているネームスペースの記述があったりするので, ライブラリ側でいろいろ対応が必要)
float2[] primvars:st = ...
変数名, ネームスペース
:
でネームスペースをつけることができます. primvars:displayColor
.
Xform ノードの, xformOp
など, 使うネームスペースの指定(また, xformOp は使える型が決まっている. xformOp:translate
は float3 or double3 型と決まっている)があったりしますが, schema では定義されていないので注意です(schema のコメントにはどのネームスペースを使うか記述あったりはする)
userProperties
DCC ツールなど用にカスタムの attribute 情報付与したい場合は, userProperties
ネームスペースをつけるのが推奨のようです.
e.g.
custom string userProperties:blenderName:data = "Cube"
prefix, suffix
Xform には !invert!
の prefix があったりします(行列処理を invert する)
.connect
で connection を指定.
.timeSamples
でアニメーションデータを指定
suffix として .
をつけられるのはこの .connect
と .timeSamples
のみのようです(他の名前をつけると syntax error となる)
Custom attribute
スキーマに定義されていないものは custom
qualifier をつけます.
custom float myval = 1.3
ただ, usdchecker
などでは厳格なチェックはされていないよう? で custom つけなくてもユーザ定義の変数(Attribute)
Metadatum
Node(e.g. GPrim)や Attribute はそれぞれメタデータを持つことができます.
また Stage(.usd のグローバル/ヘッダの要素に対応) もメタデータを持つことができます. たとえば upAxis などもはメタデータになります.
interpolation
は, UsdStage などオブジェクト本体(?)に持つのと, PrimVar の meta として扱われるのがあってややこしいので注意ください.
Custom data
メタデータにさらに任意の情報を付与するのに customData
(dictionary 型) を使います.
(Stage の場合は customLayerData
)
customData
で使える型は USD の仕様では限定されているようです(TODO: ソースコードのどこで限定されているか調べる).
(bool, string, float あたりの基本的な型は使える).
また, dictionary データも更にネストして含めることができます.
def "Xform" bora (
customData = {
bool zup = 1
dictionary Blender bmeta = {
bool generated = 1
}
}
) {
....
}
Xform
glTF とかのシーングラフでいういわゆる node です.
変換行列 or traslate
などの変換 op)を持ちます.
xformOpOrder で変換の順を記述できます.
namespace
xformOp:translate
では float3 or double3 と, 取れる型が決まっています.
(schema では決まっておらず, 実装側で対応が必要).
また,
xformOp:translate:mytx:bora
のような namespace がさらに付いている場合も xformOp:translate
の namespace が付く変数(つまり float3 or double3 型)として取り扱われます.
prefix
!invert!
で変換を invert できます!
!invert!xformOp:translate:scalePivot
基本は値を negate ですが, matrix(transform
) の場合は逆行列を求めることになるんじゃろか
special token
!resetXformStack!
もあります. これは最初の要素だけに付くっぽい.
(例が無いのでよくわらぬが, ["!resetXformStack!", "xformOp:translate", ...]
みたいな?)
行列は親のノードを継承しますが, !resetXformStack!
ではこの変換スタックを取り除きます(つまりこれが定義されたノードは identity matrix から始まる).
親の変換を無視したいときに利用します(ライトや skinning の場合に使うのかな?)
Visibility
interited
or invisible
のみです.
inherited
は親の visibility 設定を引き継ぎます.
親が不可視で, 子が可視というケースは無いので, visible
はありません.
root 親のデフォルトは inherited
(つまり可視)です.
extent
geometry などのバウンディングボックスです.
基本的にはアプリ側で計算しないといけない?
Purpose
主にレンダラ用です.
Prim の描画の使われ方に使います.
Reference
string assetPath;
Path primPath;
LayerOffset layerOffset;
Dict customData;
T.B.W.
Scope
パスに名前をつけて階層を管理しやすくする(グループ化)感じの役割をします.
Identity Matrix を持つ Xform と同じです.
Material/Shader
Material の下に Shader があったり, Shader のノードで UVTexture
を定義があったりという感じです.
Texture mapping
UVTexture でテクスチャファイルなどを指定しますが,
USD ですと, UV 座標はを PrimvarReader で Geom から取得して処理, と面倒になっています.
PrimvarReader
inputs::varname
で読み取り先(多くの場合は UV texture coordinate)の attribute 名を指定します.
ただなぜか primvar
namespace は指定しません(Mesh に primvar:st
とあれば inputs:varname
には st
を指定.
の schema を見ると primvar
の namespace は除くとあります.
また, primvarProperty
というメタデータ定義されています.
sdrMetadata = {
token primvarProperty = "1"
}
参照する先のデータが primvar の Property であることを示すようです(e.g. visibility
みたいなアトリビュートは NG になるはず)
UDIM texture
ファイル名を調整で対応しています.
two-sided material, per-face Material?
GeomSubset で face をまとめることで per-face material を実現はできます.
Geom を doubleSided にして, メッシュの表と裏に別のマテリアルを割り当てるのはできないっぽ?
MaterialX
usdMtlx で, MaterialX 形式のマテリアルを読めます.
実体としては MaterialX .xml を利用するだけ? のようです(MaterialX 設定をどう利用するかはツール側にまかせる)
Stage, Layer
Stage = シーングラフ全体, Layer = Stage 内で個々のシーングラフを構成する要素(たとえば Layer は特定の時間軸に配置することができる)という感じです.
Layer として .usd を読み込んだ場合一部の Stage Meta(e.g. upAxis)は無視されるのかしらん?
Layer Stack
T.B.W.
Plugin 関連
Plugin は USD ではなく JSON で構造記述になります. ややこしいネ...
usd の定義自体 plugInfo.json
である程度変更することができます. たとえば upAxis のデフォルト(厳密には実装依存?)は Y ですが, これを Z に変えたりできます.
その他は T.B.W.
Deformer
いわゆる Maya での wrap deformer など.
pxrUSD 自体では schema で定義はありませんが, たとえば Omniverse では拡張としてあります.
Composition arcs
合成の環
というたいそうな名前が付いてややこしいですが,
いわゆる設定のオーバーライドみたいな感じです.
メッシュデータは同じでマテリアル設定だけキャラクターごとに変えたり, などが記述できます.
T.B.W.
List Editing(ListEdit op)
Compositon などとからんだり, 編集の履歴を残したりのために ListEdit op があります.
-
append
: prim を composite するときの最後に処理する -
prepend
: prim を composite するときの最初に処理する -
delete
: 値を削除する(のを指示する) -
reset to explicit
: unqualified に相当. 他の weaker レイヤーの listedit op を無視
評価には優先順位があります.
LIVRPS に準拠かしらん?
を参照ください.
Prim の Property の場合, Attribute は List editing できません. rel
(Relation) だけ適用可能です.
API Schema
3 つあります.
Non-applied API Schemas
よくわかりません. とりあえず普通のユーザにとっては考えなくてもよさそう.
Single Apply Schemas
Prim のメタデータで apiSchema
として inject されるものです(たぶん)
prepend apiSchema = ["MaterialBindingAPI"]`
など.
Multiple-apply schemas
インスタンス名(ネームスペースとして)を取って instanciate して inject できる API schema.
CollectionAPI
が該当します.
prepend apiSchemas = ["CollectionAPI:material:MainMaterial"]
CollectionAPI
は
参照ください. light link などもこれで実現できる... はず.
apiSchemas の仕組み上, ListEdit Op は prepend
のみです(のはず)
(append apiSchemas
, delete apiSchemas
というのも一応構文上は OK のようで, usdchecker
はエラー出さないけど...)