Shader Labについての個人的なまとめ
今更感が強いけれども、いくらビジュアル的にコードが組めるようになったとしても基礎は大事ということで勉強しました。自分がまとめたいと思っているところしかまとめてません。
参考:公式ドキュメント
レンダリングパイプラインの前提の話について
参考サイト→https://yttm-work.jp/shader/shader_0002.html#head_line_08
実際の話に入る前に基礎的な前提の話。シーンに置かれたゲームオブジェクトは以下のような流れでレンダリング処理を行われる。
プログラマーは主に固定処理以外の青い部分を改変できるようになっている。
〇カリング
全てのオブジェクトには表面と裏面があり、裏面が見えることはほとんどの場合ないため、裏面を処理の対象外に指定する処理で、カリングステージで行われる。
〇深度テスト
カメラにもっとも近いものだけを描画できているかテストする。テストの際に深度の情報をためておくメモリ空間またはその技術のことをZバッファ、またはデプスバッファと呼ぶ。
〇アルファテスト
透明度が高いものよりも低いものを優先して描画できているか確かめるテスト。葉っぱのテクスチャような、目的のもの以外を完全にないものだと考えたい場合にこれを利用する。
〇ステンシルテスト
ステンシルバッファと呼ばれる描画する場所を指定するマスクのようなものを事前に設定しておいて、ステンシルバッファで指定された部位だけ描画できているかのテスト。よくわからないけどポストプロセスなんかで使われるものなんだろうか。
〇ブレンディング
深度テストをオフに設定してあるシェーダーが表示するピクセルでは複数の色が同じピクセルに乗ることになる。ブレンディングを設定しておくことで、二つの色を混ぜ合わせることができる。具体的に言うとガラスの裏にオブジェクトがあれば、カメラから見るとガラスの色で白みがかったオブジェクトが見える、という感じだ。
UnityでShaderを書く方法
①サーフェスシェーダー
簡潔な書き方で複雑なシェーダーがかける。だいたいのことは自動生成されるっぽい。しかしサーフェスシェーダーは光の影響をうけてしまうので、unlitなシェーダーを書きたい場合はこれは使えない。
②頂点シェーダー、フラグメントシェーダー
レンダリングパイプライン内で固定処理を除く、プログラマーが処理を改変できる頂点シェーダーステージとフラグメントシェーダーステージを書く方法。必要なエフェクトを柔軟に作成することができるが、より多くのコードを書かなければならない。
③固定関数シェーダー
ちょっとよくわからないが、DirectX9以前にあったもので、今更使うことはないと誰かが言っていたので無視しても良いだろう。
Shader Labとは
Microsoftのシェーダー言語、HLSLを用いてUnityが独自で作ったシェーダーを書き換えるための言語。Shader Lab自体はシェーダー本体の構造を整理するのに使われている。
HLSL部分は完全なHLSLというわけではなく、一部セマンティクスや型が異なる。下で詳細について書く。
【Shader Lab構文】基本構造
//Shaderの名前を宣言する。1ファイル一つだけ置かなければならない。
Shader "name" {
Properties {
_MyTexture ("My Texture", 2D) = "white" { }
// colors や vectors などの他のプロパティーもここに置けます
}
SubShader {
// ここに
// - サーフェスシェーダー か
// - 頂点シェーダーとフラグメントシェーダー か
// - 固定関数シェーダー を置きます
}
SubShader {
// 古いグラフィックスカードでも実行できるように
// 簡単なバージョンの SubShader をここに置きます
}
//SubShadeは任意の数置ける。
}
①Shader "シェーダー名"{ }
シェーダーの名前を宣言し、このブロック内にシェーダーの中身を記述していく。1ファイルに1つだけこのブロックをいれなければならない。
②Properties{ }
マテリアルインスペクターに表示され、いじることができる色やテクスチャのような変数を宣言する。
③SubShader{ }
Shaderブロック内に最低一つは必要で、シェーダーの実際の処理が記述されるブロック。SubShader内に様々な条件を付けて置き、実際にマシンがShaderをロードしたとき、自分のマシンのスペックにあったSubShaderを使用することができる、というもの。
【Shader Lab構文】Propertiesブロックについて
ここではあくまでUnityから持ってきたデータをHLSLに渡す役割なので、HLSLでの記法や型とは全く異なるので注意。
name= shader_labで使われる変数名
inspector_name= inspectorで表示される変数名
default_value = デフォルト値
//数字プロパティー
////スライダーの範囲を読み込む
name("inspector_name", Range(min,max)) = default_value
////float型もしくはint型を読み込む
name("inspector_name", Float) = default_value
name("inspector_name", Int) = default_value
//カラーとベクトル
///カラー値と4次元ベクトルを作成する
name("inspector_name", Color) = (value1,value2,value3,value4)
name("inspector_name", Vector) = (value1,value2,value3,value4)
//テクスチャ
///それぞれのテクスチャを作成する。
///defaulttextureはwhiteなどで無地色を指定しておくことが一般的
name("inspector_name", 2D) = "defaulttexture"{}
name("inspector_name", Cube) = "defaulttexture"{}
name("inspector_name", 3D) = "defaulttexture"{}
またプロパティーのすぐ手前に以下のような属性を付与することで扱い方を指定することができる。
[HideInInspector]..インスペクターに表示しない
[NoScaleOffset] ..インスペクターにタイリング/オフセット項目を隠す
[Normal]..テクスチャーが法線マップであることを示す
[HDR]..テクスチャーがHDR画像であることを示す
//以下説明省略
[Gamma] [PerRendererData] [MainTexture] [MainColor]
【Shader Lab構文】SubShaderブロックについて
SubShader内には一つまたはそれ以上のPassを定義する。PassはそれぞれStateというものが定義されている。PassのStateをすべて共通の値にしたい場合SubShaderのCommonStateで定義できる。
SubShader
{
Tags{ //いつどのようにレンダリングエンジンでレンダリングするのかを指定 }
[CommonState] //すべてのPassで共通のstateの設定
//詳細についてはPassブロックの説明の時に行う
Pass
{
//単一のPassブロックでジオメトリを一回レンダリングする
}
//Passは任意の数置けるが、処理が重くなる。
}
【Shader Lab構文】Tags(SubShader内)ブロックについて
KeyとValueペアになっているタグを用いてレンダリング方法を指定する。
Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
Pass内のタグはパスがShaderの中でどのような役割であるかを定めるのを主な目的として使用される。タグにはSubShaderブロック内に書き込まなければならないものと、Passブロック内に書き込まなければならないものがあるので注意。
以下は必ずSubShader内に書き込まなければならない。
● Queuタグ
出力マネージャーステージとの兼ね合いで表現したいオブジェクトによっては他のオブジェクトの処理が終わった後に処理を始めるような設定をしないといけない場合がある。もしくは順番を決めた方が効率的になることがある。このタグで他のシェーダーとの処理順を決められる。①~⑤の順で処理されていく。
① Background...
背景のような最奥にカメラから見て最奥のオブジェクトのようなものを指定する。
②Geometory...
一般的な設定。不透明なジオメトリはこれを使用する。
③AlphaTest...
アルファテストを行いたい場合これを指定する。
④Transparent...
ガラスのような背後のオブジェクトの色と、自分自身の色をブレンドしたい場合これ。その場合このシェーダーは深度テストをオフにしておかないと、背後のオブジェクトが消える。
⑤Overlay...
レンズフレアのようなオーバーレイ効果を出したいものはこれ。最も後に描画される。
またそれぞれの値は内部的に整数として考えられていて、以下のようにすれば細かい処理順を決めることもできる。
Tags { "Queue" = "Geometry+1" }
ちなみにそれぞれの整数値は Background は 1000、Geometry は 2000、AlphaTest は 2450、Transparent は 3000、Overlay は 4000となっている。
● RenderTypeタグ?
定義済みのいくつかのグループに分類される。
●PreviewType タグ
インスペクターでのマテリアルの表示を変更する。
【Shader Lab構文】Passブロックについて
Pass
{
Name "PassName" //他のシェーダーから呼ぶときの名前
Tags
{
//いつどのようにレンダリングエンジンでレンダリングするのかを指定
}
[RenderState] //様々なグラフィックスハードウェアのstateを設定する。
CGPROGRAM
//実際の処理をHLSLで記述する場所
ENDCG
}
〇Nameについて
名前を付けておくことで、外部からUsePassで参照できるようになる。
【Shader Lab構文】RenderStateについて
様々なグラフィックスハードウェアのStateを設定します。(公式マニュアルより)。レンダリングパイプライン内の固定処理の部分のオプションみたいなものだと思う。Passですべて共通のものを指定したいときはSubShaderブロックに書き込むことで、まとめて指定できる。
〇 Cull
Back: 背面をカリング
Front: 表面をカリング
Off: カリングオフ
〇 ZWrite
On/Off: Zバッファ(深度バッファ)への書き込みを行う(深度テストを行う)
〇 Ztest
Less | Greater | LEqual | GEqual | Equal | NotEqual | Always
深度テストの判定方法。デフォルトはLEqualで既に描画されているオブジェクトと距離が等しいもしくは、より近い場合に合格とする。
〇 Offset OffserFactor , OffsetUnits
二つのパラメータで深度のオフセットを決める。よく動きが分かっていない。
〇 Blend
複数のシェーディングをブレンドする。とんでもなくややこしかったので、まだまとめられていない。→参考
【Shader Lab構文】Tags(Pass内)について
KeyとValueペアになっているタグを用いてレンダリング方法を指定する。
Tags { "TagName1" = "Value1" "TagName2" = "Value2" }
Pass内のタグはパスがShaderの中でどのような役割であるかを定めるのを主な目的として使用される。以下のオプションはSubShader内のオプションではなく必ずPassのTagに書き込まなければならない。
● LightModeタグ
ライティングパイプラインでのパスの役割を定義する。ライティングパイプラインについては本テーマからそれるためこちらを参照。
ただし、ライティングを使用するオブジェクトは基本的にサーフェスシェーダーを使うことが多いため、このタグを手動で適用することは稀である。
①Always
②ForwardBase
③ForwardAdd
④Deffered
⑤ShadowCaster
⑥MotionVectors
⑦PrepassBase
⑧PrepassFinal
⑨Vertex
⑩VertexLMRGBM
⑪vertexLM【HLSL構文】データの型、セマンティクス
● PassFlagsタグ
パスはフラグを使って、レンダリングパイプライン がどのようにデータをパスに渡すかを変更することができます。
● RequireOptionsタグ
特定の外部条件が満たされた場合のみレンダリングすることを、パスで指定することができます。
【Shader Lab構文】そのほかの記法について
上でPassの一般的な記法について説明したが、それ以外にもPassを作ることができる。
①UsePass
Passにnameを設定しておけば、UsePassコマンドを使って別のシェーダーの中でパスを挿入することができる。
UsePass "Shader/Name"
②GrabPass
特別なパスのタイプで、描画されるときの画面のコンテンツをテクスチャに保存する、レンダーテクスチャのようなもの。やや本テーマと離れるためこちらを参照。
③Fallback
現在のシェーダーの中にこのハードウェアで実行できるものがなかった時に別のシェーダーに飛ばすもの。
Shader "example" {
// プロパティーとサブシェーダーをここに...
Fallback "otherexample"
}
【HLSL構文】データの型、セマンティクス、プリプロセッサディレクティブについて
〇データの型
ShaderLab内でのHLSLは実際のHLSLと若干異なるので注意
〇セマンティクス
セマンティクスはHLSL特有の考えで、同じデータ型でも頂点座標を示すのか色情報を示しているのかをUnityに教える必要がある。代表的なものを下にまとめる。
〇プリプロセッサディレクティブ