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


この記事は、シェーダーアドベントカレンダー Advent Calendar 2019 23日目の記事です。

昨日は、@guilty_vrchat さんのVRChatで現実と同じ月齢の月を実装する でした

■ 概要

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)。

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

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

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

シェーダーの種類

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

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

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

■ LV0 はじめてのshader

秒速でShader使いになろう。

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

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

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

  • GLSL Sandbox
    色々な作品が投稿されています。色々数字を触ってみましょう。

  • 色々な2D図形の描き方
    Shaderではディスタンスフィールド(ファンクション)という方法で図形を描きます。

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

■ LV1 UnityではじめてのShader

 フラグメントシェーダーで絵を描く

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

 バーテックスシェーダー

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

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

■ LV2 応用1

 フラグメントシェーダーでノイズを使う

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

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

シンプレックスノイズ

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


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で使えます。

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

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

 ジオメトリーシェーダー

■ LV3 応用2

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

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

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

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

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

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

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

 バーテックスシェーダーでアニメーション

■ LV4 フュージョン

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

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

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

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

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

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

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

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

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

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

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

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

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

Shaderで計算機を作る

CustomRenderTextureだけでボリュームレンダリングしてみる!!

「Lead Shader」 原理解説 ~ 他のアバターの座標を取るシェーダー ~

Vacuous Park : Alter 技術解説

Unityでスクリプトを使わずに流体を計算する

参考ソースコード

booth(有料含む)

参考VRChat World

Unity Shader 参考記事

Unity 参考サイト

明日の記事は

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