夜風のMixedReality

xRと出会って変わった人生と出会った技術を書き残すためのGeekなHoloRangerの居場所

UnityでShaderを勉強する その⑭ 拡散パラメータと発光パラメータを扱うライティング

本日はShader勉強枠です。

昨日から頂点シェーダーの勉強に入りました。

本日は拡散パラメータと発光パラメータを扱うライティングのShaderに関して勉強していきます。

Unityシェーダーのサンプルやデモを数多く紹介しているサイトShaders Laboratoryを基に勉強していきます。

www.shaderslab.com

〇拡散パラメータと発光パラメータを扱うライティング

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のマテリアルで以下のように表示されます。

f:id:Holomoto-Sumire:20200430081538j:plain

 サンプルShaderではDiffuseEmissionで区切っています。

f:id:Holomoto-Sumire:20200430081858j:plain

_EmissionColorに付けられている属性[HDR]は選択されたテクスチャがHDRテクスチャあることを示します。HDRテクスチャはハイダイナミックレンジ画像のことです。

 白飛びすることなく明るさの幅を表現できる技術のようです。

time-space.kddi.com

● Tags
 Tags { "LightMode"="ForwardBase" }

ライティングモードがForwardBaseライティングに指定されています。これによって環境光、sceneのディレクショナルライト、頂点/SH ライトが適用されます。

docs.unity3d.com

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

 

〇これまでのShader学習記事

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com

redhologerbera.hatenablog.com