マッハで学ぶ VRChat Shader LV5 習得 までのLV別 技術情報まとめ

この記事は、[シェーダーアドベントカレンダー Advent Calendar 2019](https://qiita.com/advent-calendar/2019/shader-advent-calender-2019) 23日目の記事です。

昨日は、@guilty_vrchat さんの[VRChatで現実と同じ月齢の月を実装する](https://qiita.com/guilty_vrchat/items/05b5ad0fa1ce3945c093)　でした

# ■ 概要
VRChatでshaderを学びたいが何からはじめていいか分からない人のためのまとめです。
ひとくちにシェーダーといっても、色々な種類があり、例えばイキナリGPUパーティクルからはじめると詰みます。
まず、はじめは一番簡単なフラグメントシェーダーからはじめましょう。

※ ちょと燃え尽きたので、レベル別参考資料リンク集、まとめ、と見てもらえればと思います。余裕があったらあとで、サンプル等を追加します。

# ■ 注意事項
* 2019/12/22時点でのVRChatで使われているUnityのバージョン(Unity Version 2017.4.28f1)前提で、記事を書いています。もうすぐUnity 2018に代わります。
* VRChatのSDKはC#のスクリプトが使えないなど色々制限されており、一般的なUnityの使い方では使わない手法が使われていることがあります。例えば、GPUパーティクルはC#必須のコンピュートシェーダーが使えないので、ジオメトリシェーダーで代用するとか。CustomRenderTextureは外部から更新できない(VRChat的に出来ないだけでC#からなら更新できます。テクスチャーから入力は出来ます。)

# ■ Shaderとは

そもそもShaderとは、ものすごくざっくり説明すると、光の方向からポリゴンの表面に陰(Shade)を付ける為のプログラミング言語です。だからshad(er)。

![shade.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/267a065c-80c1-f217-1157-9af3f24f993a.png)

たとえば、shaderに光の計算をしないで黒でべた塗りするように指示すると

```c#
Shader "Fill Black" {
	SubShader{
		Pass{ Color(0,0,0,0) }
	}
}
```

![black.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/0ee161c7-7442-cd82-4106-6c7d3ed6159a.png)

ポリゴン全体が真っ黒に塗られ、陰影が無くなって全く立体感が無くなります。

# シェーダーの種類

* フラグメントシェーダー
* バーテックスシェーダー
* ジオメトリシェーダー
* テッセレーションシェーダー
* コンピュートシェーダー

ポリゴンの表面に陰を付けるメインと言えるシェーダーを『フラグメント(ピクセル)シェーダー』と呼んでいますが、他にも、ポリゴンの面を構成する頂点(バーテックス)を操作する『バーテックスシェーダー』があり、この2つが基本のシェーダーとなります。さらに頂点(バーテックス)を増やしたり、変更したりしたいという要望から『ジオメトリーシェーダー』、ポリゴンの面を細かく分割する『テッセレーションシェーダー』、シェーダーの高速な演算能力をパーティクルなどの演算に使用したりする『コンピュートシェーダー』※ C#必須なので、残念ながらVRChatでは使えません。代わりにジオメトリシェーダーとRenderTextureとCamera、又はCustomRenderTextureを使用してコンピュートシェーダーと同じような仕組みを作ってGPUパーティクルを実現しています。一般的なUnityのGPUパーティクルはコンピュートシェーダーを使っているので注意が必要です。

そして、もはや、shade(陰)とは全く関係なくなっていますが、shaderの処理できる内容は、対象(頂点なのか、画像のピクセルなのか)が違うだけでほぼ同じです。

# ■ LV0 はじめてのshader

秒速でShader使いになろう。

UnityでShaderをはじめるのも良いのですが、Unityを立ち上げ、プロジェクトを作り、モデルを配置したり、Shaderのファイルを作ったり、マテリアルを作ってモデルに適用したりと、Shaderをはじめるにあたって、ちょとした作業が必要になります。

まずは、ブラウザで気軽に始められるフラグメントシェーダーを触ってみましょう。

* [The Book of Shaders](https://thebookofshaders.com/?lan=jp)

Shaderの教科書です。サンプルのコードを直接書き換えるとリアルタイムに反映されます。

* [GLSL Sandbox](http://glslsandbox.com/)
色々な作品が投稿されています。色々数字を触ってみましょう。

* [色々な2D図形の描き方](https://qiita.com/7CIT/items/fe33b9b341b9918b6c3d)
Shaderではディスタンスフィールド(ファンクション)という方法で図形を描きます。

これで、あなたも今日からShader使いです。

# ■ LV1 UnityではじめてのShader
## 　フラグメントシェーダーで絵を描く

ブラウザで学んだフラグメントシェーダーのお絵かきをUnityでもやってみましょう

* [GLSLをHLSLに書き換える](https://qiita.com/kitasenjudesign/items/89297f239059662cd38e)

* [「楽しい！Unityシェーダーお絵描き入門！」という講演をしました](https://setchi.hatenablog.com/entry/2018/04/15/195928)
* [【Unityシェーダ入門】円やリングをかっこよく動かす方法](http://nn-hokuson.hatenablog.com/entry/2016/11/14/203745)

## 　バーテックスシェーダー
* [【Unityシェーダ入門】シェーダで旗や水面をなびかせる](http://nn-hokuson.hatenablog.com/entry/2017/04/04/201537)

## 　テッセレーションシェーダー

ようは、ポリゴンの面を分割して滑らからな表面を作るということですね。
主に、バーテックスシェーダーで波打たせるときに併用されます。

* [【Unity】テッセレーションシェーダを導入する](http://baba-s.hatenablog.com/entry/2018/08/31/170000)
* [Unity で距離に応じたテッセレーションを行ってみた](http://tips.hecomi.com/entry/2016/11/28/232726)

# ■ LV2 応用1
## 　フラグメントシェーダーでノイズを使う

* [【Unityシェーダ入門】シェーダで作るノイズ５種盛り](http://nn-hokuson.hatenablog.com/entry/2017/01/27/195659)

基本的にこちらのサイトの内容を抑えておけばオッケーなので、特に解説しませんが、補足をしておきます。fbmはノイズの種類ではありますが、一口にfbmと言っても元のノイズをどのノイズにするかで結果が変わってきます。fbmはあらゆるノイズに適用可能なテクニックです。fbmというと通常はパーリンノイズをfbmにしたものを指しますが、パーリンノイズは重いのでバリューノイズをfbmすることもあります。もちろん、blockノイズもfbmすることが出来ます。全く使い道がないので、使われることはありませんが...。

* Unityで実行できるパッケージを用意しましたので参考にして頂ければと思います。
 * ランダムノイズ
 * ブロックノイズ
 * バリューノイズ
 * パーリンノイズ ※ 重い
 * ブロックノイズ(fbm) ※ 出来るけど、使い道がありません 
 * バリューノイズ(fbm) ※ わりと使える ノイズの品質はちょっと難あり
 * パーリンノイズ(fbm) ※ベースのノイズが重いので、重ねたことで激重に

![noise.PNG](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/135381/662b4931-6d60-9327-e61b-1a6d0d781efa.png)

https://github.com/it7c/UnityShaderNoiseBasicSample

### シンプレックスノイズ
パーリンノイズを改良したシンプレックスノイズというものもありますが、[特許](https://patents.google.com/patent/US6867776)取得済みであり、特にテクスチャの合成に使うとまずそうです。プロシージャルテクスチャとして単体で使う場合は大丈夫とのことですが、各自自己責任で使ってください。特許問題を解決したシンプレックスノイズ、オープンシンプレックスノイズというものもあるのですが、Shaderでの実装を見つけることができませんでした....。

[
Procedural Gas Giant Rendering with GPU Noise](https://www.reddit.com/r/gamedev/comments/32ews1/procedural_gas_giant_rendering_with_gpu_noise/) から引用

> 上記のアルゴリズムのいずれかをGLSLで自分で実装するか、非常に便利なAshima Simplex GLSLコードを取得できます。これは、AshimaがMITライセンスでリリースしたものの、特許取得済みのSimplex Noiseの実装であることに留意してください。ただし、テクスチャ合成にはこれを使用していないため、良いはずです。繰り返しますが、私は弁護士ではありません。これは最速または最高品質のGLSL実装ではありませんが、セットアップをまったく必要としないため、プロジェクトに組み込むのが間違いなく最も簡単です！

> You can either implement one of the above algorithms yourself in GLSL, or you can grab the incredibly handy Ashima Simplex GLSL code. Keep in mind this is an implementation of the patented Simplex Noise, even though Ashima released it under the MIT license, though since we aren't using this for Texture Synthesis we should be good. Again... I'm not a lawyer. This isn't the fastest or best quality GLSL implementation, but it's definitely the easiest to pop into your projects, since it requires no setup whatsoever!

Unityで使いたい場合は、keijiroさんの[Noise Shader Library for Unity](https://github.com/keijiro/NoiseShader)で使えます。

* The book of shader
 * [ノイズ](https://thebookofshaders.com/11/?lan=jp)
 * [セルラーノイズ](https://thebookofshaders.com/12/?lan=jp)
 * [非整数ブラウン運動(fbm)](https://thebookofshaders.com/13/?lan=jp)

Unityのshader言語とは異なりますが、ブラウザで使われているglslというshader言語の解説です。
ノイズの解説として参考になると思います。

* [ノイズの話](http://c5h12.hatenablog.com/entry/2014/07/06/084125)

shaderに限定しないノイズについての解説です。

## 　ジオメトリーシェーダー 
* [【Unity】カメラが近づくと弾けるポリゴン分解シェーダー](https://styly.cc/ja/tips/polygondestruction-go-shader/)

* [ワイヤーフレームシェーダー](http://www.shaderslab.com/demo-94---wireframe-without-diagonal.html)

# ■ LV3 応用2

* [FuwaParticle 解説](http://phi16.hatenablog.com/entry/2018/10/03/223758)

## 　フラグメントシェーダーでレイマーチング

* [魔法使いになりたい人のためのシェーダーライブコーディング入門](https://qiita.com/kaneta1992/items/21149c78159bd27e0860)

レイマーチングの仕組みについてはこちらが詳しいです。(glsl ブラウザの方のshaderになります。) 要するにshaderにポリゴンの表面に2Dで絵が描けるのならば、ポリゴンの表面に3Dを描けるよねという話です。なので、3Dの空間の中に3D描かれている状態になります。なので、このままだと、ポリゴンと、レイマーチングの奥行の位置関係がおかしくなります。

Shaderではディスタンスフィールド(ファンクション)という方法で図形を描きましたが、レイマーチングの3Dモデルもこのディスタンスフィールドを2次元から3次元に拡張したものになります。

* [【Unity】レイマーチング超入門チュートリアル前編。板ポリに球体を描く【ライティングあり】](https://gurutaka-log.com/ray-marching-tutorial)

Unityで、まずは球を描いてみましょう。

* [プリミティブ(基本3Dモデル)一覧](http://iquilezles.org/www/articles/distfunctions/distfunctions.htm)

球体以外も書いてみましょう

## 　ジオメトリーシェーダーで新しいポリゴンを作る

モデリングデータをUnityに持ってくるときに自動的に、三角形のポリゴンに割ってくれますが、ジオメトリーシェーダーでポリゴンを作る場合は、そんなことはしてくれないので、まず設計する必要があります。難度高めです。

* [growCrystalsでやっていること[考えたこと]](https://tonoshake.hateblo.jp/entry/2019/11/14/221743)

* [Grass Shader using Unity engine 2018.3](https://roystan.net/articles/grass-shader.html)

## 　バーテックスシェーダーでアニメーション
* [頂点シェーダーアニメーション入門](https://docs.google.com/presentation/d/1F7HAdPVSmZ159egi4RBpUVtLZ0_9s_NcwUAcXBIY0cM/edit#slide=id.g45c1167e43_1_67)

# ■ LV4 フュージョン

色々なシェーダーテクニックの複合技です。

##　フラグメントシェーダー 深度付きレイマーチング

レイマーチングはポリゴンの表面に3Dを描くものなので、通常は奥行が合いません。
深度情報を元に、ポリゴンとレイマーチングを合成する方法が深度付きレイマーチングです。

* [yagiriさんの深度つきレイマーチングサンプル](https://yagiri.booth.pm/items/1617941)
* [凹みTipsさんのUnity でオブジェクトスペースの Raymarching をフォワードレンダリングでやってみた](http://tips.hecomi.com/entry/2018/12/31/211448)

## 　ジオメトリーシェーダー + Custom Render Texture or (Camera + Rendertexture)で GPU パーティクル

通常、Unityで入力によって状態は変化するGPUパーティクルを作る場合はコンピュートシェーダーを使いますが、VRChatではC#のスクリプトが使えません。コンピュートシェーダーはC#のスクリプトから使うことが前提なのでVRChatではコンピュートシェーダーが使えません。代わりジオメトリシェーダーと(カメラとRenderTexture)またはCustomRenderTextureを使って実装しています。

* [【VRChat】GPUパーティクル同士が線で結ばれるワールド「Simple Plexus」の解説](http://kaiware007.hatenablog.jp/entry/2019/12/02/000221)

# ■ LV5 人外 神 人知を超えたなにか

## 　テッセレーション HUL/DOMAINシェーダーを直接書く

単純にポリゴンを分割したいだけなら、標準のテッセレーションの機能だけでUnityが勝手にやってくれるのですが、オリジナルの分割処理をしたい場合は、直接テッセレーションシェーダー(実は内部的には、HULシェーダーとDOMAINシェーダーに分かれている)を書かなければならずとたんに難易度が跳ね上がります。

良くあるのが三角形のポリゴンを分割する場合、どんどん分割が汚くなるので四角形のポリゴンを分割したくなるのですが、その場合、オリジナルのテッセレーションシェーダーを書かなければいけなくなります。※ メッシュ作成、インポート時にメッシュのタイプを四角ポリゴン(quad)にしておく必要があります。

以下、四角形のポリゴンを分割するための参考資料です。

* https://catlikecoding.com/unity/tutorials/advanced-rendering/tessellation/

三角ポリゴンだと蜘蛛の巣のように汚く分割されるので

* https://polycount.com/discussion/152326/unity3d-quad-tessellation

こちらのように綺麗に分割したい

* [quad_tessellation.shader](https://github.com/przemyslawzaworski/Unity3D-CG-programming/blob/master/quad_tessellation.shader)

Unityでの四角形のポリゴンを分割するシェーダー
※ メッシュのタイプが四角ポリゴン(quad)でないと正常に動作しません

* https://www.gamedev.net/articles/programming/graphics/d3d11-tessellation-in-depth-r3059/
* http://www.defectivestudios.com/devblog/quad-patch-tessellation-in-unity/
* https://fgiesen.wordpress.com/2011/09/06/a-trip-through-the-graphics-pipeline-2011-part-12/

## [Shaderで計算機を作る](http://phi16.hatenablog.com/entry/2019/09/02/010324)

## [CustomRenderTextureだけでボリュームレンダリングしてみる!!](https://qiita.com/konchannyan/items/f2a0a7320dba83fdf7da)


## [「Lead Shader」 原理解説 ～ 他のアバターの座標を取るシェーダー ～](https://twitter.com/konchannyan/status/1122847816202637313)

## [Vacuous Park : Alter 技術解説](https://qiita.com/phi16_/items/2d6e063f257a12d2ea01)

## [Unityでスクリプトを使わずに流体を計算する](http://el-ement.com/blog/2018/12/13/unity-fluid-with-shader/)

# 参考ソースコード
* [phi16さんのVRChat shaderコード github](https://github.com/phi16/VRC_storage)
* [ButadieneさんのGPUPTrail github](https://github.com/Butadiene/GPUPTrail)
* [BUTADIENE WORKS](https://drive.google.com/drive/folders/15eiiQGh41pdKSzMrLBLJc0hKLd03aH2i)
* [HhotateA github](https://github.com/HhotateA)

# booth(有料含む)
* https://noriben.booth.pm/
* https://hhotatea.booth.pm/
* https://voxelkei.booth.pm/
* https://virtual-boys.booth.pm/
* https://phi16.booth.pm/
* https://kususu.booth.pm/
* https://yagiri.booth.pm/
* https://kaiwarestore.booth.pm/
* https://rakuraiworks.booth.pm/

# 参考VRChat World
* [Shader Fes 2019](https://www.vrcw.net/world/detail/wrld_f5a298f6-83e6-45ee-be58-4fafbbd38139)
* [Simple Plexus](https://vrchat.com/home/launch?worldId=wrld_2d9c2fb3-8306-4b02-bac0-459fbb99d83e)

# Unity Shader 参考記事
* [VRChat 技術メモ帳 - VRChat tech notes Shader関連](https://vrcworld.wiki.fc2.com/wiki/Shader%E9%96%A2%E9%80%A3)
* ~~yagura Lab. シェーダー~~ 2019/12/22 [閉鎖](https://note.com/ygr/n/nd431f70c141a)
* [凹みTips Unity のシェーダの基礎を勉強してみたのでやる気出してまとめてみた](http://tips.hecomi.com/entry/2014/03/16/233943)
* [7日間でマスターするUnityシェーダ入門](http://nn-hokuson.hatenablog.com/entry/2018/02/15/140037)
* [Unityシェーダーによる3Dモデルのアレンジ方法](https://styly.cc/ja/tips/shadedcity_discont_shader/)
* [Unityを利用した空間スキャンライン表現](https://styly.cc/ja/tips/unity-shader-worldscanline/)
* [Shaders Laboratory](http://www.shaderslab.com/shaders.html)

# Unity 参考サイト
* [おもちゃラボ](http://nn-hokuson.hatenablog.com/)
* [凹みTips](http://tips.hecomi.com/)
* [テラシュールブログ](http://tsubakit1.hateblo.jp/)
* [LIGHT11](http://light11.hatenadiary.com/)
* [コガネブログ](http://baba-s.hatenablog.com/)

# 明日の記事は

@Ka-punnさんの『SubstanceDesignerとUE4を使ってTV Pixelをシェーダーで再現する記事を書こうと思います』です。