Houdini Engine For Unityを色々試してみる 

Houdini Engine For Unity触ったことがなかったので
記事やチュートリアル参考に何ができるのかざっくり試してみました。

# はじめに
- まず前提としてHoudini Engineを利用する場合は、Houdini Indie以上が必要なようです。
- UnityEditor上で動作します。一応プレイ中でもビルドはできました。
- とりあえず導入のとっかかりとしては[Unity道場のこちらの動画](https://www.youtube.com/watch?v=Lz8xLqRHkbE)が参考になるかと思います。基本的にはHoudiniでHDA作成してUnityプロジェクトに配置してhierarchyにドラッグで準備完了です。
- ちなみにsteam版の場合、HoudiniEngineのUnityパッケージは以下にありました。
    - `Steamインストールフォルダ\steamapps\common\Houdini Indie\engine\unity\`
- 環境
    - Houdini Indie 19.0.455
    - Unity 2021.2.3f1

# メッシュ加工
渡したモデルをHoudiniに加工してもらう
<img width="512" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/9ef2c238-5918-5d3d-2cc1-e6c3536b9712.png">

### Houdiniネットワーク
<img width="256" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/cb61bf8d-0936-cbff-120c-b01e4ac81db4.png">
ついでにScriptをAttributeでアタッチ指定
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/c73d5188-cff7-e6ae-52eb-9fc17a6ebe08.png">
### Unity
<img width="512" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/8a1b523f-1b5d-7db0-c04c-f191df6ac530.png">

### 実行結果
合わせてScriptのアタッチも試しましたが、ちゃんとアタッチされてていてメッセージも送られてました。([公式ドキュメント](https://www.sidefx.com/docs/unity/_unity_scripts.html))
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/1546ad99-bf7f-cd1a-b2a8-60139390dfcd.png">

```
[ExecuteInEditMode]
public class HoudiniAttacheScript : MonoBehaviour
{
    void Msg(string text)
    {
        Debug.Log(text);
    }
}
```
![mesh_cloud_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/6cd7d6de-7bd4-d031-5f4b-f93899bbbab4.png)

GroupごとにアタッチするScriptを付け替える等活用できそうな気がします。

# カーブに沿ってMeshを作成・配置
Unity側でカーブを作成して、Houdiniに渡す。
内容的には真ん中のモデルをCurve上に均等に並べるだけです。

<img width="512" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/613470b6-30b7-a19c-b068-685724ebcbd8.png">
ということでやってみましたが、本来Editable NodesにCurveを追加して準備完了のはず(?)だと思っていたのですが
Unity側でカーブ編集のギズモが表示されなかったのでCurve Nodeは別で用意して、HDAパラメータに指定することでできました。
(HoudiniEngine → New Node → Curve)
### Houdiniネットワーク
<img width="300" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/784f27ee-74fd-b362-9dab-7ec47361a3f2.png">

### Unity
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/03f5fae3-d166-d22f-f678-abdb57f90496.png">
### 実行結果
ちなみにPackしているのでインスタンスもちゃんと分かれてます
![curve_05.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/1277c231-0ab6-e082-5cdd-d8d53b84cae5.png)
無事動作しましたが、カーブを編集してもリアルタイムに反映されなかったり、カーブ周りは色々不具合があるのかなという印象でした。(Rebuildするとちゃんと動く)

# Photoshop連携
[GDC2019の動画](https://www.youtube.com/watch?v=sDDruhbcEAk&t=402s)を見かけて、やってみたかったので。
<img width="512" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/102908a4-8a99-1912-eb32-7d4cecdb3765.png">
赤色がスポーン位置用レイヤー、橙色が道エリア用レイヤー、その他のエリアには木を生やすみたいな想定です。
<img width="512" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/06ebc404-a738-570d-95ff-2040f852ebf0.gif">


Photoshopのレイヤーごとにグループ化してTraceしてくれました。
<img width="256" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/14abeb49-b009-fc8e-1bb2-356041cca7fa.png">
### Houdiniネットワーク
<img width="700" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/552254cc-f139-be29-c162-1ced8ad13814.png">
### Unity
PSDファイルはUnity側からパスを渡してます。
![photoshop_03.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/dab5b49c-5fa0-25ea-d5ca-8e791fce38a8.png)
Unity側のモデルを渡し、UnityマテリアルもアタッチするようAttributeを設定してます。
![photoshop_04.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/0e192ddc-535c-2ebd-022c-d04bcfdddaa9.png)

# SDF出力チャレンジ
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/f7f1b44f-fb53-83ab-5410-9f1d7b1c0067.gif">

メッシュ以外に書き出しできないかと、割と強引?にHoudiniでSDFを出力してUnityに取り込んでレイマーチングしてみました。
SDFのレンダリングは[凹みさんのuRaymarching](https://tips.hecomi.com/entry/2020/06/28/152905)を使用させていただいてます。

### Houdiniネットワーク
Houdini側は最終的に「Volume Exporter」ノードを介すことでSDFファイル出力まで一貫して処理できました。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/eac5c81b-4a82-bcc1-3fc3-04be74b98c99.png">

また、なにも考えずHoudiniでSDF変換したためかメッシュによってボリュームの解像度が異なります。
なのでUnityでサイズを受け取り、Texture3Dに変換します。
Houdini側の準備としてボリューム解像度をAttributeに変換して、Unity側に返すよう設定します。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/b2cf2df5-6317-c039-0258-d378de42f884.png">
WrangleでAttribute化
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/9c972893-3b84-bf22-4691-8761a1f18b7c.png">
Unityに渡すAttribute設定([公式ドキュメント](https://www.sidefx.com/docs/unity/_attributes.html#AttributesStore))
<img width="450" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/6bec0657-12a2-cf6e-f0e4-fe9ffc28d9d1.png">


### Unity
Unity側は書き出し先の指定とRebuildのイベントを受け取りSDF(TGA)をTexture3Dに変換。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/26055398-db70-b728-68d1-e4623fb27cd4.png">

Rebuild完了イベントをUnityでキャッチして設定値を取得、Texture3Dに変換できれば完了なはずです。

```
    public void OnRebuilded(HEU_ReloadEventData data)
    {
        foreach(var o in data.OutputObjects)
        {
            var store = o.GetComponent<HEU_OutputAttributesStore>();
            if (store != null)
            {
                var volumeresAtt = store.GetAttribute("volumeres");
                var volumeres = volumeresAtt._floatValues;
                var tex = TGALoader.LoadSDFTGA(srcPath, volumeres [0], volumeres [1], volumeres [2]);
                AssetDatabase.CreateAsset(tex, destPath);
                AssetDatabase.SaveAssets();
                AssetDatabase.Refresh();
            }
        }
    }
```

# 実行結果
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/6340d271-f06a-8c3c-42d9-ac63324a52ff.png">
Houdiniから値がセットされていることを確認できます。上記のスクリプトで受け取りTexture3D生成処理に自動で渡ってます。
<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/67dd63cc-a3d6-1861-52b4-801dcb99de5a.png">

SDFのTGA→Texture3Dの変換方法が見つからなかったので地味に苦労しました。
[こちら](https://gist.github.com/mikezila/10557162)を参考に作成してみました。
[TGALoader.LoadSDFTGA()](https://gist.github.com/oudont3/bdb6f60ef793973dbc6f18178da76dc0)

<img width="400" src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/179625/fa858d17-0f14-042a-f9c2-c81878ce7800.gif">

結果的にできるはできたのですが、モデルによってはうまく出力されなかったり、
Objectを出力してないので「Unable to generate terrain due to lack of heightfield layers!」と怒られたり
Raymarching側でさらに調整したりと、まあうまくはいかなかったかなと。
(諸々理解不足だったことも大いにありますが。。)

# 感想
特に処理自体はHoudini単体でも実現はできるのですが、
Unityをベースにワークフローとして一定自動化できて便利そうです。
個人的には＋αで、Houdini側からComponentのアタッチやパラメータの受け渡しができることもメリットかと思います。

# 参考
- [Houdini Engine for Unityを使ってみよう - インストールからインスタンス配置まで - Unity道場2020 2月](https://www.youtube.com/watch?v=Lz8xLqRHkbE)
- [公式ドキュメント](https://www.sidefx.com/docs/unity/index.html)
- [Photoshop Level Design in Unity using Houdini Engine](https://www.youtube.com/watch?v=sDDruhbcEAk&t=402s)
- [Houdini Engine with Unity: Creating a Digital Asset for Unity](https://www.youtube.com/watch?v=putgmnGMGFk)
- [実践 Houdini Engine for Unity](https://note.com/aoi/n/n8fbfd54c349f)
- [SDF テクスチャを uRaymarching で描画してみる](https://tips.hecomi.com/entry/2020/06/28/152905)