本日は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でピクセルごとに塗りつぶす