Reflex Shader 2.0の基本的な作りについて

こんにちは[Reflex (@reflex_1124)](https://twitter.com/reflex_1124)です。
![ReflexShader_h.png](https://qiita-image-store.s3.amazonaws.com/0/150773/8c8294a1-1d23-885b-8ffe-0c1878a99679.png)
早速ですが、Reflex Shader 2.0はご存知でしょうか？
Reflex Shader 2.0はVRChat用アバター向けシェーダーなのですが、

* クレリック・カジュアル - Pondero ponderogen
* Alcedo - Serasuzuna Sersz7
* EBA - Ogyno tuwill

等の販売アバターに採用されています。
VRChatの統一されてないライティング環境の中、できるだけCubed's Unity Shader等のVRChatにおける一般的なシェーダーの色味に近づけるように設計しています。

ダウンロード先 [Reflex Shader 2.0.1 - Booth](https://booth.pm/ja/items/1088971)
GitHub Repository [Reflex-Shader-2](https://github.com/reflex1124/Reflex-Shader-2)


## Amplify Shader Editor
Reflex Shader 2.0はAmplify Shader Editorにて開発されています。もともとRelfex ShaderはShader Forgeで開発していたのですが、ShaderForgeにはいくつか問題点がありました。

* コメントやグルーピング機能が無い・弱い
* シェーダーを複数開くことができない
* 落ちる
* サポートが終了している

このような感じでちょっと現状使い続けるには少し厳しいものが有りました。

Amplify Shader Editor(以下ASE)に変えたことによって上記のポイントがすべて解決しました。
そしていくつか他にも利点がありました。

* 公式サンプルとしてたくさんシェーダーが付属している
* 公式サイトのドキュメントが英語ながら非常に整理されていて読みやすい
* Shader Forgeでは１から作らなきゃいけないノードがプリセットとして用意されている

しかし問題点もあります。

* 公式以外のサンプルやチュートリアルがほとんど存在しない
* ノード名がShader Forgeと異なるのでForgeのノードをみてもよくわからない
* なんかアウトラインがバグい（あとで解説します）

## Reflex Shader 2.0 基本要素の解説
### Lighting
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/480febf9-d529-3fb2-2c2a-f4a5051d4b9c.png)
まずシェーダーの基本となるライティングノードです。
基本的にはShadeSH9に対してリアルタイムライトの明るさを加算しています。
ShadeSH9はASEのCustom Expressionノードで、
`return ShadeSH9(half4(0,0,0,1));`
を書いています。

IF分岐が入っているのは、ディレクショナルライトとポイントライトの場合で処理を分割したかったのが理由で、World Space Light PosノードにてDirectional LightとPoint/Spot Lightを判別しています。
ディレクショナルライトのLight Attenuationは次の項のShadowで処理されています。

### Shadow
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/2c5a2119-be26-a3d5-3258-e558b05c4af8.png)
これがShadowグループの全体図です。トゥーンシェーディングはすべてここで処理しています。
基本的にはASEの標準ノードである、Half Lambert Termによって影を生成しています。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/a257f6d1-debb-48f0-e7cc-8ef49036b95f.png)
左側のLerpノードで影に影響するノーマルマップの強度を指定しています。
ワールドノーマルにノーマルマップを入力したあと、Half Lambert Termに入力しています。
ここでLight AttenuationをHalf Lambertに乗算することでここで落ち影を出しています。
Light Attenuationを乗算するかは切り替えることができるようにToggle Switchで迂回できるようにしています。
上の方にあるV Dir Lightはワールドにディレクショナルライトがなかった場合指定した方向から擬似的にライトを当てるようにするものです。基本的にX=0,Y=0.6,Z=1に設定されていて、これはだいたいアバターの真正面からちょっと上くらいの位置です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/41fd513a-3d8f-2b8a-cc6e-f8c4e7be5326.png)
ここではコントラストを強くしたりと言った処理をしています。
ポスタライズ（階調化）も用意してあります。デフォルトでは2値化ですが、シェーダー内部のパラメーターを弄るとことで変更可能です。
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/5fde3a65-681a-42cf-dbed-fd2de49c93ee.png)
最後にここまでで作ってきたシャドウ部をを指定したカラーに変えます。
Shadow 1はShadow 2と比較（暗）で合成していて、Shadow 2がShadow 1より明るい場合Shadow 2が無効化されます。
マスクをしてStatic Switch (シェーダーバリアント)でオンオフできるようにしてShadow部は終わりです。

### Matcap
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/e728355c-d040-6900-dd8f-62f6703c4b81.png)
Matcapは単純でカメラ補正をかけたMatcapを切り替えられるようにしているのがポイントでしょうか。
またForce Matcap Toggleというものを用意していまして、このToggleをオンにすると、Diffuseテクスチャを黒く塗りつぶし、Matcapのみの描画になります。（金属光沢を出したいときに有効です）
Matcapは加算と乗算一つずつ用意しています。

### RimLight
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/804cf490-b1c4-f289-7416-bfd74261a5a6.png)
こちらも特に特別な処理はしていません。
視点ベクトルとワールドノーマルの内積を取って作る最も一般的なリムライトです。
リムライトにノーマルマップを反映させるか否かをチェックボックスで決めることができます。

### Diffuse
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/b3829c84-f4c4-8ea8-5c5d-84ddd7b8322f.png)
ここは特に書くことはないのですが、Diffuseテクスチャを読み込んでいる部分です。
Alphaを抽出して最終出力ノードのOpacity Clip/Opacity等につなげるようにしています。
Diffuseの値はMatcapのグループの中につながっています。

### Outline
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/27fa7d9d-0047-364a-46c7-84d6b376bfe9.png)
ASEで一番苦戦している部分です。
基本的にOutlineノードを使うのですが、このグループの中にはOutlineノードが存在しません。
現在開いているファイルはReflex Shader Functionなので、実際のシェーダーファイルではないのです。
なのでReflexShader.shaderの中にOutlineノードがあって、そのノードにわたす値をここで決めています。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/2ab79065-7a56-edf9-e68c-f2f688b22de0.png)

OutlineノードをVertex OffsetにつなぐとAdd Passでアウトライン用のパスが追加されます。
よって、このパスが存在する限りアウトラインを無効化することができません。なので僕のシェーダーにはアウトラインのトグルスイッチが存在しないことになります。

またこのOutlineノード、Returnにアルファ値が含まれていて、カメラで撮ってRenderTextureを読み込むとアウトラインの部分がTransparent扱いになり、色がなくなってしまいます。
よってVRChatのカメラの透過モードで撮影するとアウトラインの部分が透過されてしまいます。
これはShaderのコードを直接書き換えることによって対応しています。
s.Alphaを1に書き換えています。

これら以外の場所は基本的にシンプルで、Diffuseカラーに対して色と、明るさと、影を乗算しています。

### Emission
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/f087657d-0f8e-e491-236e-f4e68c9fb29a.png)
光り物を合成したりしているだけです。

### Composite
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/8aac1098-eb38-694b-779f-9d186297638b.png)
基本機能の最後に存在するのがコンポジットグループです。
ここでは今まで紹介してきたすべての処理を合成しています。
基本的に明るくなるものを加算、暗くなるものを乗算といった形で合わせています。
Force Emissive Toggleはマスクがかかっている部分のみ今までの処理をすべて無視するトグルスイッチです。

## おまけ
### Reflection
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/3aad70d1-c1f2-60c9-4cc9-407129e94619.png)
Reflex Shader 2.0.1の段階ではまだ実装されていませんが、一応入っています。
Indirect Specular Lightノードを使ってリフレクションプローブからの反射を使うことができます。

### Emissive Scroll
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/833b654e-92fb-1449-d2ca-12ee821e8efa.png)
光るやつです。
グラデーションをスクロールさせる方法と、画像をUVスクロールするものの二種類が入っています。

### ScanLine
![image.png](https://qiita-image-store.s3.amazonaws.com/0/150773/f8f6e7b9-ff37-dbb8-24bb-8a5e841bf46a.png)
下から上にラインがヴァーーーって走るアレです。
なんかうまいことやってジャンプしてもラインの位置がずれないようにしてるはずです。

## まとめ
すごくざっくりした説明でしたがなんとなくReflex Shader 2.0の作りを理解していただけたでしょうか？
このシェーダーでいちばん重要なのはLightingとShadowですので、そこらへんを観ていただけるとありがたいです。

Reflex Shader 2.0はMITライセンスにてオープンソースで公開されています。
また、ノードの情報も維持したまま公開しているので、Reflex Shader Function.assetをASEで開くことによってシェーダーをノードベースで改変することが可能です。一部コードを修正しないとバグが出るところがありますが、遊ぶ分には何も問題ないかと思われます。

またReflex Shader 2.0採用アバターもよろしくおねがいします。
[クレリック・カジュアル - ポンデロニウム研究所](https://booth.pm/ja/items/1090956)
[EBA - L.A.G](https://booth.pm/ja/items/1093731)
[SIRIA - 遥か風の向かう所](https://booth.pm/ja/items/1095369)
[LISA - ST-12ay 1am B](https://booth.pm/ja/items/1123775)
[亜久涼子 - 渡篠那間江](https://namaewatashino.booth.pm/items/1111224)
[さくら - ちゅばきのようちえん](https://tyubaki.booth.pm/items/1137369)

Reflex Shader 1.0採用アバター
[yell - 鯨井印の肉まん屋](https://booth.pm/ja/items/1048063)
[シャオマオ - オレンジみかん栽培所](https://booth.pm/ja/items/1014608)
[TetraBox - 三丁目の魔界](https://booth.pm/ja/items/1047627)