はじめに
自分の為にUnityのシェーダの基礎から勉強をしていきます。
Unityのシェーダはざっくり分けると以下の3種類があります。
- サーフェイスシェーダ
- 頂点/フラグメントシェーダ
- 固定関数シェーダ
サーフェイスシェーダは、ライティング等を自動で行ってくれます。
頂点/フラグメントシェーダは、ライティングを使わない場合などに使います。
ライティングも可能ですが、全部自力で書く必要があるので、自由度は高いですがその分難易度も高いです。
固定関数シェーダは、プログラマブルシェーダに対応していない古いハードウェア用のものなので、今回は勉強しない方向です。
簡単なサーフェイスシェーダ
いきなりですが、サーフェイスシェーダのサンプルです。
Shader "Custom/surfaceshader001" {
SubShader {
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
};
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = half3(0.75, 1.0, 0.5);
}
ENDCG
}
FallBack "Diffuse"
}
説明
上から順に説明します。
Shader "Custom/surfaceshader001"
シェーダ自身の名前です。
Shader "グループ名/シェーダー名" という感じです。
SubShader {
~~
}
シェーダの実装部分です。
ちなみに、1つのシェーダ内にSubShader{}を複数記述することもできます。
その場合は、上から順に実行可能なSubShaderが選択されます。
いろんなハードで動作させるために書き方を変えたりするためです。
Tags { "RenderType"="Opaque" }
Tagsは、SubShaderが「いつ」「どのように」レンダリングされるかの情報です。
この例では、RenderTypeをOpaque(不透明)に指定しています。
タグはRenderType以外にも色々ありますが、おいおい紹介していきます。
CGPROGRAM
~~
ENDCG
サーフェイスシェーダでは、実際の描画処理をCGPROGRAM~ENDCGの間に記述します(パスと呼ぶ)
また、一つのSubShaderの中に複数のCGPROGRAM~ENDCGを記述することもできます。
その場合、上から順に実行されていきます。
複数のSubShaderの時とは違い、順番に全て実行されます。
例えば、モデルに輪郭線を付けたい時は下記のように指定します。
- 1パス目で、頂点を法線方向に膨らませて黒ベタで裏面描画
- 2パス目で、通常描画
# pragma surface surf Lambert
#pragma surfaceでUnityにサーフェイスシェーダである事を宣言しています。
その後のsurfが、エントリ関数の名前です。
その後のLambertは、ライティングモデルの指定で、ランバート反射を指定しています。
ライティングモデルは、他にもスペキュラや自作のライティング関数を使ったり出来ます。
#pragmaは他にも色々オプションが指定できますが、おいおい紹介していきます。
struct Input {
float4 color : COLOR;
};
次のsurf関数に渡す構造体の指定です。
float4は4つのfloat型がセットになった変数です。
:のあとの大文字のCOLORは、セマンティクスといって、Unityから渡される情報の意味を指定します。
COLORは、頂点カラー(頂点自体の色)を意味します。
名前の付け方やセマンティクスは決まっていますが、おいおい紹介していきます。
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = half3(0.75, 1.0, 0.5);
}
サーフェイスシェーダのエントリ関数です。
引数のInput INは、前述のInput構造体の情報が入っています。
inout SurfaceOutput oは、入力されたデータを色々して詰め込んで出力する構造体で、下記の変数が入っています。
struct SurfaceOutput {
half3 Albedo; // 拡散反射光(=Diffuse)、物体の色
half3 Normal; // 法線ベクトル
half3 Emission; // 発光色
half Specular; // スペキュラ、鏡面反射の強度
half Gloss; // 光沢、スペキュラとの違いは今のところ不明…
half Alpha; // 不透明度、0で透明、1で不透明
};
今回はo.Albedoに、half3(0.75, 1.0, 0.5)という色を渡しています。
FallBack "Diffuse"
最後は、全てのSubShaderの実行に失敗した時のフォールバック処理の指定です。
固定関数シェーダでDiffuseを指定しています。
省略可能です。
実行結果
特にライティング等の指定はしてませんが、いい感じに明るい部分と暗い部分ができています。
サーフェイスシェーダ様々です。
プロパティで色を変えてみる
Unityはシェーダの値を外部から変えることができます。
変更後のシェーダは下記になります。
Shader "Custom/surfaceshader002" {
Properties {
_Color("Diffuse Color",Color) = (1.0, 1.0, 1.0)
}
SubShader {
Tags { "RenderType"="Opaque" }
CGPROGRAM
#pragma surface surf Lambert
struct Input {
float4 color : COLOR;
};
float4 _Color;
void surf (Input IN, inout SurfaceOutput o) {
o.Albedo = _Color.rgb;
}
ENDCG
}
FallBack "Diffuse"
}
変更点は、3つあります。
Properties {
_Color("Diffuse Color",Color) = (1.0, 1.0, 1.0)
}
頭のほうで、Inspector上に表示するプロパティを指定しています。
プロパティは、
変数名("Inspector上の表示名", 変数の種類) = デフォルト値
のルールで記述します。
変数の種類は、下記のものが使えます。
// 数値系
name ("display name", Range (min, max)) = number // 範囲指定型
name ("display name", Float) = number // 浮動小数点型
name ("display name", Int) = number // 整数型
// 色とベクトル
name ("display name", Color) = (number,number,number,number) // 色
name ("display name", Vector) = (number,number,number,number) // ベクトル
// テクスチャ系
name ("display name", 2D) = "defaulttexture" {} // 2Dテクスチャ
name ("display name", Cube) = "defaulttexture" {} // キューブマップ
name ("display name", 3D) = "defaulttexture" {} // 3Dテクスチャ
float4 _Color;
プロパティで受け取ったデータをシェーダ内で使うための定義です。
o.Albedo = _Color.rgb;
SurfaceOutput oにプロパティで受け取った**_Color**のrgb値を代入しています。
実行結果
上記のシェーダを実行すると下記のようになります。
実行直後
色を変えてみる
球体の色も赤くなっています。