MaterialDescriptionを用いてMayaからUnityにMaterial情報を共有する

# はじめに
本記事は[QualiArts Advent Calender 2021](https://qiita.com/advent-calendar/2021/qualiarts)の19日目の記事です。

Unity2019よりMaterialDescriptionというものを使って、Importerでモデルに含まれるMaterialの情報を取得できるようになりました。
本記事ではこちらの使用についての調査結果を紹介します！

# 使ってみる
まずは適当なMaya標準のphongシェーダーをあてたモデルを用意し、colorにテクスチャを接続します。
![2021-12-17_11h27_56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18707/e6abe7d0-4c78-6c4e-15c4-af81126e515f.png)

あわせて以下のようなImporterを用意します。

```csharp
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEditor.AssetImporters;
using System.Linq;

public class FBXImporter : AssetPostprocessor
{
    public void OnPreprocessMaterialDescription(MaterialDescription description, Material material, AnimationClip[] materialAnimation)
    {
        var textProps = new List<string>();
        description.GetTexturePropertyNames(textProps);
        foreach (var prop in textProps)
        {
            TexturePropertyDescription textureProperty;
            description.TryGetProperty(prop, out textureProperty);
            
            Debug.Log($"[{description.materialName}]{prop}: {textureProperty.path}, {AssetDatabase.GetAssetPath(textureProperty.texture)}",textureProperty.texture);
        }

    }
}

```

MaterialDescriptionのドキュメントは以下。
https://docs.unity3d.com/ja/2021.1/ScriptReference/AssetImporters.MaterialDescription.html

この状態でFBXとテクスチャをUnityに出力してみると…

```
[phong1]DiffuseColor: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_color.png, Assets/Data/description/Textures/tex_color.png
```
このような形でMaterialの名前、テクスチャが接続されたプロパティ、Mayaシーンにおけるテクスチャパス、Unityにおけるテクスチャの相対パスが取得できていることが確認できました。

次にちょっと発展させて、LayeredTextureとBumpMappingの動作も確認してみます。
![2021-12-17_11h35_42.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18707/12fdf4fe-1deb-eaf9-3037-01af298388f0.png)

この状態で再出力してみると…

```
[phong1]DiffuseColor: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_color.png, Assets/Data/description/Textures/tex_color.png
[phong1]Bump: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_normal.png, Assets/Data/description/Textures/tex_normal.png
```

ColorはlayeredTextureの1つ目（tex_color.png）だけが取れていて、2つ目（tex_layer.png）は取れていません。ぐぬぬ…。

BumpMappingは正しく取得され、シェーダーがUniversal Render Pipeline/Litの場合、ちゃんとNormalMapにセットしてくれています。
![2021-12-17_11h40_49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18707/31ddd8c9-8e11-5ec7-f585-841d3c6c8548.png)

# 取得できない情報のやりとりの方法を考える
## カスタムアトリビュートの使用
layeredTextureの情報を取ってくれないのは分かったので、試しにカスタムアトリビュートを追加し、そちらにテクスチャを接続してみます。

```python
from maya import cmds

target = "phong1"
_LAYER_COLOR_ATTR = "custom_layer_color"
_LAYER_COLOR_ATTR_R = _LAYER_COLOR_ATTR + "R"
_LAYER_COLOR_ATTR_G = _LAYER_COLOR_ATTR + "G"
_LAYER_COLOR_ATTR_B = _LAYER_COLOR_ATTR + "B"
cmds.addAttr(target, longName=_LAYER_COLOR_ATTR, usedAsColor=True, attributeType="float3")
cmds.addAttr(target, longName=_LAYER_COLOR_ATTR_R, attributeType="float", parent=_LAYER_COLOR_ATTR)
cmds.addAttr(target, longName=_LAYER_COLOR_ATTR_G, attributeType="float", parent=_LAYER_COLOR_ATTR)
cmds.addAttr(target, longName=_LAYER_COLOR_ATTR_B, attributeType="float", parent=_LAYER_COLOR_ATTR)
```

ヨシ！
この状態でReimportしてみると…！

```
[phong1]DiffuseColor: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_color.png, Assets/Data/description/Textures/tex_color.png
[phong1]Bump: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_normal.png, Assets/Data/description/Textures/tex_normal.png
```

はい。
全然駄目でした。

ちなみにこんな感じでfloatのプロパティを取得することができるんですが、カスタムアトリビュートはここには入ってきてませんでした。

```csharp
var floatProps = new List<string>();
description.GetFloatPropertyNames(floatProps);
foreach (var prop in floatProps)
{
    string floatProp = "";
    description.TryGetProperty(prop, out floatProp);
    Debug.Log($"[float:{description.materialName}]{prop}: {floatProp}");
    
}
```

FBXをASCIIで出力して読んでみると、そもそもこのカスタムアトリビュートに接続したテクスチャのパスは含まれていなかったので、FBXプラグインがそもそも対応していない感…。

## DirextX 11 Shaderの利用
まずは適当なDX11 Shaderを用意しまして。
https://gist.github.com/tm8r/363b2b4cdd9329b935921c93a56c55e0

```hlsl
texture diffuseTexture
<
	string name 	= "";
	string UIName	= "Diffuse";
	string TextureType = "2D";
>;
uniform sampler2D _diffuseMap
<
	string UIName = "Diffuse";
> = sampler_state{
	Texture = <diffuseTexture>;
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};

texture layerTexture
<
	string name 	= "";
	string UIName	= "Layer";
	string TextureType = "2D";
>;
uniform sampler2D layerMap
<
	string UIName = "Layer";
> = sampler_state{
	Texture = <layerTexture>;
	Filter = MIN_MAG_MIP_LINEAR;
	AddressU = Wrap;
	AddressV = Wrap;
};
```

こんな感じでDiffuse用のテクスチャとLayer用のプロパティを用意、テクスチャと接続します。
![2021-12-17_16h02_47.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18707/fc93bf77-6464-964f-f249-5336744162fb.png)

これで再度書き出してReimportしてみると…

```
[dx11Shader1]diffuseTexture: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_color.png, Assets/Data/description/Textures/tex_color.png
[dx11Shader1]layerTexture: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_layer.png, Assets/Data/description/Textures/tex_layer.png
```

無事取得することが出来ました…！
名前のルールを作ればImporterで色々自動化できそうです。

## Stingray PBSの利用
Stingray PBSのMaterialを作成、必要なMapにチェックを入れてテクスチャと接続します。
![2021-12-17_16h09_36.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/18707/2a7d9626-ff64-f152-30a3-02ebe7619fd4.png)
今回はColor、Normalに適切なものを、そして実験のためにMetallicにtex_layer.pngを接続してみました。

これを出力してReimportすると…

```
[StingrayPBS1]TEX_metallic_map: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_layer.png, Assets/Data/description/Textures/tex_layer.png
[StingrayPBS1]TEX_normal_map: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_normal.png, Assets/Data/description/Textures/tex_normal.png
[StingrayPBS1]TEX_color_map: C:/Users/XXX/Documents/maya/projects/default/sourceimages/tex_color.png, Assets/Data/description/Textures/tex_color.png
```
こちらも無事取得ができました。
が、決められたプロパティしかなく、もちろん設定したものは全てViewPortに反映されるので、layerdTextureの情報を格納しておきたい場合や、テクスチャの各チャンネルを独自の用途に使用するケースではあまり現実的な選択肢ではなさそうです。

# 実運用を考える
layerdTextureも含め、テクスチャの解決だけであれば、命名ルールがしっかりしていればMaterialDescriptionがなくても運用は可能です。
（例えば通常のカラーは `_color`、陰影は `_shade` を末尾につけるルールにして、カラーテクスチャを起点に文字列置換で解決するなど）

逆に言えばテクスチャ名には厳しいルールを設けず、あくまで接続状況をもとに解決したい場合はDX11 ShaderやGLSL Shaderを活用することで自動化が実現できそうです。

floatのプロパティの取得に関して上で言及しましたが、Mayaでlambertやphongを使って簡易的な確認にとどめるのではなく、Unityのシェーディングを再現したDX11 Shaderなどを用いてMayaでテクスチャに限らないルックの調整まで行いたい場合に向いていそうです。

DX11 ShaderやGLSL Shaderを使用する場合、作業者のレンダリングエンジンを合わせる必要がでてきます。
もちろん各レンダリングエンジン用にシェーダーを用意すれば対応は可能ですが、二重管理は大変なのであまりやりたくありません。
また、これらのシェーダーのパスは相対解決ができないため、作業ディレクトリのパスも合わせる必要がありそうです。

StingrayPBSはShaderFXでできているため、ShaderFXで独自のシェーダーを作るという方法でもStingrayPBS同様にMaterialDescriptionでテクスチャの情報にアクセス可能です。
ただ、ShaderFXの情報はシーンに保存されるため、一元管理＆最新を常に各モデルに反映…というような状態にするのが難しいです。

# まとめ
というわけで個人的にはテクスチャの解決のみであればファイル名に厳格なルールを設けることで解決出来るので、無理にMaterialDescriptionに頼る必要はないかなーというのが現在の所感です。
ただ、上に書いた通りテクスチャに限らない様々なプロパティをMaya上で調整するケースでは選択肢の1つとしてあがってくるのかなと思います。

まだあまり活用事例を聞かないので、この記事が導入を検討している方々の参考になれば幸いです。