私家版Shader勉強のてびき。
主にUnityのシェーダーを中心に説明しているよ。でも、なるべく一般的なシェーダーの説明も書いたよ。
このてびきのゴール
読むとシェーダーというものの全体像が見えてきて、手を動かして実際に動くものを作るための入り口に立つこと。
まずはフラグメントシェーダーを使ってお絵描きするところから始めてみよう!というのが結論。
次に進む
資料集
シェーダーについて役に立つリンクを集めて整理してみたよ。
もくじ
なにができるの?
なにができるのかをもう少し
どんな道具を使えばいいの?
とりあえずやってみたい
意味がわからない用語が多すぎる
もうちょっと体系的に知りたい
よく出る関数
なにができるの?
シェーダープログラミングは、3Dオブジェクトのポリゴンに陰影をつけるものだよ。3DゲームやVRの用途などで利用するよ。シェーダープログラム単体で、計算式を組み立てると2Dのお絵描きや、レイマーチングのような手法を使った3Dプログラムが書けるよ。
GPUを使ってグラフィックを扱う専門のプログラム言語だから、CPUだけを使うのよりも高速に描画できるよ。
2018年現在だと、VR Chatでシェーダー芸が注目されているらしいよ。ジオメトリシェーダーというものを使ったりするらしいよ。 プログラムでお絵描きをするジャンルだと、他にもProcessingというものがあるよ。Java言語を使って計算式で動くグラフィックを作れるし、熟練した人はこれでゲームまで作っちゃうよ。
このページでは主にシェーダープログラミングでのお絵描きについて扱うよ。シェーダープログラムを始めようとすると、前提になる数学や3Dの知識、ツールとAPIなど、知っておくことが多くてハードルが高い。だけど、そこに到達するためにまずは「フラグメントシェーダー」でお絵描きをすることから始めると、慣れるための手がかりになるよ。
なにができるかをもう少し
シェーダーでできることについて、いくつかのキーワードと例を載せておくよ。
テクスチャーファイルを使わずに絵を描くことができる(座標と時間を使ったフラグメントシェーダー)
風に揺れる葉っぱや木の枝をプログラムで実現できる(頂点シェーダーやテッセレーションやジオメトリ)
キャラクターの顔にうつる光の色や影を調整できる(リムライト)
FPSゲームで壁の向こうにいる敵の輪郭を画面に描画したりできる(ステンシルバッファ)
眉を髪に隠れず描画するマンガ的・アニメ的表現(トゥーン、セル)でもステンシルが使われる
お絵描き
https://gyazo.com/24d5ee87e7b0612bfe22bc9302faf581
内積を使ってキャラクターを照らす
http://unity-chan.com/download/thumbnails/thumb_2017_05_01_11_11_54_.gif
パーティクル
http://assetstorev1-prd-cdn.unity3d.com/key-image/ce58de04-ccef-42d2-8e17-00c01d48246d.jpg
頂点シェーダーで葉っぱを揺らめかせる
https://qiita-image-store.s3.amazonaws.com/0/154678/dfb2d0fc-fd2e-d3b6-8acd-e7bb13e10779.gif
ディゾルブエフェクト
https://cdn-ak.f.st-hatena.com/images/fotolife/n/nn_hokuson/20170414/20170414203903.gif
どんな道具を使えばいいの?
GLSL言語とCg/HLSL言語というものがあるよ。このふたつはとても良く似てるので、どちらを勉強しても良いと思うよ。
GLSLは長年使われていて、シェーダーの達人たちはこれにとても詳しいよ。いろいろなプラットフォームで動くよ。
HLSLはUnityのシェーダーやMMDのエフェクト開発で使われるよ。古くからDirectXでゲームを作ってきた人なら、シェーダーはHLSLで書いているよ。
歴史が長いだけあって、資料も充実してるよ。GLSLの日本語チュートリアルはとてもよくまとまっているので、UnityでCg/HLSLを書く人でもすごく勉強になるよ。 Three.jsというJavaScriptのライブラリを使うと、WebページにGLSLのシェーダーで動くものを乗せることができるようになるよ。これはWebGLをベースに作られた便利なツールだよ。 数式は重要な道具だよ。
三角関数をグラフで見て、数字のきもちを知ろうとすることは重要だよ。
Desmosでは、数式を書くとそのグラフがすぐに表示される。すごい。 とりあえずやってみたい
以下の資料を読みながら手を動かして写経すると、いろいろとためになるよ。
HLSL
GLSL
どうすればいいんだ?というのはひどく迷う問題だ。
なので、追加のテキストを用意することにした。
手を動かしてみたいけど、どこから手を付ければいいの
フラグメントシェーダーを書こう。
すぐ上にある資料が役に立つ。
シェーダーわかんないけど、とにかくキャラクターのルックを良くするプログラムを書きたい
急がばまわれ。まずはともかく、フラグメントシェーダーを書くべきだ。
もし、Unityの上でキャラクターのルックを整えたいなら、UTSのセットアップ方法を調べてやってみるのがいい。
爆発とか煙とかを作りたい
さまざまなエフェクトを表現するのにはパーティクルが使われる。パーティクルはテクスチャ、半透明などを処理するシェーダー、そしてそれらの発生と動きを制御するシステムの組み合わせでできている。
UnityではParticle System(SHURIKEN)やVFX Graphが使われ、HoudiniやBISHAMONといったツールで作ることもできる。
パーティクルを使いたいならアセットを探すのが手っ取り早いけど、もし自作したりカスタマイズしたいならシェーダーを描く必要もあるかもしれない。
まったくシェーダーを描いたことがないなら、フラグメントシェーダーでお絵描きするところから始めるといいよ。
ShaderGraphとかShaderForgeというツールを使えば、簡単にシェーダーが作れるって聞いたけど
シェーダーの用語や関数に慣れていないと、ノードベースのエディタに触っても何もできないよ。
フラグメントシェーダーを書くといいよ。
VR Chatをよく遊ぶんだけど、シェーダー芸を使いこなして目立ちたい
悪いことは言わないからフラグメントシェーダーを書きなはれ。
意味がわからない用語が多すぎる
今は耐える時だよ。ひたすらググるしかないよ。
そんな冬の時代を少しでも短くするためにこの資料が書かれているよ。
用語の一覧を用意してみた。いずれはこれにわかりやすい説明を追記していく。でも、たぶんこれは永遠に終わらない作業だ。
ちょっと遅くなったけど、「シェーダー」というひとことにはいくつか解釈の仕方があるので、ここで補足しておくよ。
シェーダー「プログラミング」。GLSL、ShaderLAB、Cg/HLSLというプログラム言語を使って、グラフィック処理を行うプログラム。
GPUのシェーダー「ステージ」。GPUでグラフィックを扱う処理には種類があって、それぞれをシェーダーステージと呼ぶ。これらの代表的なものには、頂点シェーダー、フラグメントシェーダーがある。
GPUのシェーダー「ユニット」。GPUの中には計算処理を行うたくさんのシェーダーユニットが組み込まれている。
キャラクターモデルのルックを整えるシェーダー「セットアップ」。けもみみ女の子のテクスチャを指定し、影、半透明、エミッション、MatCapといった要素を設定する作業のこと。
もうちょっと体系的に知りたい
「シェーダーの概念をどうとらえればいいの?」
座標を使って色を決めるよ。
動きをつけたいなら時間をパラメータとして使うよ。
3Dで立体物を扱う時にテクスチャ画像を使ったりするよ。
これらを数式でいろいろ計算すると絵になるよ。
計算では「0から1までの間の数字を作る」ことになるよ。
sin関数で0から1までの間の数字を作るよ。
uv座標は0から1までの間で座標があらわされるよ。
色のRGBも0から1までの間で表現するよ。
「MVP」
座標の考え方がいくつかあるんだ。これについて調べてみよう。モデル、ビュー、プロジェクションという呼ばれ方がある。
行列をかけると変換ができるよ。ローカル座標、ワールド座標、カメラ空間座標、クリッピング空間座標というもののあいだを変換するよ。
Unityでシェーダーを書く時は、UNITY_MATRIXからはじまる行列を利用することができるよ。
「GPUレンダリングパイプライン!」
パイプラインの流れ。
パイプラインっていうのは、処理の流れを定義したものだよ。
グラフィックをGPUで扱う時には、何をどういう順番で計算するのかっていうのがあるよ。
GPUレンダリングパイプラインの詳しいことは、DirectXの資料とかを読むといいよ。同様の資料はOpenGLにもあるよ。
https://docs.microsoft.com/en-us/windows/desktop/direct3d11/images/d3d11-pipeline-stages.jpg
2018秋現在では、NVIDIAが発表した次世代GPUのTuringで、GPUレンダリングパイプラインの進化が期待されているよ。いよいよハードでレイトレーシングをサポートしてくれるような、新しい時代の幕開けになるよ。
シェーダーのステージについてもう少し。パイプラインで出てくるひとつひとつの処理をステージと呼ぶよ。
いろいろ省略して書くよ。
頂点シェーダー
頂点の座標を変換するよ。
テッセレーションシェーダー
プリミティブをさらに分割するよ。プリミティブっていうのはポリゴンのことだよ。
ジオメトリシェーダー
プリミティブを増やしたり減らしたりするよ。
フラグメントシェーダー
色を決めるよ。DirectXではピクセルシェーダーとも呼ばれるよ。
Zテスト
深度テストとも呼ぶよ。いま見えているものよりさらに奥にあるものはどうせ見えないから、もう計算しなくていいよね!っていう判断をするよ。
「サーフェスシェーダーというものを見かけました」
多分、Unityの話だろう。
Unityで新規のシェーダーファイルを作ろうとすると、大きく2つの種類を選ぶことになる。
https://gyazo.com/f4c9452da72ae44cfeec914a2d305af3
Unlitというのは、「アン」「ライト」という意味を指している。ライトの影響を受けないシェーダーのことだよ。
フラグメントシェーダーを使って、お絵描きだけをしたいのならこのシェーダーが向いているよ。
テクスチャの純粋な塗りだけでキャラを見せたい場合にも、Unlitシェーダーを使うことができる。
Surfaceシェーダーは、Unityが用意している便利な雛形だよ。ランバートやブリンフォンといった、スペキュラーやディフューズをそれっぽく描画してくれるライティング処理を、最初から使えるようにしてくれているよ。
「PBR、NPR」
物理ベースのレンダリングと、フォトリアルではないレンダリング。
PBRは物理に従った、リアルなレンダリング処理を行う(…とは言っても、計算機でぼくたちが住む現実世界をシミュレーションすることは、まだまだ実現できていないよ。現実を描画するには、ぼくたちの計算機は貧弱すぎるし、アルゴリズムもまだまだ未完成だ)。
NPRは実写っぽくないルックを指すよ。アニメ映像っぽいやつはトゥーンシェーディングと呼ばれたり、特にセル画のアニメっぽい表現をセルルックと呼んだりするよ。
「スペキュラー、ディフューズ」
手元につやのあるマウスとかノートPCとかがあるなら、あるいは光沢のあるディスプレイとか壁とかでもいいけど、それを眺めながら頭を動かしてみて欲しい。あ、部屋はよく明るくして。
いちばん光の反射が強く見えるハイライトの場所は、見る角度を変えると動いて見える。これがスペキュラー。鏡面反射とも言うよ。
今、ここには3つのベクトルがある。光源からオブジェクトに向かうベクトル、オブジェクトの表面から垂直に伸びる法線、そして視線(カメラ)からオブジェクトに向かうベクトル。
これらのベクトルから、光の反射をうまいこと計算することができる。もし、光源ベクトルとオブジェクト法線の内積を取れば、0から1までの数字を決めることができる。これに色を掛け算すると、オブジェクト表面に見える光と色を決めることができる。
光は壁とかいろいろな部屋のオブジェクトとかに反射して、反射して、反射して、そして減衰してそれ以上の力がなくなったっていうところで最終的な物体表面の明るさが決まるよ。ディフューズ、拡散反射。
もしもまじめにこれを表現するなら、おそろしい計算量になるので、画面が1枚作られるまでものすごい時間がかかるよ。
リアルタイムにやらなきゃいけないゲームやVRでは、そこまでの計算はしないように実装するよ。ライトの影をテクスチャにベイクしたり、空間にプローブを置いて計算を簡略化するよ。
SSS(サブサーフェススキャッタリング)やSSAO(スクリーンスペース・アンビエント・オクルージョン)は重要だよ。
調べてみるといいよ。
「フォワード、デファード」
たくさんライト(光源)があると、光の反射とかで計算量がたくさん増えるよ。計算量が増えるとFPSが落ちるよ。低いFPSでVRを遊ぶとVR酔いするかもしれないよ。
フォワードだとライトの灯数が多いとパフォーマンスが悪くなる。
デファードはフォワードの弱点をおぎなう。たくさんライトがあっても、最後に1回計算すればオッケー!ってやり方にしている。そのかわり、グラフィックメモリをたくさん使う。
「GI」
グローバルイルミネーション!光の計算にはすごくコストがかかるので、局所的に照明の計算をしてきたよ。グラフィックのハードウェア性能の向上と、計算手法の改善により、大域的な照明の計算ができるようになってきたよ。
「シャドウアクネ!!」
(このチュートリアルでは他にも行列とか、役に立ついろいろな話題がある) 「シェーダーを書くのに『公式』とか『定石』みたいなものってあるの?」
よく使う関数
step
ある価よりも大きいか?小さいか?によって1か0を返すよ。bool(if文)とか二項演算子のかわりにも使うよ。
sin
三角関数はとにかくすごく重要なんだ。あらゆるすべての基本になるし、周期的を数字を作ったりできる。
saturate
与えられた値を、0から1のあいだにおさめて結果を返してくれる。マイナスは0にしてくれるし、1を超えていたら1にしてくれる。0から1以外の範囲で扱う場合はclampを使う。
lerp
線形補完ってやつをしてくれる。ある座標と別の座標を知っていて、その間の座標を求めたい時に使えるし、色とかでも使える。GLSLだとmix関数を使うよ。
dot
内積を取って0から1までのあいだの値を返せるよ。これを使えばキャラの顔にライティングできるよ。
abs
絶対値を返してくれる。シェーダーでは0から1までの数字を作るということを思い出してみよう。三角関数で0から-1までのあいだの数字を、0から1までに置き換えることができる。
frac
小数部を返すよ。2.34を与えると、0.34が返ってくる。Timeを与えて、周期的な動きを作ったりできて、0から1までに値が変わっていった後、また0に戻って1に進む。
floor
ceil
切り上げ、切り捨てをして、与えられた数字の整数部を返すよ。天井とか床って表現をするところがまたいい。
pow
階乗を計算するよ。
sqrt
ルートを計算するよ。