【Unity】C#で定義されたShaderGraphのカスタムノードが使えない時の対応

#はじめに
> 六角形タイルを作るShaderGraphカスタムノード · GitHub
https://gist.github.com/rngtm/d8b4bcd8785f24193bbadcf25325cdb4

こちらの六角形タイルを作るShaderGraphのカスタムノードが最近のバージョンのShaderGraphだとエラーが出るようになってしまいました。
どうやら、C#から自作ノードを定義する方法（CodeFunctionNodeを継承するやり方）は提供されなくなってしまったようです。
> Unable to create custom Shader Graph node due to inaccessibility of CustomFunctionNode class
https://forum.unity.com/threads/unable-to-create-custom-shader-graph-node-due-to-inaccessibility-of-customfunctionnode-class.586876/

その代わり、ShaderGraph内に任意の関数を作る「Custom Function」というノードが追加されているので、こちらを使ったところうまく行きました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113293/44022ed1-a368-5a92-99c1-fa34ed43ba22.png)
$\small{(街のモデルは「PolygonCity」のデモシーン)}$

この六角形タイルのノードはShaderGraphの作例を載せているサイトだとよく使われているので、対応方法を書いてみます。

> 【シェーダーグラフメモ その26】六角形シールド表現
http://r-ngtm.hatenablog.com/entry/2018/12/17/011642

#環境
Unity 2019.2.17f1
ShaderGraph 6.9.2

#出ているエラー

上記のHexagonNode.csをプロジェクトに置くと大量のエラーが発生します。

```error
HexagonNode.cs(8,28): error CS0122: 'CodeFunctionNode' is inaccessible due to its protection level
```

ネックなのはおそらくこちらのエラーです。CodeFunctionNodeにはアクセス出来ませんというエラーです。
ShaderGraph側からアクセスできないモノを何とかするのは怖いので、C#側からアプローチするのは難しそうです。

#対応

##CustomFunctionノードのための関数を記述する
C#でカスタムノードを定義する方法の代わりに、「CustomFunction」というShaderGraphのノードが追加されていますのでこちらを使います。

まず、C#カスタムノードで記述されている処理をShaderGraphの関数として記述し、.hlslで保存します。
（UE4のようにノードに直書きすることも可能です）

ShaderGraphの関数のルールについては以下の公式マニュアルに書いてあります。

> Custom Function Node
https://docs.unity3d.com/Packages/com.unity.shadergraph@6.7/manual/Custom-Function-Node.html

C#カスタムノードから書き直す際に気をつけるポイントは、

* 関数名の最後に"_float"をつける
* 引数の型はVectorXではなくfloatまたはfloatX
* 戻り値にはoutをつける

ルールに従って書き直すと以下の通りになります。

```HexagonNode.hlsl
///////////////////////////////////////////////////////////////////
//
// Original shader from: https://gist.github.com/rngtm/d8b4bcd8785f24193bbadcf25325cdb4
//
// added: hex cell pos, hex cell uv, hex cell index
//
///////////////////////////////////////////////////////////////////
void HexagonNode_float( float4 UV, float Scale, out float Hexagon, out float2 HexPos, out float2 HexUV, out float2 HexIndex ){
    float2 p = UV * Scale;
    p.x *= 1.15470053838; // x座標を2/√3倍 (六角形の横方向の大きさが√3/2倍になる)
    float isTwo = frac(floor(p.x) / 2.0) * 2.0; // 偶数列目なら1.0
    float isOne = 1.0 - isTwo; // 奇数列目なら1.0
    p.y += isTwo * 0.5; // 偶数列目を0.5ずらす
    float2 rectUV = frac(p); // 四角形タイル
    float2 grid = floor(p); // 四角形グリッド
    p = frac(p) - 0.5;
    float2 s = sign(p); // マス目の右上:(+1, +1) 左上:(-1, +1) 右下:(+1, -1) 左下:(-1, -1)
    p = abs(p); // 上下左右対称にする
    // 六角形タイルとして出力
    Hexagon = abs(max(p.x*1.5 + p.y, p.y*2.0) - 1.0);
    
    float isInHex = step(p.x*1.5 + p.y, 1.0); // 六角形の内側なら1.0
    float isOutHex = 1.0 - isInHex; // 六角形の外側なら1.0
    // 四角形マスのうち、六角形の外側の部分を補正するために使用する値
    float2 grid2 = float2(0, 0); 
    // 偶数列目と奇数列目を同時に加工
    grid2 = lerp(
        float2(s.x, +step(0.0, s.y)), // 奇数列目 (isTwo=0.0の場合はこちらを採用)
        float2(s.x, -step(s.y, 0.0)), // 偶数列目 (isTwo=1.0の場合はこちらを採用)
        isTwo) * isOutHex; // 六角形の外側だけ取り出す
    // 六角形の番号として出力
    HexIndex = grid + grid2; 
    // 六角形の座標として出力
    HexPos = HexIndex / Scale;
    // 六角形の内側ならrectUV、外側なら4つの六角形のUVを使う
    HexUV = lerp(rectUV, rectUV - s * float2(1.0, 0.5), isOutHex); 
}
```
#CustomFunctionノードの設定をする
hlslファイルを作ったら、ShaderGraph画面に移り、CustomFunctionノードを作成します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113293/ac05c698-4a2d-fda8-6b71-513eb77aed49.png)
ノードを作ったら、歯車アイコンをクリックして、設定パネルを出します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113293/105a3c97-5bca-b7b2-d27f-c60f21f739ad.png)
「Precision」はInheritのままでOK。
「Inputs」にはhlslに記述した関数の引数の中で「out」がついていないものを、「Outputs」には「out」がついているものを設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113293/fadc5427-d02e-7729-b726-b36d73f4fb61.png)
そして「Type」にFileを指定、「Name」は関数名から接尾の「_float」を除いた部分、「Source」に.hlslファイルを設定すればCustomFunctionノードの設定は終了です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113293/a9df63ea-7fa8-d078-0917-d949b91d27e1.png)
うまくいけば画像のように、ピンがつながるようになります。
動作をテストしてみた図↓
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/113293/e2abfb70-5838-8afc-87d7-4c70bc429fe7.png)

#おわりに

C#でカスタムノードを作れる方が、このCustomFunctionノードのセットアップを毎回やらなくて済むので良いと思うんですがなぜ廃止されてしまったんだろう…。C#の名前空間にVector1とかシェーダー系の定義が氾濫しちゃうからかな。