C++幼女先輩

プログラミング成分多め

UNITYシェーダ研究 Lambertモデル実装

前回は 新規で作ったUNITシェーダの説明したので
簡単なシェーダ作ります

コードはココ
https://github.com/YukiMiyatake/UnityLesson/tree/Shader_3

基本シェーダ(Albedo)

先ほどのシェーダからFOGを削除しシンプルにします
テクスチャは・・・雰囲気欲しいからつけておく

Shader "Unlit/yUnit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

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

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                return o;
            }
            
            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                return col;
            }
            ENDCG
        }
    }
}

f:id:murasame-labo:20170911092726p:plain

テクスチャ(Albedo)のみですね

Lambert

最も簡単なライティングモデルの ランバート反射を作ります
先ほどのAlbedoはライトの影響をうけなかったけど今度はうけます

ランバート反射は、面にライトが当たる角度により影響の強さが変わる
具体的には 法線とライトベクトルの内積を乗算する

Shader "Unlit/yUnit"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        Pass
        {
            Tags{
                "LightMode" = "ForwardBase"
            }

            Cull Back

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

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv     : TEXCOORD0;
                float3 normal : TEXCOORD1;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;
            
            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }
            
            float3 frag (v2f i) : SV_Target
            {
                // sample the texture
                float4 col = tex2D(_MainTex, i.uv);

                // Diffuse
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                float3 NdotL = dot( i.normal, lightDir);
                float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz;
                return diffuse;
            }
            ENDCG
        }
    }
}

LightMode

Tags{
  "LightMode" = "ForwardBase"
}

これを付けないと安定しなかったので付けた
LightModeにより呼ばれるパスが変わる

ForwardBaseは Forwardレンダリングのメインのライトのパス
ForwardAddは Forwardレンダリングのメイン以外のライトがそれぞれ呼ばれる
Deferredは Deferredレンダリングのパス
ShadowCasterは 影のパス
など

構造体

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

            struct v2f
            {
                float4 vertex : SV_POSITION;
                float2 uv     : TEXCOORD0;
                float3 normal : TEXCOORD1;
            };

法線が必要なので NORMALが追加

シェーダ中身

         v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                o.normal = UnityObjectToWorldNormal(v.normal);
                return o;
            }
            
            float3 frag (v2f i) : SV_Target
            {
                // sample the texture
                float4 col = tex2D(_MainTex, i.uv);

                // Diffuse
                float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                float3 NdotL = dot( i.normal, lightDir);
                float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz;
                return diffuse;
            }

o.normal = UnityObjectToWorldNormal(v.normal);
法線がローカル座標系なので World座標系に変換する

WorldSpaceLightPos0、LightColor0 はビルトイン定数(Lighting.cginc のinclude必要)
WorldSpaceLightPos0: 0番目のライトのポジション(ディレクショナルライトは方向) LightColor0: 0番目のライトの色

float3 NdotL = dot( i.normal, lightDir);
ここで 法線とライトの内積を計算する

ディフューズは ライトと法線の内積ライトカラーとテクスチャカラーを掛ければ求められる
内積は負の場合(裏面)があるので その場合は0にしておく

f:id:murasame-labo:20170911160322p:plain

ライトを動かせば ライティングも変わるようになった
でも少し暗い

またSphereを置けばわかりやすいが、陰のつき方が90度でわかれている
実際のオブジェクトは光の回析や吸収反射などで もっとゆるやかに出るべきである

ハーフランバート

先ほどのランバートだと、ライティングが90度で終わる
f:id:murasame-labo:20170911162221p:plain

もう少しゆるやかにしたいのでハーフランバートを適用してみる 先ほどのランバート反射では ライトと法線の内積が -1~1 であったが  0~1の範囲にリマップすれば ゆるやかになる

//              float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz;
                float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb * col.xyz;

f:id:murasame-labo:20170911162420p:plain

もう少しピーキーにしたいなら 先ほどの内積を2乗する

//              float3 diffuse = max(0.0, NdotL ) * _LightColor0.rgb * col.xyz;
//             float3 diffuse = (NdotL*0.5 + 0.5) * _LightColor0.rgb * col.xyz;
                float3 diffuse = pow(NdotL*0.5 + 0.5, 2) * _LightColor0.rgb * col.xyz;

f:id:murasame-labo:20170911162635p:plain

他にも float3 diffuse = (NdotL*0.75 + 0.25) * _LightColor0.rgb * col.xyz;

とか色々と考えれるので、好みで使い分けよう

f:id:murasame-labo:20170911172540p:plain