本日はShader勉強枠です。
昨日から頂点シェーダーの勉強に入りました。
本日は拡散パラメータと発光パラメータを扱うライティングのShaderに関して勉強していきます。
Unityシェーダーのサンプルやデモを数多く紹介しているサイトShaders Laboratoryを基に勉強していきます。
〇拡散パラメータと発光パラメータを扱うライティング
Shader "Custom/Lighting/Emission"
{
Properties
{
[Header(Diffuse)]
_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)
_Diffuse ("Diffuse value", Range(0, 1)) = 1.0
[Header(Emission)]
_MainTex ("Emissive Map", 2D) = "white" {}
[HDR] _EmissionColor ("Emission Color", Color) = (0,0,0)
_Threshold ("Threshold", Range(0., 1.)) = 1.
}
SubShader
{
Tags { "LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
struct v2f {
float4 pos : SV_POSITION;
fixed4 col : COLOR0;
float2 uv : TEXCOORD0;
};
fixed4 _Color;
fixed4 _LightColor0;
float _Diffuse;
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata_base v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float NdotL = max(0.0, dot(worldNormal, lightDir));
fixed4 diff = _Color * NdotL * _LightColor0 * _Diffuse;
o.col = diff;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
float4 _EmissionColor;
float _Threshold;
fixed4 frag(v2f i) : SV_Target {
fixed3 emi = tex2D(_MainTex, i.uv).r * _EmissionColor.rgb * _Threshold;
i.col.rgb += emi;
return i.col;
}
ENDCG
}
}
}
●Properties
Propertes{
[Header(Diffuse)]
_Color ("Color", Color) = (1.0, 1.0, 1.0, 1.0)
_Diffuse ("Diffuse value", Range(0, 1)) = 1.0
[Header(Emission)]
_MainTex ("Emissive Map", 2D) = "white" {}
[HDR] _EmissionColor ("Emission Color", Color) = (0,0,0)
_Threshold ("Threshold", Range(0., 1.)) = 1.
}
このShaderではアトリビュート[属性]を付けることでDiffuseとEmissiveのプロパティを区別しています。
[Header(Name)]
_Color("color",color)=(1,1,1,1)
[Header(Name2)]
_MapColor("MapColor")=(1,1,1,1)
を記述することでUnityのマテリアルで以下のように表示されます。

サンプルShaderではDiffuseとEmissionで区切っています。

_EmissionColorに付けられている属性[HDR]は選択されたテクスチャがHDRテクスチャあることを示します。HDRテクスチャはハイダイナミックレンジ画像のことです。
白飛びすることなく明るさの幅を表現できる技術のようです。
● Tags
Tags { "LightMode"="ForwardBase" }
ライティングモードがForwardBaseライティングに指定されています。これによって環境光、sceneのディレクショナルライト、頂点/SH ライトが適用されます。
●struct v2f
struct v2f {
float4 pos : SV_POSITION;
fixed4 col : COLOR0;
float2 uv : TEXCOORD0;
};
structは構造体を指します。
v2fは慣例表記で、Vertex To Fragmentを指し、頂点シェーダーからフラグメントシェーダーへ複数の値を渡すときの定義を記述します。
ここではSV_POSITIONとカラーそしてテクスチャのuv座標を渡しています。
このSV_POSITIONのSVはシステム上の情報(System Value)を意味します。SV_POSITIONは頂点のスクリーン座標を意味します。
v2fの値はほとんどの場合appdataの中身と同じようになるようです。
●v2f vert(appdata_base v) {}
v2f vert(appdata_base v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float NdotL = max(0.0, dot(worldNormal, lightDir));
fixed4 diff = _Color * NdotL * _LightColor0 * _Diffuse;
o.col = diff;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
頂点シェーダーの関数です。
以下の部分が定型になります。
v2f vert(appdata_base v) {
v2f o;
return o;
}
これは頂点座標を基に処理を行い出力することを意味します。
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.posというのはv2f{}でSV_POSITIONと定義されています。mul(UNITY_MATRIX_MVP,v.vertex)は現在のモデルビュー行列×射影行列 を行うという意味です。
Unityのローカル座標からカメラから座標へ変換しています。
float3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float NdotL = max(0.0, dot(worldNormal, lightDir));
fixed4 diff = _Color * NdotL * _LightColor0 * _Diffuse;
o.col = diff;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
各パラメータの定義を行っています。worldNormalはオブジェクトの法線ベクトル、lightDirは光源までのベクトル、NdotLは法線ベクトルと光源までのベクトルの内積を計算しています。(Normal dot LightDir)
diffはDiffuseの意でしょうか?ColorとNdotLとLightColor()とpropertyに与えた_Diffuseの値の積で定められます。
o.colはv2fのCOLOR0に当たります。
つまりColorとNdotLとLightColor()とpropertyに与えた_Diffuseの値の積がCOLOR0になります。
o.uvはTEXCOORD0(テクスチャ座標)になりこれはTRANSFORM_TEX(v.texcoord,_MainTex)を指します。
TRANSFORM_TEX()テクスチャに指定されたTillingとoffsetを扱うためのものです。
以上がv2f vert(appdata_base v) {}の内部の処理になります。
これらの数値を扱うために事前にパラメータの型を宣言します。
fixed4 _Color;
fixed4 _LightColor0;
float _Diffuse;
sampler2D _MainTex;
float4 _MainTex_ST;
v2f vert(appdata_base v) {
v2f o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
float3 worldNormal = normalize(mul(v.normal, (float3x3)unity_WorldToObject));
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
float NdotL = max(0.0, dot(worldNormal, lightDir));
fixed4 diff = _Color * NdotL * _LightColor0 * _Diffuse;
o.col = diff;
o.uv = TRANSFORM_TEX(v.texcoord, _MainTex);
return o;
}
ここでfloat4 _MainTex_STはTRANSFORM_TEX()で扱うために置かれている一時変数になります。
●fixed4 frag(v2f i) : SV_Target { }
フラグメントシェーダーの関数です。
セマンティクスに用いられているSV_Targetはピクセル色を意味します。
fixed3 emi = tex2D(_MainTex, i.uv).r * _EmissionColor.rgb * _Threshold;
i.col.rgb += emi;
return i.col;
emiはEmmisiveの意味と思われます。MainTexのRにEmissionColorのrgbと_thresholdがかけられています。
i.col.rgbはv2fで定義されているCOLORです。ここではCOLORにemiが加算されています。
ここで処理した値をi.colとしてv2fのcolに返しています。
以上が拡散パラメータと発光パラメータを扱えるShaderになります。
全体的な処理は以下の様になります。
①appdataでモデルの頂点座標を参照
②v2tで準備してvertに渡す。
③カメラから見た画面上の頂点位置を指定
④fragでピクセルごとに塗りつぶす