7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

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

Posted at

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

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

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

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

9.png

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

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

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

             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する

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しか書いていないので、2つ目のライトを与えても
反映されないので
ForwardAddパスを追加する

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

BlendModeなし
10.png

ForwardBaseのみ
11.png

ForwardAdd追加(赤いライト追加)
12.png

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

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

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

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

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

ForwardAddの構造体

    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シェーダーの中身

    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

軽量
14.png

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

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

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?