UNITY 古典シェーダ Part2 Ambient~複数ライト

Part1 https://qiita.com/YukiMiyatake/items/f71aa766de336fbc6781

#Ambient
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_5

物理ベースだと GI（グローバルイルミネーション）を使う
これは光の回析などを計算したライトモデル
ただし古典シェーダでは一律に明るさをあたえる Ambientライト

##設定
設定はメニューの
Window -> Lighting -> Setting 

![9.png](https://qiita-image-store.s3.amazonaws.com/0/13916/d0178f7e-cede-2eb6-fd36-6ac139ee0002.png)

Environment Lightingで設定します
Sourceを Colorにすると Ambient色も変更可能  

##シェーダ
シェーダからは UNITY_LIGHTMODEL_AMBIENT として取得できる
基本的にAmbientは 他のカラーに足せば良い

前回の続きで、ディフューズ、スペキュラーに足してみる

```hlsl
             float3 frag(v2f i) : SV_Target
                {
                    float3 L = normalize(_WorldSpaceLightPos0.xyz);
                    float3 V = normalize(_WorldSpaceCameraPos - i.vertexW.xyz);
                    float3 N = i.normal;
                    float3 H = normalize(L + V);


                    //Ambient
                    float3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;
                    
                    // texture albedo
                    float4 tex = tex2D(_MainTex, i.uv);

                    // Diffuse(HalfLambert)
                    float3 NdotL = dot(N, L);
                    float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb;

                    // Speculer
                // float3 specular = pow(max(0.0, dot(reflect(-L, N), V)), _Spec1Power) * _Spec1Color.xyz;  // reflection
                    float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) *  _Spec1Color.xyz;  // Half vector


                    return (ambient + diffuse) * tex + specular;
                }
```

これで、Ambientが反映される


#ライトの強さ
今まではライトの角度のみで強さに関しては無視していたので
ライトの強度を反映させる
LIGHT_ATTENUATION を使うために AutoLight.cgincを includeする

```hlsl
float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);

float3 diffuse = (NdotL*0.5 + 0.5) * lightCol;

float3 specular = pow(max(0.0, dot(H, N)), _Spec1Power) *  _Spec1Color.xyz * lightCol;  // Half vector
```

こんな感じで、光源色と強さをdiffuseやSpecularに乗算すればいい




#複数ライト
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_6

UNITYでは複数のライトを扱うことが出来る
Forwardレンダリングモードでは
一番影響の高いディレクショナルライトのみが ForwardBaseで処理され
その他のライトは ForwardAdd で処理される

今の状態では ForwardBaseしか書いていないので、２つ目のライトを与えても
反映されないので
ForwardAddパスを追加する

シェーダーの中身は ForwardBaseと同じでよい
ただし Ambientライトは１回しか加えてはいけないので ForwardAddでは加算しない
ブレンドモードも OneOne　などにしなければうまく加算されないので　ブレンドモードも気をつけること

BlendModeなし
![10.png](https://qiita-image-store.s3.amazonaws.com/0/13916/2756e895-614b-7bdf-cd83-c5524955a41a.png)

ForwardBaseのみ
![11.png](https://qiita-image-store.s3.amazonaws.com/0/13916/53881a68-0ce4-d25e-0699-f562e4a5bd65.png)

ForwardAdd追加（赤いライト追加）
![12.png](https://qiita-image-store.s3.amazonaws.com/0/13916/c17d6bf7-3d57-19d8-56eb-67fa914f9098.png)

これで複数ライトの影響を与えられるようになった


#Vertexシェーダでのライト計算
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_7

今までは 補間された法線をつかい、フラグメントシェーダでピクセル単位でライト計算をしていた
頂点数にくらべ　ピクセル数のほうが圧倒的に多く、可能であれば計算は頂点シェーダにまかせたい

ので、試しに
ForwardBaseは今までどおりの フラグメントシェーダでのライティングを行い
ForwardAddでは 頂点シェーダで頂点ごとライティング計算を行い補間を行い フラグメントシェーダではライト計算を行わない事にした

ただし、スペキュラのような ピーキーなライトでは、頂点による補間ではよい結果が出ないことが想定されるので
あくまでテスト

ForwardAddの構造体

```hlsl
    struct appdata
    {
        float4 vertex : POSITION;
        float3 normal : NORMAL;
        float2 uv     : TEXCOORD0;
    };

    struct v2f
    {
        float4 vertex : SV_POSITION;
        float2 uv     : TEXCOORD1;

        float3 diffuse : TEXCOORD2;
        float3 specular : TEXCOORD3;
    };
```

フラグメントシェーダではもうライティング計算をおこなわないので、ワールド座標も法線も不要
頂点シェーダで diffuseとspecularを計算して　補完し送るだけだ

ここでセマンティクスを TEXCOORD2、TEXCOORD3にしているのは　あまり理由がない
たしか今のHLSLでは TEXCOORDとCOLORには明確な違いがなくなったと思ってる
ので　多分どちらを使っても良いと思う

ForwardAddシェーダーの中身

```hlsl
    v2f vert(appdata v)
    {
        v2f o;
        o.vertex = UnityObjectToClipPos(v.vertex);
        float4 vertexW = mul(unity_ObjectToWorld, v.vertex);

        o.uv = TRANSFORM_TEX(v.uv, _MainTex);
        float3 normal = UnityObjectToWorldNormal(v.normal);


        float3 L = normalize(_WorldSpaceLightPos0.xyz);
        float3 V = normalize(_WorldSpaceCameraPos - vertexW.xyz);
        float3 N = normal;
        float3 H = normalize(L + V);
        float3 lightCol = _LightColor0.rgb * LIGHT_ATTENUATION(i);
        float3 NdotL = dot(N, L);

        o.diffuse = (NdotL*0.5 + 0.5) * lightCol;
        o.specular = pow(max(0.0, dot(H, N)), _Spec1Power) *  _Spec1Color.xyz * lightCol;  // Half vector

        return o;
    }

    float3 frag(v2f i) : SV_Target
    {

        // texture albedo
        float4 tex = tex2D(_MainTex, i.uv);

        return i.diffuse * tex + i.specular;
    }
```

今まで行っていたライト計算を 頂点シェーダにもってきただけだ
それにより　フラグメントシェーダが　非常にコンパクトになり
そうとう速度が速くなったと思われる

肝心のレンダリング結果

通常
![13.png](https://qiita-image-store.s3.amazonaws.com/0/13916/f419d243-d97b-7824-164c-0cb5e39cdc5e.png)


軽量
![14.png](https://qiita-image-store.s3.amazonaws.com/0/13916/8dc40171-952c-5759-fcd5-69808edb2ab5.png)


予想以上に変化がなかった
もちろん　モデルのポリゴン数やライトの状況によっては　全く使えない事もあるし
そもそも 法線マッピングは メインライト以外影響しなくなるので
限定的ではあるが　覚えておいて損はないかと。

私はパフォーマンスチューニングで結構使います