1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

USDの実践メモ 04 : テクスチャマッピング

Posted at

前回に続き,今回はテクスチャマッピングを行います。用いる画像は USD のロゴマークとします。

USDLogoLrg_180x176.png

テクスチャマッピング

テクスチャリーダーとプリミティブ変数リーダー

前回は PBR シェーダーの diffuseColoropacity に特定の色(マゼンダ)や特性を割り当てましたが,今回はここにテクスチャを割り当てます。そのために,テクスチャリーダー(UsdUVTexture)とプリミティブ変数リーダー(UsdPrimvarReader_float2)の 2 つのシェーダーノードを用意します(これらについては公式の仕様書に詳しい説明があります)。

まずは,テクスチャリーダーから。file アトリビュートにテクスチャ(画像ファイル)のパスを指定します。マッピングに使用する st 座標 はマテリアルに張るメッシュを参照して得るようにするので,ここでは初期化しません。出力は rgba で,それぞれこのあとのプログラムでシェーダーの diffuseColoropacity に接続されます。

def create_uvtexture(stage, path):
    uvtexture = UsdShade.Shader.Define(stage, path)
    uvtexture.CreateIdAttr('UsdUVTexture')

    # Inputs
    uvtexture.CreateInput('file', Sdf.ValueTypeNames.Asset).Set('./USDLogoLrg.png')
    uvtexture.CreateInput('st', Sdf.ValueTypeNames.Float2)

    # Outputs
    uvtexture.CreateOutput('rgb', Sdf.ValueTypeNames.Float3)
    uvtexture.CreateOutput('a', Sdf.ValueTypeNames.Float)

    return uvtexture

次に,プリミティブ変数リーダーを用意します。これはメッシュから st 座標を取り込むなど,ジオメトリプリムから情報を取得するときに使うものです。今回は st 座標の取得にしか使いませんので,2次元配列を返す float_2 型のリーダーだけを準備しておけば充分です。

def create_primvarReader(stage, path):
    primvarReader = UsdShade.Shader.Define(stage, path)
    primvarReader.CreateIdAttr('UsdPrimvarReader_float2')

    # Inputs
    primvarReader.CreateInput('varname', Sdf.ValueTypeNames.String)

    # Outputs
    primvarReader.CreateOutput('result', Sdf.ValueTypeNames.Float2)

    return primvarReader

varname のデータ型は Token ではなく String にします。コロンなどの特殊文字の対応のためだと思われます。たしかにここのプリミティブ変数リーダーの定義でも string になっています。誤って Token にしてしまった場合は usdcheker に掛けると間違いを指摘してくれます。

メッシュノードの修正

前々回作成したメッシュノードを修正して,UV 座標データを与えます。一例として,下図のように左右の面で異なる向きの座標を与えてみます(右は90度回転している)。

uvcoord_480x360.png

def create_mesh(stage, path):
    mesh = UsdGeom.Mesh.Define(stage, path)

    # Points, FaceVertexCounts, FaceVertexIndices は前回と同じ
    ...

    # UV
    uvList = []
    uvList.append((0, 0))
    uvList.append((1, 0))
    uvList.append((1, 1))
    uvList.append((0, 1))
    uvList.append((0, 0))
    uvList.append((1, 0))
    uvList.append((1, 1))
    uvList.append((0, 1))

    primvar_api = UsdGeom.PrimvarsAPI(mesh)
    primvar = primvar_api.CreatePrimvar('st', Sdf.ValueTypeNames.TexCoord2fArray, interpolation=UsdGeom.Tokens.faceVarying)
    primvar.GetAttr().Set(uvList)

    uvIndexList = []
    uvIndexList.extend((0, 1, 2, 3))
    uvIndexList.extend((5, 6, 7, 4))

    primvar.SetIndices(uvIndexList)

    # DoubleSided も前回と同じ
    ...

    return mesh

マテリアルノードの修正

ここでいま一度,シェーダー,マテリアル,メッシュの各々が保持する surface 情報を整理してみます。色(diffuseColor)と 透明度(opacity)はシェーダーの input アトリビュートに定義されますが,データの中身はテクスチャリーダーによって外から得ます。そのテクスチャリーダーは,input アトリビュートの定義に見られるように,UV(st)を必要とします。しかしながらそのデータは,いま,メッシュが保持しています。従って,シェーダーネットワークのコンテナであるマテリアルとメッシュとの接続が必要になってきます。そこでマテリアルノードに,メッシュから st 情報を取得するための接続ターミナルを用意します。

def create_material(stage, path):
    material = UsdShade.Material.Define(stage, path)

    # Inputs
    material.CreateInput('frame:stPrimvarName', Sdf.ValueTypeNames.Token).Set('st')

    # Outputs は前回と同じ
    ...

    return material

接続

シェーダー,マテリアル,メッシュを接続します。本件ではメインプログラムでこれを行うことにします(ステージ作成からシェーダー作成までは前回までの記事と同じです)。

if __name__ == '__main__':
    # ステージ作成
    stage = create_stage()

    # ルートノード作成
    rootNode = create_node(stage, '/World', is_defaultPrim=True)

    # メッシュ作成
    mesh = create_mesh(stage, rootNode.GetPath().AppendChild('Mesh'))

    # マテリアル作成
    material = create_material(stage, rootNode.GetPath().AppendChild('Material'))

    # シェーダー作成
    shader = create_shader(stage, material.GetPath().AppendChild('PBRShader'))

    # プリミティブ変数リーダー作成
    stReader = create_primvarReader(stage, material.GetPath().AppendChild('StReader'))

    # テクスチャリーダー作成
    uvTextureReader = create_uvtexture(stage, material.GetPath().AppendChild('Texture'))

    # 接続 (1): マテリアルの output ターミナルをシェーダーに接続
    material.GetSurfaceOutput().ConnectToSource(shader.ConnectableAPI(), 'surface')

    # 接続 (2): シェーダーの input ターミナルをテクスチャリーダーの output ターミナルに接続
    shader.GetInput('diffuseColor').ConnectToSource(uvTextureReader.ConnectableAPI(), 'rgb')
    shader.GetInput('opacity').ConnectToSource(uvTextureReader.ConnectableAPI(), 'a')

    # 接続 (3): テクスチャリーダーの input ターミナルをプリミティブ変数リーダーの output ターミナルに接続
    uvTextureReader.GetInput('st').ConnectToSource(stReader.ConnectableAPI(), 'result')

    # 接続 (4): プリミティブ変数リーダーの input ターミナルをマテリアルに接続
    stReader.GetInput('varname').ConnectToSource(material.GetInput('frame:stPrimvarName'))

    # メッシュとマテリアルのバインド
    api = UsdShade.MaterialBindingAPI(mesh)
    api.Apply(mesh.GetPrim()).Bind(material)

    # USDファイルにエクスポート
    stage.GetRootLayer().Export('./Material.usda')

出力は次のとおりになりました。usdchecker もパスしています。

#usda 1.0
(
    defaultPrim = "World"
    metersPerUnit = 0.01
    upAxis = "Y"
)

def Xform "World"
{
    def Mesh "Mesh" (
        prepend apiSchemas = ["MaterialBindingAPI"]
    )
    {
        uniform bool doubleSided = 0
        int[] faceVertexCounts = [4, 4]
        int[] faceVertexIndices = [0, 1, 4, 3, 1, 2, 5, 4]
        rel material:binding = </World/Material>
        point3f[] points = [(-1, 0, 0), (0, 0, 0), (1, 0, 0), (-1, 1, 0), (0, 1, 0), (1, 1, 0)]
        texCoord2f[] primvars:st = [(0, 0), (1, 0), (1, 1), (0, 1), (0, 0), (1, 0), (1, 1), (0, 1)] (
            interpolation = "faceVarying"
        )
        int[] primvars:st:indices = [0, 1, 2, 3, 5, 6, 7, 4]
    }

    def Material "Material"
    {
        token inputs:frame:stPrimvarName = "st"
        token outputs:surface.connect = </World/Material/PBRShader.outputs:surface>

        def Shader "PBRShader"
        {
            uniform token info:id = "UsdPreviewSurface"
            color3f inputs:diffuseColor.connect = </World/Material/Texture.outputs:rgb>
            float inputs:metallic = 0
            float inputs:opacity.connect = </World/Material/Texture.outputs:a>
            float inputs:roughness = 0.4
            token outputs:surface
        }

        def Shader "StReader"
        {
            uniform token info:id = "UsdPrimvarReader_float2"
            string inputs:varname.connect = </World/Material.inputs:frame:stPrimvarName>
            float2 outputs:result
        }

        def Shader "Texture"
        {
            uniform token info:id = "UsdUVTexture"
            asset inputs:file = @./USDLogoLrg_340x332.png@
            float2 inputs:st.connect = </World/Material/StReader.outputs:result>
            float outputs:a
            float3 outputs:rgb
        }
    }
}
$ usdchecker Material.usda 
Success!

マテリアルを表示してみます。左右ふたつの面にそれぞれロゴがマッピングされました(右側は90度回転している)。

material_logo_395x200.png

まとめ

テクスチャの情報をPBRシェーダのパラメータに繋ぎながら,マテリアルへのマッピングを実現しました。

参考資料

ft-lab (Yutaka Yoshisaka) さん,あんどうめぐみさん,OpenUSD公式 による以下のサイトが理解の助けになりました。

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

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?