「[Omniverse] MaterialXを使う – その2」の続きです。
以下についてメモ書きです。
- テクスチャのRGBAチャンネルを分解し、MetallicやRoughnessのfloatとして使用 (separate)
- NormalMapの表現
- floatの引き算 (subtract)
- UnityのマテリアルをMaterialX(mtlxファイル)で表現
ここではOmniverse Create 2022.3.1を使用しました。
テクスチャのRGBAチャンネルから1つを取り出す
"separate"タグを使うことで、テクスチャのRGBまたはRGBAから個々のチャンネル値をfloatで取得します。
以下が参考のドキュメントです。
MaterialX: Supplemental Note
https://materialx.org/assets/MaterialX.v1.38.Supplement.pdf
実際のmtlxの記述は、以下が参考になります。
https://github.com/AcademySoftwareFoundation/MaterialX/blob/main/resources/Materials/TestSuite/stdlib/channel/channel.mtlx
以下は、テクスチャ画像(png)ファイルのR成分を取り出しています。
<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
<!-- Get texture -->
<nodegraph name="NG_texture" fileprefix="../images/">
<tiledimage name="image_texture" type="color4">
<input name="file" type="filename" value="tile_image.png" />
</tiledimage>
<output name="out" type="color4" nodename="image_texture" />
</nodegraph>
<!-- Separate texture into RGBA -->
<nodegraph name="NG_separate">
<separate4 name="separate" type="multioutput">
<input name="in" type="color4" nodegraph="NG_texture" output="out" />
</separate4>
<output name="out" type="float" nodename="separate" output="outr" />
</nodegraph>
</materialx>
"NG_texture"のNodeGraphでテクスチャをRGBAを持つcolor4として取得します。
次に、"NG_separate"のNodeGraphで"separate4"タグを使用しています。
これはcolor4またはvector4を4つに分解します。
RGBのcolor3を分解する場合は"separate3"タグを使います。
<separate4 name="separate" type="multioutput">
</separate4>
の type="multioutput" は忘れずにつけるようにしてください。
<input name="in" type="color4" nodegraph="NG_texture" output="out" />
の指定で、nodegraphで指定されるノードグラフ名、outputで指定される出力を与えています。
これはテクスチャのRGBA(color4)が入力されます。
<output name="out" type="float" nodename="separate" output="outr" />
の指定で、nodenameで指定したノードのR成分をfloatとして取り出します。
output="outr"はR成分の取り出し。
output="outg"はG成分の取り出し。
output="outb"はB成分の取り出し。
output="outa"はA成分の取り出し。
この場合、テクスチャのRGBAにパックされたRoughness/Metallicなどを分解してfloatにする、という流れができそうですね。
Unityの標準的なマテリアルをMaterialXに割り当てる
では、実用的な例としてUnityのBaseColor Map、MetallicSmoothness Map、Normal MapをMaterialXのstandard_surfaceに渡してみましょう。
Occlusionはstandard_surfaceにはないようでした。
Unityで以下のうち、Occlusion以外の3つのテクスチャを使用しました。
テクスチャ名 | 説明 |
---|---|
table_zataku_baseColor.png | Base Map |
table_zataku_MetallicSmoothness.png | R:Metallic A:Smoothness |
table_zataku_normal.png | Normal Map |
Occlusion Map |
MetallicSmoothnessマップ
Unityでは、MetallicSmoothnessは1つのテクスチャにパックされています。
以下のようにチャンネルごとに要素が分かれます。
- [R] Metallic
- [G] Occlusion
- [B] None
- [A] Smoothness
Smoothness値はRoughness = 1.0 – Smoothnessとして計算できます。
多くのフォーマットではRoughness値を使うことが多いと思われます。
MaterialXもRoughessを使うため、Smoothness値は反転させる必要があります。
Unityでの表現
UnityのURPではマテリアルを以下のように指定しました。
以下のように木のテーブルを配置しています。
これをMaterialXのmtlxファイルに渡していきます。
いくつか引っかかった部分があるため、先にそれを書いておきます。
mtlx : テクスチャは1つのnodegraphにまとめる
テクスチャは"tiledimage"タグで指定しますが、複数扱う場合は1つのnodegraphにまとめて入れないとエラーになるようでした。
これらの記載例は後述します。
mtlx : NormalMapの表現
NormalMapはテクスチャをvector3型で指定し、normalmapタグに渡す。
standard_surfaceタグでは"normal"に渡したら表現できました。
<nodegraph name="NG_textures" fileprefix="../images/">
<image name="normalMap_texture" type="vector3">
<input name="file" type="filename" value="normal.png" />
</image>
<normalmap name="normalMap" type="vector3">
<input name="in" type="vector3" nodename="normalMap_texture" />
</normalmap>
<output name="out_normal" type="vector3" nodename="normalMap" />
</nodegraph>
<standard_surface name="SR_unity" type="surfaceshader">
<input name="normal" type="vector3" nodegraph="NG_textures" output="out_normal" />
</standard_surface>
mtlx : Unityのマテリアルをmtlxで表現
最終的なmtlxファイルの内容を記載します。
<?xml version="1.0"?>
<materialx version="1.38" colorspace="lin_rec709">
<!-- Textures -->
<nodegraph name="NG_textures" fileprefix="../images/zataku/">
<tiledimage name="baseColor_texture" type="color3">
<input name="file" type="filename" value="table_zataku_baseColor.png" colorspace="srgb_texture" />
</tiledimage>
<tiledimage name="metallicSmoothness_texture" type="color4">
<input name="file" type="filename" value="table_zataku_MetallicSmoothness.png" />
</tiledimage>
<image name="normalMap_texture" type="vector3">
<input name="file" type="filename" value="table_zataku_normal.png" />
</image>
<normalmap name="normalMap" type="vector3">
<input name="in" type="vector3" nodename="normalMap_texture" />
</normalmap>
<output name="out_baseColor" type="color3" nodename="baseColor_texture" />
<output name="out_metallicSmoothness" type="color4" nodename="metallicSmoothness_texture" />
<output name="out_normal" type="vector3" nodename="normalMap" />
</nodegraph>
<!-- Separate texture into RGBA -->
<nodegraph name="NG_separate">
<separate4 name="separate" type="multioutput">
<input name="in" type="color4" nodegraph="NG_textures" output="out_metallicSmoothness" />
</separate4>
<output name="out_metallic" type="float" nodename="separate" output="outr" />
<output name="out_smoothness" type="float" nodename="separate" output="outa" />
<output name="out_occlusion" type="float" nodename="separate" output="outg" />
</nodegraph>
<!-- Convert Smoothness to Roughness -->
<nodegraph name="NG_roughness">
<subtract name="subtract" type="float">
<input name="in1" type="float" value="1.0" />
<input name="in2" type="float" nodegraph="NG_separate" output="out_smoothness" />
</subtract>
<output name="out" type="float" nodename="subtract" />
</nodegraph>
<standard_surface name="SR_unity" type="surfaceshader">
<input name="base" type="float" value="1" />
<input name="base_color" type="color3" nodegraph="NG_textures" output="out_baseColor" />
<input name="metalness" type="float" nodegraph="NG_separate" output="out_metallic" />
<input name="specular_roughness" type="float" nodegraph="NG_roughness" output="out" />
<input name="normal" type="vector3" nodegraph="NG_textures" output="out_normal" />
</standard_surface>
<surfacematerial name="UnityMaterial" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="SR_unity" />
</surfacematerial>
</materialx>
name="NG_textures"のnodegraphには、3つのテクスチャを格納しました。
タグ名 | name | 説明 |
---|---|---|
tiledimage | baseColor_texture | BaseColorテクスチャ |
tiledimage | metallicSmoothness_texture | MetallicSmoothnessテクスチャ |
image | normalMap_texture | NormalMapテクスチャ |
BaseColorテクスチャはRGBを使うcolor3型としてます。
MetallicSmoothnessテクスチャは、Unityの該当テクスチャをそのまま使用しています。
R:Metallic、A:SmoothnessなのでRGBAのcolor4の型にしています。
NormalMapテクスチャは色ではなくベクトル情報になるため、vector3型にしています。
NormalMapについては、以下の記載で法線のvector3に変換しています。
<image name="normalMap_texture" type="vector3">
<input name="file" type="filename" value="table_zataku_normal.png" />
</image>
<normalmap name="normalMap" type="vector3">
<input name="in" type="vector3" nodename="normalMap_texture" />
</normalmap>
この"NG_textures"のNodeGraphのoutputは以下のようにしました。
<output name="out_baseColor" type="color3" nodename="baseColor_texture" />
<output name="out_metallicSmoothness" type="color4" nodename="metallicSmoothness_texture" />
<output name="out_normal" type="vector3" nodename="normalMap" />
それぞれ型(type)が違うのに注意してください。
"NG_textures"の"out_metallicSmoothness"のRGBAを分離して、Metallic/Smoothness値を取得します。
<nodegraph name="NG_separate">
<separate4 name="separate" type="multioutput">
<input name="in" type="color4" nodegraph="NG_textures" output="out_metallicSmoothness" />
</separate4>
<output name="out_metallic" type="float" nodename="separate" output="outr" />
<output name="out_smoothness" type="float" nodename="separate" output="outa" />
<output name="out_occlusion" type="float" nodename="separate" output="outg" />
</nodegraph>
前述の"separate4"を使って
Rは"out_metallic"、Aは"out_smoothness"、Gは"out_occlusion"のfloat値を取得しました。
Smoothness値を使って"1.0 – Smoothness"を計算します。
これがRoughness値になります。
<nodegraph name="NG_roughness">
<subtract name="subtract" type="float">
<input name="in1" type="float" value="1.0" />
<input name="in2" type="float" nodegraph="NG_separate" output="out_smoothness" />
</subtract>
<output name="out" type="float" nodename="subtract" />
</nodegraph>
"subtract"タグを使用し、"in1"から"in2"を引いた値を計算します。
この場合、"NG_roughness"のNodeGraphの"out"がfloat型のRoughness値になります。
最後に、BaseColor/Metallic/Roughness/Normalをstandard_surfaceタグに渡します。
<standard_surface name="SR_unity" type="surfaceshader">
<input name="base" type="float" value="1" />
<input name="base_color" type="color3" nodegraph="NG_textures" output="out_baseColor" />
<input name="metalness" type="float" nodegraph="NG_separate" output="out_metallic" />
<input name="specular_roughness" type="float" nodegraph="NG_roughness" output="out" />
<input name="normal" type="vector3" nodegraph="NG_textures" output="out_normal" />
</standard_surface>
<surfacematerial name="UnityMaterial" type="material">
<input name="surfaceshader" type="surfaceshader" nodename="SR_unity" />
</surfacematerial>
Omniverse Createにmtlxファイルと形状のfbxを読み込み
このmtlxファイルをOmniverse Createに読み込みました。
Omniverse Createでのmtlxファイルの認識は「[Omniverse] MaterialXを使う」もご参照くださいませ。
fbxファイルでMeshをインポートします。
このfbxファイルは、Unityで使っていたものと同じものを指定しました。
Omniverseにfbxを読み込む際は"Convert to USD"でUSDファイルに変換し、
そのUSDファイルをStageに読み込みました。
※ USD以外の形状ファイルを使うと、(特にテクスチャ周りが)キャッシュとずれることがあります。
そのため、OmniverseではfbxやglTFファイルをそのまま読み込めはしますが、
一度USDに変換し、ネイティブな形(=USD)で扱うことをお勧めします。
読み込んだmtlxファイルから取得したMaterialを、各Meshに割り当てていきます。
これで、Unityの時と同じマテリアル表現になりました。
テクスチャは加工せずに読み込めるのを確認できました。
今回は、separateでテクスチャのRGBA成分を分解し、Unityのマテリアル構造をMaterialXで表してみる、という内容でした。
次回はNoiseについて説明予定です。