0
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.

usdShade のメモ

Last updated at Posted at 2022-09-06

USD の文法を使いましてシェーダネットワークが記述できるようになっていますが...

USD の Attribute の文法を流用してむりくり頑張っているためめんどいです...

おおまかな仕組み

Blender での Material / Shader Graph とだいたいは同じです.
(Maya の HyperShade とも同等かもしれません)

Material : マテリアル. 複数のシェーダグラフを内包(もしくはシェーダの最終的な出力がマテリアルに繋がれる). surface などの決まった出力がある.
Shader : シェーダノード. info:id でノードの種類を指定(UsdUVTexture ならテクスチャノードなど)

Connection

Blender での Shader graph 例です.

Screenshot from 2022-09-06 15-10-12.png

USD の場合もこれと同様な感じで定義していきます.
connection は destination の input(consumer) から source の output(producer) の方向で張ります
(図でいうと右から左方向)

USD での Material 例はこんな感じです.

def Material "mymat" {
  float inputs:myval = 1.3

  token outputs:surface = <./pbr.outputs:surface>

  def Shader "pbr" {
    uniform token info:id = "MyPBRShader"
 
    color3f inputs:diffuseColor.connect = <../diffuseTex.outputs:rgb>
    token outputs:surface
  }

  def Shader "diffuseTex" {
    uniform token info:id = "UsdUVTexture"
    float2 inputs:st.connect = <./texcoordReader.outputs:result>
    
    float3 outputs:rgb
    
    def Shader "texcoordReader" {
      uniform token info:id = "UsdPrimvarReader_float2"
      token inputs:varname = "st"
    }
  }

}

inputs, outputs namespace が使われます. 自身のシェーダを新規で定義する場合は任意にできる... かなとは思います.

ややこしいのは Materialoutputs:surface は cosumer(終端アトリビュート. terminal attribute) のため Shader での inputs に対応することです.
(したがって Material では outputs:*** から Shader への attribute に connection を貼る)

また, Shader での output アトリビュートは定義だけで, 値や connect がアサインされることはありません.
(構文上はアサインできるがたぶん無視される?)

UsdShadeNodeGraph から派生したのは connection を持つことができる(Material).
UsdShadeShader から派生したのは connection を持たない(Shader).
https://graphics.pixar.com/usd/dev/api/usd_shade_page_front.html

のためです.

Materialinputs:*** で定義したアトリビュートは, シェーダ側で参照(connect の targetPath)することができます
(global 変数的なものとして使う感じかしらん)

Predefined shader

USD コア仕様ではシェーダ関連は実は定義されておらず(Prim としての Material, Shader が定義されているくらい?), Blender や USDZ などで使われるシェーダの定義は, usdImaging(Hydra でレンダラなどとつなぐとき用のパッケージ?)で plugin 相当で行われています.

USD/pxr/usdImaging/plugin/usdShaders/shaders/shaderDefs.usda

  • UsdPreviewSurface : プレビュー用サーフェスシェーダ
  • UsdUVTexture : UV texture mapping. Texture tranform(UsdTransform2d) なども含む
  • PrimvarReader : texture mapping 用の UV 座標をジオメトリのどの primvar(アトリビュート)にするかを指定

の 3 つになります.

PrimVar reader

USD では, multi-UV texturing などのために, メッシュなどのジオメトリのどの UV coordinate の情報(primvar)を利用するか(読み取るか)を PrimvarReader で指定します.

型としては float, float2, ..., string, matrix4d などが使えます.
(string 型の PrimvarReader って何に使うのかしらん?)

PrimvarReader ノードの "varname" で, ジオメトリの primvar を指定します.

    def Shader "texcoordReader" {
      uniform token info:id = "UsdPrimvarReader_float2"
      token inputs:varname = "st"
    }

ややこしいのはここでの varname には primvars namespace を含まないことです.
token inputs:varname = "st" とあったら, 対応する(マテリアルがアサインされた)ジオメトリの primvars:st が参照されます.
(primvar reader と primvars:st で型の不一致が起こったらどうなるんじゃろ?)

interfaceOnly

    int inputs:useSpecularWorkflow = 0 (
        connectability = "interfaceOnly"
        doc = """This node can fundamentally operate in two modes :
            Specular workflow where you provide a texture/value to the
            "specularColor" input. Or, Metallic workflow where you
            provide a texture/value to the "metallic" input."""
    )

のように, Schema で interfaceOnly となっているもの.

Shader での input attribute のいくつかがこれに相当します. どこかへとつなぐか, もしくは同じく interfaceOnly を持つ input から繋がれることができます.

たとえば inputs:diffuseColorinterfaceOnly ではないので, inputs:diffuseColor.connect = ... でテクスチャノードに繋ぐ(テクスチャノードの情報を利用する)ことはできますが, 別のシェーダノードから, color3 val.connect = <myshader.inputs:diffuseColor> というのはできない(はず).

interfaceOnly でも, fallback としての値(.timeSamples 含む)を指定することもできます.
両方指定された場合, .connect が優先(Connection が有効であれば)されます. usdShade 解説ドキュメントでの Valid Shader Connections Win Over Input Values を参照ください.

NodeGraph

よりシェーダグラフを汎用的に記述するためのもの?
よくわらぬ.

ジオメトリでのマテリアルの割当

ジオメトリ側で rel material:binding でマテリアルをアサインします.

def Mesh "mymesh" {
  ...
  rel material:binding = <../mymat>
  ...
}

プレビュー用マテリアルとか, collection とか, bindMaterialAs などの strength の設定などもあります

GeomSubset

per-face material をしたい場合は, GeomSubset で対応します.

double sided material

あんまり情報がないですが, familyName を指定することで可能になると思われます.
(family とかややこしい名前だね... group とかのほうがよいとおもう)

familyName で GeomSubset のgroupのようなものを指定することができます.

def GeomMesh "cube" {
    ...
    
    uniform token subsetFamily:side0:familyType = "unrestricted"
    uniform token subsetFamily:side1:familyType = "unrestricted"

    def GeomSubset "side0"
    {
        uniform token elementType = "face"
        uniform token familyName = "side0"
        int[] indices = [0, 3, 5]
    }

    def GeomSubset "side1"
    {
        uniform token elementType = "face"
        uniform token familyName = "side1"
        int[] indices = [0, 3, 5]
    }

また, familyName に対応する familyType を上位 GeomMesh で指定します.

subsetFamily:<FAMILYNAME>:familyType

familyType は subset の index が elementType のジオメトリ要素(現状は elementType face しか対応していないため, face index)がどのようにアサインされるかを指定します.

  • partition : それぞれの GeomSubset の face id は GeomMesh の face を一意(unique)かつすべての face を参照する. つまり GeomMesh の faces を重なりや余りがなく subset に分解する
  • nonOverlapping : 各 GeomSubset の face id は相互排他, つまりある GeomSubset の face id は他の GeomSubset に現れてはならない. partition とは違い GeomMesh のすべての face を参照しなくともよい(つまり subset でカバーしない face が存在してもよい)
  • unrestricted : 制限なし.

最近になり pxrUSD dev 版で point もサポートされました. point の場合も同様です.

法線ごと, テクスチャ UV ごとのインデックス

法線ごと, テクスチャ UV ごとにインデックスを持ちたい...
(OpenGL とかリアルタイムレンダリングでは複数インデックスあつかうの無理だったり SSBO とかで無理くり頑張るしかなかったりするが, オフラインレンダリングだったりジオメトリ/シェーダのデータ交換とかでは複数インデックス持つのは普通だったりする).

usdGeom の Primvar(Property) では, :indices suffix があればそのアトリビュートのインデックスを個別に指定できます.

e.g.

normal3f primvars:normals = [ ... ] ( interpolation = "..." )
int[] primvars:normals:indices = [...] 

Indexed Primvar と言われています.

同じ値をインデックス(配列要素番号)で参照するようにしてデータ削減などにも使われるようです.
ただ,
ドキュメントには higher purpose で使われると, ちょっと何言っているかよくわからない説明ですが, USD ライブラリを使う側が, faceVarying にもかかわらず :indices が存在したらその情報を参考にしたほうがいいよ, 程度でしょうか.

また, usdGeom の normals にインデックスをつけることはできません. normals を使うの自身あまり推奨されないようですので, primvars:normalsprimvars ネームスペースをつけて対応します.

Composite(Flatten)

pxrUSD で Composite(Flatten) させると index を参照して, 元の変数(テクスチャ座標とか)を並び替えします
(tinyusdz では flatten_by_indices)

float primvars:var = [1.0, 3.1, 3.4]
int[] primvars:var:indices = [0, 0, 1, 0, 1]

とあると,

float primvars:var = [1.0, 1.0, 3.1, 1.0, 3.4]

となります(たぶん)

Id attribute

primvar には, id 配列(インデックスと似たものと誤解しがちであるがこちらは string id となる(token id のほうがいいきもするけどね))を持たせるというものできます.

ここでも :indices を使うことができるようです.

string[] primvars:myids = ["bora", "dora", ...]
int[] primvars:myids:indices = [0, 1, 0, 0, ...]

また, Relation の :idFrom で id の配列を参照(Reference)させることもできるようですが...

rel primvars:myids:idFrom = [</bora/dora>, </bora/muda>]

pxrUSD のソースコードとかみても実例が皆無なのでよくわかりませんね...

まとめると, :indices:idFrom は suffix として使われるので, これをそのまま primvar の名前にするのは避ける必要があります(e.g. primvars:indices は NG)

MaterialX とかその他のシェーダシステム

MaterialX のファイルを指定することで, MaterialX 対応できます(単に記述はできるというレベルなので, 利用側で MaterialX のシェーダ解釈などは別途必要)

def Scope "Materials" {
    def Material "MaterialX" (
        references = [
            # USD Preview Surface MaterialX Nodes
            @./usd_preview_surface_nodes.mtlx@</MaterialX>,
        ]
    )
    {
    }
}

と, mtlx(XML ファイル! めんどいネ) を references で読み込むことで対応します.
MaterialX 自身はシェーダのパラメータ設定集みたいな感じなので, 利用側(レンダラなど)で別途シェーダは必要です(MaterialX 自体にリファレンスのシェーダがありますが, Subsurface などはレンダラ側での実装が必要となる)

MDL では特定のプロパティを追加で対応のようです.

def Shader "flex_material"
{
    token outputs:out
    uniform token info:implementationSource = "sourceAsset"
    uniform asset info:mdl:sourceAsset = @nvidia/core_definitions.mdl@
    uniform token info:mdl:sourceAsset:subIdentifier = "flex_material"
  ...
}

その他のシェーダシステムは... T.B.W.

Connect 先の値のアクセス(C++, Python)

あたりを参考にして,

def Material "Mat"
{
    token inputs:st = "uv0"
    ...
            def Shader "primvareader"
            {
                ...
                token inputs:varname.connect = </Mat.inputs:st>
            }
}

のようなシェーダを作ったとします.
ここで inputs:varname の実際の値を取得したいとき, UsdShade.Shader の場合だと, connection がある場合は Get() ではリンク(connection)先の値を取得できません.
(UsdAttribute も同等?)

たとえばPython で,

GetInput('varname').Get() としても "uv0" ではなく None が帰ってくる...

真面目に Connection をたどるのが本当はいいかもしれませんが, GetConnection() だと単に connection の情報しかかえってこない...

(UsdShade.ConnectableAPI(Usd.Prim(</TexModel/boardMat>)), 'frame:stPrimvarName', pxr.UsdShade.AttributeType.Input)

とりあえずは GetValueProducingAttributes で行けました!

attrs = GetInput('varname').GetValueProducingAttributes()


# [Usd.Prim(</TexModel/boardMat>).GetAttribute('inputs:frame:stPrimvarName')]
# のように Attribute のリスト返ってくる.

print("varname = attrs[0].Get())
# => "st" 

で値取得できました... めんどくさいネ...

ところで pxrUSD の Python binding はエラーチェックとかあんまりなくて, 呼び出し変にやるとすぐクラッシュしてつらい...

循環参照?

def Scope "bora" {
  token st.connect = </bora/pxrUsdPreviewSurface1SG.bora>

  def Material "pxrUsdPreviewSurface1SG"
  {
      token bora.connect = </bora.st>
  }
}

ユーザ定義のアトリビュートだと上記のような循環参照ができてしまうが, Shader inputs:*** でやると schema で interfaceOnly あたりの設定のおかげか循環参照はできないようになっている(usdchecker とかでエラーがでる)

0
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
0
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?