Houdiniで書き出したAlembicファイルをUnityで使うときのTips

## この記事
[Houdini Advent Calendar 2018](https://qiita.com/advent-calendar/2018/houdini) 12月18日の記事です。
忘備録として以前書いていたものをカレンダーが空いていたので記事にしました。
対象はHoudini初心者、Unity初~中級者くらいだと思います。


## こんなのできるよ

今回はこんな絵を目指そうと思います。
※絵作りやモデルなどにはほとんど言及しておらず、あくまでこんなのをUnityでやるためのAlembicファイルの吐き出し方の紹介になっています。
この画像でいうと真ん中のうねってるやつと回転している光るやつをHoudiniからAlembicを出力して
Unityにインポートしてそれっぽくマテリアルつくって作りました。

<img width="856" alt="スクリーンショット 2018-10-29 16.24.30.png" src="https://qiita-image-store.s3.amazonaws.com/0/79986/8df907b2-09ab-0b18-ac40-960ead52c5b1.png">



## Houdini側(基本)

まずはなんでもいいのでアニメーションするジオメトリをつくります。(画像でいうとNull:Outputのところ)
今回は簡単にチューブが回転しているジオメトリをつくりました。
上で紹介しているものの光ってるやつにあたります。

<img width="1836" alt="スクリーンショット 2018-10-29 16.33.51.png" src="https://qiita-image-store.s3.amazonaws.com/0/79986/d9bb8ed0-f773-441f-d2ee-84d7e366fc3f.png">

これから説明しようと思うところは赤く囲ってみました。(自明なのは省略してますが)

HoudiniからAlembicファイルを出力するときはrop alembicノードを使います画像の末端のノードです。
Outputとrop alembicの間にtransformをはさんでいるのはHoudiniとUnityではスケールが違うので
100倍したものを出力します。右上の右下のビューがそれを示しています。

次に左側のAlembicの出力設定側を見ます。Valid Frame Rangeを"Render Frame Range"にします。
これで任意のフレームを指定して書き出します。

最後にFormatをOgawaにする。これはDefault formatでは動かなかったのとHDF5よりもOgawaの方が
早くて容量少ないと[ここ](https://knowledge.autodesk.com/ja/support/3ds-max/learn-explore/caas/CloudHelp/cloudhelp/2016/JPN/3DSMax/files/GUID-B3282EE1-EBA6-4975-B422-006520D13376-htm.html)に書いてあったからです。

そして任意のファイル名を指定してRender to Diskを押せばひとまずアニメーションつきのAlembicファイルが出力されると思います。

以降、Alembicファイルをabcと省略することがあると思います。。[^1]

## Unity側(基本)

Unity側はごく簡単でAlembic ImporterがUnity Technologies JapanのGitHubで提供されているので
ダウンロードする前にスターを押して、ご利用させていただきましょう。
https://github.com/unity3d-jp/AlembicForUnity

ありがたいことに導入手順も日本語で書いてあるので説明も省きまくりますが
普通にパッケージダブルクリックして、Projectフォルダーにabcをドラッグして入れて
そのままHierarchyにうつせばひとまずモデルが表示され、Inspectorに自動で
Alembic Stream PlayerというのがついてくるのでTimeの数字を動かせばエディタ上で
アニメーションするのではないでしょうか。

ここまででとりあえずUnityでabc動かしてみようという部分です。

## ネクストステップへ向けて

任意のabcがUnityで動いた！Alembicすごい！という感動にしばらく浸ったら
いくつか気になる点が出てくるのではないでしょうか。

- HoudiniでCdアトリビュート(@ Cd)に設定した色はそのままでは出てないのでそれを解決したい
- まぁあとついでにUnityのシェーダーで使用したい任意の値を適当なアトリビュートに格納してもってこれたら便利かも

ということを思ったので以降は上記2点について説明できたらと思います。

## Houdini側(応用)

### 色情報について

これはAlembic ImporterがMayaから出力されたabcを使う前提だから[^2]か、Unity側の仕様なのかわかりません[^3]が、
ひとまずの解決策としては***カラーのアトリビュートはrgbaという名前でないといけない。***ということです。

なのでHoudini側でアトリビュート名を調整してあげると無事にカラーもUnityに反映されます。


<img width="1291" alt="スクリーンショット 2018-10-29 17.42.22.png" src="https://qiita-image-store.s3.amazonaws.com/0/79986/5a13968f-90cc-c1fc-b4bc-3036a7284929.png">

この画像はOutputの上のpointwrangle2を選択したときの状態です。
VEXの上から4行目までがアトリビュート名を変えて再代入している部分です。

```
addpointattrib(0, "rgba", {1, 1, 1, 1}, "color");
vector4 col = set(@Cd.r, @Cd.g, @Cd.b, 1);
setpointattrib(0, "rgba", @ptnum, col);
```
geometry spreadsheet上でも確認しましょう。
~~うろ覚えなのですが最初の行、addpointattribしているのは
***attribute typeをcolorにしたかった***からです。
setのときにtypeを指定できない[^4]のでまずcolorとして初期化したいという意図です。
spread sheetでみたときにrgba[0],rgba[1],,となるかrgba[r],rgba[g],,
となるかの違いなのですが0123だとカラーが表示されなかった気がします。~~
ちょっと自信ないので打ち消し線にしました。ひとまず上記のコードにしておけば
問題はないはずです。Attribute Createノードを使っても同様のことが
できると思います。

### カスタムアトリビュートについて
この項目に関してはきちんと理解して解決ができていなくて付け焼き刃的な方法になってしまいますが
一応たどり着けた解までは紹介しようと思います。
こんな方法あるよというのを知ってる方いたらご教授いただきたいです。。

どんな方法かというと、***uvという名前のアトリビュートもUnity側で自動で読み込めたのでuvに必要な値を格納する***です。

```
vector2 uv = set(@emissive, 1);
vector2 uv2 = set(1, @emissive);
setpointattrib(0, "uv", @ptnum, uv);
setpointattrib(0, "uv2", @ptnum, uv2);
```
上に先程出てきたVEXの画像の下4行を抜粋しましたが今回はUnityのスタンダードシェーダーで使う
emissionの値をhoudini側で格納して使いたいということをしています。
@ emissiveはここまでのノードで作成したpoint attributeです。それを"uv:という名前で
setpointattribしています。

ちなみに"uv"という文字以外にもあと1つ任意のvector2を格納できて、上記コードでいうと"uv2"という名前で
実験的にしています。


<img width="570" alt="スクリーンショット 2018-10-29 18.07.12.png" src="https://qiita-image-store.s3.amazonaws.com/0/79986/9832a747-9402-78dc-82c4-19d98f13cc17.png">
これも格納するにはrop atribbuteノードのGeometryタブ、Additional UV Attributesに任意の名前
(今回で言うところのuv2)を書けばできます。[^5]


alembicファイルには任意の名前でカスタムアトリビュート入れられているので、importerを調整するのか
Unityのシェーダーのattributeの受け渡しを調整するのかだと思うんですが
結構大変そうだし、自分のレベル的にもすぐにできるか怪しいのでひとまずはこういう形で我慢しました。
なので現状はカスタムアトリビュートは4つのみ格納可能ということになります。[^6]

## Unity側(応用)

上記設定をして出力をし直したabcを再度Unityにインポートします。
新規でマテリアルを作成し、シェーダーをAlembic/Overrayにします。
Alembic/Overrayで以下の画像のように値のデバッグをすることができます。
![10月-29-2018 18-25-22.gif](https://qiita-image-store.s3.amazonaws.com/0/79986/a7f6f56f-0049-125e-2639-9888e0eb0fc4.gif)

中のコードと見た目の変化でTEXCOORD0が"uv"TEXCCOORD1~に"uv2"の値がInputとして使えるのが確認できます。

```
vector2 uv = set(@emissive, 1);
vector2 uv2 = set(1, @emissive);
setpointattrib(0, "uv", @ptnum, uv);
setpointattrib(0, "uv2", @ptnum, uv2);
```
VEXコードを再掲しますが、uvとuv2でxとyを逆の値を入れてあるので正しく表示されてそうです。[^7]

とここまできたらあとはオリジナルでシェーダー書いておしまいなのでuvのx値をEmissionの値として使う
サーフェスシェーダーを以下にコピペしておきます。

```
Shader "Alembic/AlembicStandardColorUvEmissive" {
	Properties {
		_Glossiness ("Smoothness", Range(0,1)) = 0.5
		_Metallic ("Metallic", Range(0,1)) = 0.0
		[HDR]	
		_EmissionColor("Emission Color", Color) = (1,1,1,1)
		[HideInInspector]
		_UV2Tex ("Shine (RGB)", 2D) = "white" {}
	}

	SubShader {
		Tags { "RenderType"="Opaque" }
		LOD 200
		

		CGPROGRAM
		// Physically based Standard lighting model, and enable shadows on all light types
		#pragma surface surf Standard fullforwardshadows addshadow

		// Use shader model 3.0 target, to get nicer looking lighting
		#pragma target 3.0

		sampler2D _MainTex;
		sampler2D _UV2Tex;

		struct Input {
			float2 uv_MainTex: TEXCOORD0;
			float2 uv2_UV2Tex: TEXCOORD1;
			float4 color : COLOR;
		};

		half _Glossiness;
		half _Metallic;
		half4 _EmissionColor;

		// Add instancing support for this shader. You need to check 'Enable Instancing' on materials that use the shader.
		// See https://docs.unity3d.com/Manual/GPUInstancing.html for more information about instancing.
		// #pragma instancing_options assumeuniformscaling
		UNITY_INSTANCING_BUFFER_START(Props)
			// put more per-instance properties here
		UNITY_INSTANCING_BUFFER_END(Props)


		void surf (Input IN, inout SurfaceOutputStandard o) {
			float3 color = IN.color.rgb;
			float2 uv = IN.uv_MainTex.xy;
			o.Albedo = color;
			
			// Metallic and smoothness come from slider variables
			o.Metallic = _Metallic;
			o.Smoothness = _Glossiness;
			
			o.Alpha = 1.0;
			o.Emission = uv.x * _EmissionColor;			
		}
		ENDCG
	}
	FallBack "Diffuse"
}
```

ふつうに新規サーフェスシェーダーつくって、Alembic Importerのデフォルトのシェーダーみながらなんとなく改造していっただけです。
唯一ハマったのがサーフェスシェーダーではTEXCOORDは命名規則が決まっていて、
> 入力の構造体 Input には一般に、シェーダーによって必要とされるテクスチャ座標があります。テクスチャ座標の名前は、uv の後にテクスチャ名が来る形にする必要があります (第 2 のテクスチャ座標セットを使用するには、uv2 で始めます)。　　ーhttps://docs.unity3d.com/ja/current/Manual/SL-SurfaceShaders.html

ということなのでPropertiesにとりあえず値用意してInputにuv2を接頭辞とした変数つくって、、みたいなことしてます。
Unityのシェーダーの仕組み、必要に迫られたときに必要な分だけしか理解していないのでちゃんと知っておいたほうがいいなあと思いました。

## おわり
調べててもあんまり情報がなかったので記事にしてみましたが、
間違っている点や他に便利なやり方があれば教えていただけると助かります。


[^1]: 記述面倒になったため。。
[^2]: Maya前提というのはGitHubのREADMEを読んだ筆者の独断と偏見です。
[^3]: Alembic Importer側のコードを読むのも難しいし、Unityのシェーダーのシステムの理解も浅めなのでひとまず断念しました。詳しい方いたら教えていただきたいです。
[^4]: 方法を知らないだけです。
[^5]: ちなみに2つ以上できないかと思い、 "uv2,uv3,uv4"とかカンマで区切ってみたり試行錯誤した記憶があります。
[^6]: rgbaのaが1で固定でよければ5つ。色が単色でよければ8つです。
[^7]: 4つxyzwのうちzw成分はどこかの過程で抜け落ちているような挙動でした。疲れていたので気のせいかもしれません。