baobao
7/20/2016 - 12:00 PM

shadow.glsl

Shader "Unlit/LightShader"
{
    Properties {
        _MainTex ("Base (RGB)", 2D) = "white" {}
    }

    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 80

        Pass
        {
            // ?? Nameの意味
            Name "FORWARD"
            // LightModeの種類
            Tags { "LightMode" = "ForwardBase" }
            CGPROGRAM
            #pragma vertex vert_surf
            #pragma fragment frag_surf
            // ビルトインmulti_compileについて
            // http://docs.unity3d.com/ja/current/Manual/SL-MultipleProgramVariants.html
            // multi_compile_fwdbaseは、ForwardBase(Forward Rendering ベース)pass 種類で必要とされるすべての変異体をコンパイルします。変異体は、異なるライトマップタイプやシャドウをオン/オフに切り替える主要な異なるライトで対処します。
            // multi_compile_fwdadd は、ForwardAdd (forward rendering additive) の pass タイプをコンパイルします。これは、次の種類(Directional、Point light、Spot)のライトを処理するためのバリアントとクッキーテクスチャによるバリアントをコンパイルします。
            // multi_compile_fwdadd_fullshadows - 上記と同じですが、ライトがリアルタイムシャドウをつけるための機能も含まれています。
            // multi_compile_fogは、異なるフォグ(霧)の種類(off/linear/exp/exp2)を処理するいくつかのバリアントに展開されます。
            #pragma multi_compile_fwdbase
            #pragma multi_compile_fog
            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"

            // ライトの強さを含んだカラーを返却
            inline float3 LightingLambertVS (float3 normal, float3 lightDir)
            {
                // ライトの強さ
                fixed diff = max (0, dot (normal, lightDir));
                return _LightColor0.rgb * diff;
            }

            sampler2D _MainTex;

            struct Input
            {
                float2 uv_MainTex;
            };

            void surf (Input IN, inout SurfaceOutput o)
            {
                half4 c = tex2D (_MainTex, IN.uv_MainTex);
                o.Albedo = c.rgb;
                o.Alpha = c.a;
            }

            struct v2f_surf {
                float4 pos : SV_POSITION;
                // MainTexのUV
                float2 uv : TEXCOORD0;
                fixed3 normal : TEXCOORD1;
                fixed3 vlight : TEXCOORD2;

                // 定義元 : AutoLight.cginc
                // #define LIGHTING_COORDS(idx1,idx2) unityShadowCoord3 _LightCoord : TEXCOORD##idx1; SHADOW_COORDS(idx2)
                // unityShadowCoord3・・・half3 or float3
                // _LightCoord : TEXCOORD4 -> セマンティクスとセットの変数定義
                // ## SHADOW_COORDS ##
                // #define SHADOW_COORDS(idx1) unityShadowCoord4 _ShadowCoord : TEXCOORD##idx1;

                // LIGHTING_COORDS(3,4)の意味は以下
                // half3 _LightCoord TEXCOORD3;
                // half4 _ShadowCoord TEXCOORD4;
                LIGHTING_COORDS(3,4)
                //
                UNITY_FOG_COORDS(5)
            };

            // 以下のマクロを使用するための一時変数
            // UnityCG.cginc
            // Transforms 2D UV by scale/bias property
            // #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
            float4 _MainTex_ST;

            v2f_surf vert_surf (appdata_full v)
            {
                v2f_surf o;
                // 頂点のMVP座標変換
                o.pos = mul (UNITY_MATRIX_MVP, v.vertex);

                // UnityCG.cginc
                // Transforms 2D UV by scale/bias property
                // #define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
                // MainTexのUV座標を代入
                // 第一引数のtexcoord・・・ テクスチャ座標(UV座標)
                // o.uvには、第一引数のUV座標における_MainTexの
                //
                //テクスチャに設定されたタイリングとオフセットを反映したUV座標(テクスチャ座標)を取得するためのマクロ
                // http://alfa.hatenablog.jp/entry/2015/08/06/011444より
                o.uv.xy = TRANSFORM_TEX(v.texcoord, _MainTex);


                // ## _World2Object ##
                //
                // uniform float4x4 _World2Object;・・・mMatrixの逆行列
                // Unityからわたってくるuniform変数
                //
                // http://raytracing.hatenablog.com/entry/20130325/1364229762
                // http://qiita.com/edo_m18/items/63df01d0efe394c515b0
                //
                //// Transforms normal from object to world space
                //inline float3 UnityObjectToWorldNormal( in float3 norm )
                //{
                //    // Multiply by transposed inverse matrix, actually using transpose() generates badly optimized code
                //    m0 m1 m2
                //    m4 m5 m6
                //    m8 m9 m10
                //    左上の成分を使っている
                //    _World2Object[0].xyz・・・・m0 m1 m2を指す
                //    return normalize(_World2Object[0].xyz * norm.x + _World2Object[1].xyz * norm.y + _World2Object[2].xyz * norm.z);
                //}
                // 法線に対する変換行列は元の行列の逆転置行列となる。
                float3 worldN = UnityObjectToWorldNormal(v.normal);
                o.normal = worldN;


                // ## ShadeSH9 ##
                //// normal should be normalized, w=1.0
                //// output in active color space
                //
                //half3 ShadeSH9 (half4 normal)
                //{
                //    // Linear + constant polynomial terms
                //    half3 res = SHEvalLinearL0L1 (normal);
                //
                //    // Quadratic polynomials
                //    res += SHEvalLinearL2 (normal);
                //
                //    if (IsGammaSpace())
                //        res = LinearToGammaSpace (res);
                //
                //    return res;
                //}
                //
                //// normal should be normalized, w=1.0
                //
                // SH・・・Shadowのことかもしれない
                //
                //half3 SHEvalLinearL0L1 (half4 normal)
                //{
                //    half3 x;
                //
                //    // Linear (L1) + constant (L0) polynomial terms
                //    // polynomial(多項式)・・・・https://www.google.co.jp/search?q=%E5%A4%9A%E9%A0%85%E5%BC%8F&safe=off&biw=1944&bih=1081&tbm=isch&source=lnms&sa=X&ved=0ahUKEwjwhdaq7YDOAhWEUZQKHZWiD9YQ_AUIBygC
                //    // こんなやつか
                //    // 環境光の強さ
                //    x.r = dot(unity_SHAr,normal);
                //    x.g = dot(unity_SHAg,normal);
                //    x.b = dot(unity_SHAb,normal);
                //
                //    return x;
                //}
                //
                //
                // normal should be normalized, w=1.0
                //half3 SHEvalLinearL2 (half4 normal)
                //{
                //    half3 x1, x2;
                //    // 4 of the quadratic (L2) polynomials
                //    half4 vB = normal.xyzz * normal.yzzx;
                //    x1.r = dot(unity_SHBr,vB);
                //    x1.g = dot(unity_SHBg,vB);
                //    x1.b = dot(unity_SHBb,vB);
                //
                //    // Final (5th) quadratic (L2) polynomial
                //    half vC = normal.x*normal.x - normal.y*normal.y;
                //    x2 = unity_SHC.rgb * vC;
                //
                //    return x1 + x2;
                //}
                // ## unity_SHAr ##
                // SH lighting environment
                // 環境光
                o.vlight = ShadeSH9 (float4(worldN,1.0));
                // ライトの強さを含んだカラーを加算する
                o.vlight += LightingLambertVS (worldN, _WorldSpaceLightPos0.xyz);

                // _Object2World・・・mMatrixのこと
                // _LightCoord・・・half3
                //
                // wikibooks 参考
                // https://en.m.wikibooks.org/wiki/Cg_Programming/Unity/Light_Attenuation
                // http://qiita.com/edo_m18/items/21d3b37596da3fd4b32b
                //
                // Pointライトの場合  
                // 頂点座標をライトから見た時の座標に変換する
                // よって_LightCoordはライトから見た時の座標
                // #define TRANSFER_VERTEX_TO_FRAGMENT(a) a._LightCoord = mul(_LightMatrix0, mul(_Object2World, v.vertex)).xyz; TRANSFER_SHADOW(a)
                // #define TRANSFER_SHADOW(a) a._ShadowCoord = mul( unity_World2Shadow[0], mul( _Object2World, v.vertex ) );
                // uniform unityShadowCoord4x4 _LightMatrix0;
                // unityShadowCoord4x4 : half4x4
                //
                // ## unity_World2Shadow ##
                //CBUFFER_START(UnityShadows)
                //    float4 unity_ShadowSplitSpheres[4];
                //    float4 unity_ShadowSplitSqRadii;
                //    float4 unity_LightShadowBias;
                //    float4 _LightSplitsNear;
                //    float4 _LightSplitsFar;
                //    float4x4 unity_World2Shadow[4];
                //    half4 _LightShadowData;
                //    float4 unity_ShadowFadeCenterAndType;
                //CBUFFER_END
                //
                //#define _World2Shadow unity_World2Shadow[0]
                //#define _World2Shadow1 unity_World2Shadow[1]
                //#define _World2Shadow2 unity_World2Shadow[2]
                //#define _World2Shadow3 unity_World2Shadow[3]
                //
                // unity_World2Shadowはfloat4x4の配列
                //
                // 結果
                // o._LightCoord
                // o._ShadowCoord
                // ↑がいい感じに設定されるっぽい
                TRANSFER_VERTEX_TO_FRAGMENT(o);
                UNITY_TRANSFER_FOG(o,o.pos);
                return o;
            }

            fixed4 frag_surf (v2f_surf IN) : SV_Target
            {
                Input surfIN;
                surfIN.uv_MainTex = IN.uv.xy;
                SurfaceOutput o;
                o.Albedo = 0.0;
                o.Emission = 0.0;
                o.Specular = 0.0;
                o.Alpha = 0.0;
                o.Gloss = 0.0;
                o.Normal = IN.normal;
                surf (surfIN, o);

                // #define LIGHT_ATTENUATION(a)  (tex2D(_LightTexture0, dot(a._LightCoord,a._LightCoord).rr).UNITY_ATTEN_CHANNEL * SHADOW_ATTENUATION(a))
                fixed atten = LIGHT_ATTENUATION(IN);
                fixed4 c = 0;
                c.rgb = o.Albedo * IN.vlight * atten;
                UNITY_APPLY_FOG(IN.fogCoord, c);
                // #define UNITY_OPAQUE_ALPHA(outputAlpha) outputAlpha = 1.0
                UNITY_OPAQUE_ALPHA(c.a);
                return c;
            }
            ENDCG
        }

        Pass
        {
            Name "ShadowCaster"
            // そもそも`LightModeタグ` はライティングパイプラインでのパスの役割を定義
            // Always : 常にレンダリングされ、ライティングは適用されません。
            // ForwardBase : Forward Rendering で使用され、環境光、メインのディレクショナルライト、頂点/SH ライトが適用されます。
            // ForwardAdd : Forward Rendering で使用され、ライトごとに一つのパスで、Additive ピクセルライティングが適用されます。
            // Deferred : Deferred Shading で使用され、g-バッファを描画します。
            // ShadowCaster : オブジェクトのデプスをシャドウマップや深度テクスチャに描画します。
            // PrepassBase : 旧 Deferred Lighting で使用され、法線やスペキュラーをレンダリングします。
            // PrepassFinal : 旧 Deferred Lighting で使用され、最終的なカラーについて、テクスチャ、ライト、および自己発光、を合成することでレンダリングします。
            // Vertex : Vertex Lit レンダリング でオブジェクトがライトマップされてない場合に使用され、すべての頂点ライトが適用されます。
            // VertexLMRGBM : Vertex Lit レンダリング でオブジェクトがライトマップされてる場合、ライトマップが RGBM エンコードされるプラットフォームにおいて使用されます。
            // VertexLM : Vertex Lit レンダリング でオブジェクトがライトマップされてる場合に、ライトマップが doub-LDR エンコードされるプラットフォームにおいて(一般にモバイル向けに)使用されます。
            // LightModeをShadowCasterにしないと影を反映できない

            // ShadowCaster・・・オブジェクトのデプスをシャドウマップや深度テクスチャに描画します。
            Tags { "LightMode" = "ShadowCaster" }

            ZWrite On
            ZTest LEqual
            Cull Off

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma multi_compile_shadowcaster
            #include "UnityCG.cginc"

            // #define V2F_SHADOW_CASTER V2F_SHADOW_CASTER_NOPOS float4 pos : SV_POSITION
            // #define V2F_SHADOW_CASTER_NOPOS float4 hpos : TEXCOORD0;
            // struct v2f {
            //    V2F_SHADOW_CASTER;
            // };

            // 展開すると以下の構造体を定義している
            struct v2f {
                float4 hpos : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert( appdata_base v )
            {
                v2f o;
                // Vertex shader part, with support for normal offset shadows. Requires
                // position and normal to be present in the vertex input.


                // 1. TRANSFER_SHADOW_CASTER_NORMALOFFSET
                //
                // #define TRANSFER_SHADOW_CASTER_NORMALOFFSET(o) TRANSFER_SHADOW_CASTER_NOPOS(o,o.pos)


                // 2. TRANSFER_SHADOW_CASTER_NOPOS
                //
                // #define TRANSFER_SHADOW_CASTER_NOPOS(o,opos)
                //    opos = UnityClipSpaceShadowCasterPos(v.vertex.xyz, v.normal);
                //    以下の方を使うのだろう変数名的に`opos`
                //    opos = UnityApplyLinearShadowBias(opos);
                //    o.hpos = opos;


                // 3. UnityClipSpaceShadowCasterPos
                //
                //float4 UnityClipSpaceShadowCasterPos(float3 vertex, float3 normal)
                //{
                //    float4 clipPos;
                //
                //    // Important to match MVP transform precision exactly while rendering
                //    // into the depth texture, so branch on normal bias being zero.
                //    if (unity_LightShadowBias.z != 0.0)
                //    {
                //        float3 wPos = mul(_Object2World, float4(vertex,1)).xyz;
                //        float3 wNormal = UnityObjectToWorldNormal(normal);
                //        float3 wLight = normalize(UnityWorldSpaceLightDir(wPos));
                //
                //        // apply normal offset bias (inset position along the normal)
                //        // bias needs to be scaled by sine between normal and light direction
                //        // (http://the-witness.net/news/2013/09/shadow-mapping-summary-part-1/)
                //        //
                //        // unity_LightShadowBias.z contains user-specified normal offset amount
                //        // scaled by world space texel size.
                //
                //        float shadowCos = dot(wNormal, wLight);
                //        float shadowSine = sqrt(1-shadowCos*shadowCos);
                //        float normalBias = unity_LightShadowBias.z * shadowSine;
                //
                //        wPos -= wNormal * normalBias;
                //
                //        clipPos = mul(UNITY_MATRIX_VP, float4(wPos,1));
                //    }
                //    else
                //    {
                //        clipPos = mul(UNITY_MATRIX_MVP, float4(vertex,1));
                //    }
                //    return clipPos;
                //}
                //


                // 4. UnityApplyLinearShadowBias
                //
                //float4 UnityApplyLinearShadowBias(float4 clipPos)
                //{
                //    clipPos.z += saturate(unity_LightShadowBias.x/clipPos.w);
                //    float clamped = max(clipPos.z, clipPos.w*UNITY_NEAR_CLIP_VALUE);
                //    clipPos.z = lerp(clipPos.z, clamped, unity_LightShadowBias.y);
                //    return clipPos;
                //}

                // 5. UNITY_NEAR_CLIP_VALUE
                //
                // #define UNITY_NEAR_CLIP_VALUE (-1.0)

                TRANSFER_SHADOW_CASTER_NORMALOFFSET(o)
                return o;
            }

            float4 frag( v2f i ) : SV_Target
            {
                // #define SHADOW_CASTER_FRAGMENT(i) UNITY_OUTPUT_DEPTH(i.hpos.zw);
                // #define UNITY_OUTPUT_DEPTH(i) return i.x/i.y
//                SHADOW_CASTER_FRAGMENT(i)
//                return i.pos;
                return 0;
            }
            ENDCG
        }
    }
}