[Omniverse] USD 20.08から22.11での変更点を把握する

USD Composer 2023.1.1 BetaでC++やPythonでUSDを操作するにあたっていくつか修正が必要でしたので、それらについて列挙します。
USD 20.08から22.11に大きくバージョンアップされましたので、それの仕様変更になります。
また、USD Composerの"Asset Validator"もUSDの構文チェックで非常に使えました。
Asset Validatorを使って検証した内容も押さえるポイントとして追加します。

大きくUSD自身の仕様変更とOmniverseの開発における仕様変更がありますが、今回は前者についての解説になります。

なお、コードについてはPythonの場合で説明します。

from pxr import Usd, UsdGeom, UsdPhysics, UsdShade, Sdf, Gf, Tf

# Get stage.
stage = omni.usd.get_context().get_stage()

のように現在のステージを取得しているものとします。

なお、GitHub上でのUSDのリリース日は以下のようになっていました。

USDのバージョン Release日
USD 20.08 Jul 21, 2020
USD 22.11 Oct 22, 2022

Omniverse(Omniverse Create → USD Composer)では、July, 2023にUSD 20.08から22.11に移行されました。

USDのヘッダ情報の明確化 (Asset Validatorで検出)

ヘッダ情報をデフォルトでも明示指定する必要があります。

パラメータ名 デフォルト 内容
metersPerUnit 0.01 ステージの距離の単位。1.0でm、0.01でcm
upAxis Y ステージのアップベクトル。’Y’または’Z’

Pythonでは以下のように指定します。

UsdGeom.SetStageMetersPerUnit(stage, 0.01)
UsdGeom.SetStageUpAxis(stage, UsdGeom.Tokens.y)

Primvarsの取得方法の変更

Meshの場合はUVや独自パラメータはprimvarに指定します。
USD 20.08は以下のように記述していました。

m = UsdGeom.Mesh(prim)
primvars = m.GetPrimvars()

これが廃止になり、USD 22.11では以下のようになります。

primvarsAPI = UsdGeom.PrimvarsAPI(prim)
primvars = primvarsAPI.GetPrimvars()

MaterialへのShaderの割り当て方法の変更

OmniPBRを作成しこれにShaderを割り当てる場合に、USD 20.08では以下のように記述していました。

materialPrimPath = "/World/Looks/OmniPBR_mat"
diffuseColor = Gf.Vec3d(1, 0, 0.2)

material = UsdShade.Material.Define(stage, materialPrimPath)

shaderPath = materialPrimPath + "/Shader"
shader = UsdShade.Shader.Define(stage, shaderPath)
shader.SetSourceAsset("OmniPBR.mdl", "mdl")
shader.GetPrim().CreateAttribute("info:mdl:sourceAsset:subIdentifier", Sdf.ValueTypeNames.Token, False, Sdf.VariabilityUniform).Set("OmniPBR")

# Set Diffuse color.
shader.CreateInput("diffuse_color_constant", Sdf.ValueTypeNames.Color3f).Set(diffuseColor)

# Connecting Material to Shader.
mdlOutput = material.CreateSurfaceOutput("mdl")
mdlOutput.ConnectToSource(shader, "out")  # Error!

最終行の"mdlOutput.ConnectToSource(shader, "out")"は使えなくなりました。

mdlOutput.ConnectToSource(shader, "out")

shaderは"shader.ConnectableAPI()"に変更する必要があります。

mdlOutput.ConnectToSource(shader.ConnectableAPI(), "out")

Schemaの明確化 (Asset Validatorで検出)

これはPythonでのプログラミングでは関係なく、usdファイルでの指定の件になります。
形状にマテリアルをバインドする際にusdaでは以下のように出力されました。
※ これはエラーにはなりませんが、正しくない出力です。

def Sphere "sphere" (
)
{
    float3[] extent = [(-20, -20, -20), (20, 20, 20)]
    rel material:binding = </World/Looks/omniPBR_mat1>
    double radius = 20
}

マテリアルのバインド時はPythonでは以下のように記述します。

# Create sphere
spherePath = "/World/sphere"
sphereGeom = UsdGeom.Sphere.Define(stage, spherePath)
prim = sphereGeom.GetPrim()
sphereGeom.CreateRadiusAttr(20.0)

# Create material
materialPath = "/World/Looks/omniPBR_mat1"
material = UsdShade.Material.Define(stage, materialPath)

# Bind material
bindAPI = UsdShade.MaterialBindingAPI(prim)
bindAPI.Bind(material)
bindAPI.Apply(prim)  # This is important!

MaterialBindingAPIを使用しています。
ここで生成したusdをusdaでエクスポートすると以下のようになりました。

def Sphere "sphere" (
    apiSchemas = ["MaterialBindingAPI"]
)
{
    float3[] extent = [(-20, -20, -20), (20, 20, 20)]
    rel material:binding = </World/Looks/omniPBR_mat1>
    double radius = 20
}

"apiSchemas = ["MaterialBindingAPI"]"が追加されました。

xxxAPIを使う際は、Applyで確定するようにしたほうがよさそうです。
これにより、どのスキーマを使用したかが明示化されるようです。
なお、UsdGeom.PrimvarsAPIではこのApplyはありませんでした。

Lightの作成 (Asset Validatorで検出)

PythonやC++の開発側ではUSD SDKが自動で対処してくれるため意識する必要はありません。
usdaでは、過去のLightは以下のような出力になっていました。

def DistantLight "distantLight"
{
    color3f color = (1, 0.5, 0.2)
    float exposure = 0
    float intensity = 100
}

Pythonで以下を実行します。

# Create distant light.
pathName = "/World/distantLight"
light = UsdLux.DistantLight.Define(stage, pathName)

# Set intensity.
light.CreateIntensityAttr(100.0)

# Set color.
light.CreateColorAttr(Gf.Vec3f(1.0, 0.5, 0.2))

# Set Exposure.
light.CreateExposureAttr(0.0)

USD Composer 2023.1.1では、usdaファイルは以下のようになりました。

def DistantLight "distantLight"
{
    color3f inputs:color = (1, 0.5, 0.2)
    float inputs:exposure = 0
    float inputs:intensity = 100
}

すべてのLightのパラメータで"inputs:"が追加されました。

また、ライトの角度を割り当てる場合は"Shaping"を使います。
この場合はUsdLux.ShapingAPIを使用するため、Applyを忘れずに行うようにします。
Pythonで以下を実行します。

# Create sphere light.
pathName = "/World/sphereLight"
light = UsdLux.SphereLight.Define(stage, pathName)

# Set Radius.
light.CreateRadiusAttr(2.0)

# Set intensity.
light.CreateIntensityAttr(10000.0)

# Set color.
light.CreateColorAttr(Gf.Vec3f(1.0, 0.9, 0.8))

# Set Exposure.
light.CreateExposureAttr(0.0)

# cone angle.
shapingAPI = UsdLux.ShapingAPI(light)
shapingAPI.CreateShapingConeAngleAttr(180.0)
shapingAPI.Apply(light.GetPrim())  # This is important!

usdaでは以下のように出力されました。

def SphereLight "sphereLight" (
    apiSchemas = ["ShapingAPI"]
)
{
    color3f inputs:color = (1, 0.9, 0.8)
    float inputs:exposure = 0
    float inputs:intensity = 10000
    float inputs:radius = 2
    float inputs:shaping:cone:angle = 180
}

"shaping:cone:angle"の前に"inputs:"がついているのを確認できます。

extentの明示指定 (Asset Validatorで検出)

Primの形状を囲むローカルのバウンディングボックスは"extent"で指定できます。
これは省略しても動作に問題なさそうでしたが、

Why Extent and not Bounds ?
https://openusd.org/release/api/class_usd_geom_boundable.html#details
内部で使われているようなので念のためextentは明示します。
Asset Validatorを使った場合、extent未使用のときは警告されました。

このextentが使用できるのはSphereやMeshなどのジオメトリやライト(DistantLightやDomeLightは除く)があります。

SphereやCubeなどのジオメトリは、extentは自動で割り当てられます。
MeshやLightは、以下のようにextentを計算して割り当てることができます。

# Create mesh.
meshGeom = UsdGeom.Mesh.Define(stage, "/World/mesh")

# Assign various parameters.
...

# Compute extent.
boundable = UsdGeom.Boundable(meshGeom.GetPrim())
extent = boundable.ComputeExtent(Usd.TimeCode(0))

# Set Extent.
meshGeom.CreateExtentAttr(extent)

"UsdGeom.Boundable"でextent計算用のクラスを作成し、ComputeExtentで計算。
その結果をジオメトリの"CreateExtentAttr"でセットします。

新たに発見次第、これらのUSDのバージョンにより変更が必要な点はブログで追記していく予定です。