1. 程式人生 > >unity之shader學習筆記(四)--高光反射

unity之shader學習筆記(四)--高光反射

但是我們會發現物體的背光面是個全黑的顏色,現實生活中物體的背光面並不是全黑的,而是可以看到物體的大概形狀,並不是全黑的,之前使用的計算方式是蘭伯特光照模型。要實現此種現象,那麼就需要使用半蘭伯特光照模型來實現

半蘭伯特光照模型
Diffuse = 直射光顏色 *(cosθ*0.5+0.5)
蘭伯特光照模型
Diffuse = 直射光顏色 * max(0,cosθ) θ是指光照方向與法線方向的夾角

半蘭伯特光效果如圖:

這裡寫圖片描述

高光反射的計算 (Blinn光照模型)

Specular = 直射光*pow(max(cosθ,0),高光的引數)

對於上述公式的解釋:
θ是指反射光方向和反射點到相機方向的夾角
pow的次方的意思 就是cosθ的高光引數次方
max(cosθ,0)作用是防止在物體背光面,如果高光引數為偶數,那麼最後的值會從負數變為正數

使用的示例程式碼如下

Properties{
        _Diffuse("Diffuse Color",Color) = (1,1,1,1)
        _Gloss("Gloss",Range(8,200)) = 10
    }
        SubShader{
            Pass{

                Tags{"LightMode" = "ForwardBase"}

                CGPROGRAM

                #include "Lighting.cginc"  
                //unity系統定義好的類  可以使用一些內建的變數來簡化計算操作  
//_LightColor0 該變量表示第一個直射光的顏色 //_WorldSpaceLightPos0 該變量表示第一個直射光的位置(世界座標下) //UNITY_LIGHTMODEL_AMBIENT 該變量表示系統的環境光 #pragma vertex vert #pragma fragment frag fixed4 _Diffuse; half _Gloss; struct a2v { float4 vertex : POSITION; //系統自動將頂點座標傳入vertex
float3 normal : NORMAL; //系統自動將法線傳入 變數normal }; struct v2f { float4 position:SV_POSITION; fixed3 color : COLOR; }; v2f vert(a2v x) { v2f f; //UNITY_MATRIX_MVP 這個矩陣用來把一個座標從模型空間轉換到剪裁空間 f.position = mul(UNITY_MATRIX_MVP, x.vertex); fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz); fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb; fixed3 normalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject)); //暫時沒有考慮alpha屬性 fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的顏色 //計算高光 //計算反射光夾角 fixed3 reflectDir = normalize(reflect(-lightDir,normalDir)); fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz); fixed3 specular = _LightColor0.rgb*pow(max(dot(reflectDir, viewDir), 0), _Gloss); f.color = diffuse +ambient+ specular; return f; } fixed4 frag(v2f f) :SV_TARGET{ //將計算得到的顏色值輸出到視野中的物體上 return fixed4(f.color, 1); } ENDCG } }

程式碼裡面有一個reflect方法,這個方法是用來計算入射光的反射光與法線的夾角。
函式reflect中第一個引數是入射光向量,第二個引數是法線向量,計算的時候是通過入射光向量與法線之間的夾角計算得出反射光向量與法線的夾角,因此傳入的時候需要將入射光進行翻轉,否則入射光與法線的夾角是(180-(入射光向量與法線向量夾角))°。然後再通過巨集定義的變數得到頂點到視野的夾角。最後只要使用公式進行計算即可。
高光效果如圖:
這裡寫圖片描述

高光的區域範圍是由高光反射係數來決定的,定義一個引數用於控制高光範圍

_Gloss("Gloss",Range(8,200)) = 10

並在公式中進行使用,最後可以進行自由的控制高光範圍的大小。

Blinn-Phone光照模型 :Blinn光照模型的改進
計算公式修改為:

Specular = 直射光*pow(max(cosθ,0),高光的引數)

θ這裡是指法線和x的夾角 x是光的方向和攝像機視角方向的平分線(中間線)
如圖
這裡寫圖片描述
那麼最後只要對vert裡面程式碼做出如下修改即可。

v2f vert(a2v x) {
                    v2f f;
                    //UNITY_MATRIX_MVP 這個矩陣用來把一個座標從模型空間轉換到剪裁空間
                    f.position = mul(UNITY_MATRIX_MVP, x.vertex); 
                    fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;


                    fixed3 normalDir = normalize(mul(x.normal, (float3x3)unity_WorldToObject));
                    //暫時沒有考慮alpha屬性 
                    fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的顏色    

                    //計算高光
                    //計算反射光夾角
                    //fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
                    fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);

                    //平分線
                    fixed3 halfDir = normalize(viewDir + lightDir);
                    //控制高光顏色
                    fixed3 specular = _Specular * _LightColor0.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);

                    f.color = diffuse +ambient+ specular;
                    return f; 
                }

                fixed4 frag(v2f f) :SV_TARGET{
                    //將計算得到的顏色值輸出到視野中的物體上
                    return fixed4(f.color, 1);
                }
                ENDCG
        }

這個B-P模型比之前的Blinn光照模型進行了優化,之前我們會在背光面檢視到一個高光點,但是在B-P中則不會看到

UnityCG.cginc中一些常用的函式和巨集定義變數

_LightColor0              該變量表示第一個直射光的顏色

_WorldSpaceLightPos0      該變量表示第一個直射光的位置(世界座標下)

UNITY_LIGHTMODEL_AMBIENT  該變量表示系統的環境光
_World2Object 這個矩陣用來把一個方向從世界空間轉換到模型空間
UNITY_LIGHTMODEL_AMBIENT用來獲取環境光

    //攝像機方向(視角方向)
    float3 WorldSpaceViewDir(float4 v)      根據模型空間中的頂點座標 得到 (世界空間)從這個點到攝像機的觀察方向
    float3 UnityWorldSpaceViewDir(float4 v) 世界空間中的頂點座標==》世界空間從這個點到攝像機的觀察方向
    float3 ObjSpaceViewDir(float4 v)        模型空間中的頂點座標==》模型空間從這個點到攝像機的觀察方向
    //光源方向
    float3 WorldSpaceLightDir(float4 v)     模型空間中的頂點座標==》世界空間中從這個點到光源的方向
    float3 UnityWorldSpaceLightDir(float4 v)    世界空間中的頂點座標==》世界空間中從這個點到光源的方向
    float3 ObjSpaceLightDir(float4 v)   模型空間中的頂點座標==》模型空間中從這個點到光源的方向
    //方向轉換
    float3 UnityObjectToWorldNormal(float3 norm) 把法線方向 模型空間==》世界空間
    float3 UnityObjectToWorldDir(float3 dir) 把方向 模型空間=》世界空間
    float3 UnityWorldToObjectDir(float3 dir) 把方向 世界空間=》模型空間

使用內建的函式方法可以使得程式碼的書寫更加簡單
需要將程式中的程式碼

fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

替換為

fixed3 lightDir = normalize(WorldSpaceLightDir(mul(x.vertex, unity_WorldToObject)).xyz);
fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);

替換為

fixed3 viewDir = normalize(UnityWorldSpaceViewDir(mul(x.vertex, (float3x3)unity_WorldToObject).xyz));

法線的計算直接替換為
//直接使用方法從模型空間轉換為世界空間

fixed3 normalDir = normalize(UnityObjectToWorldNormal(x.normal));

替換修改後程式碼如下

v2f vert(a2v x) {
                    v2f f;
                    //UNITY_MATRIX_MVP 這個矩陣用來把一個座標從模型空間轉換到剪裁空間
                    f.position = mul(UNITY_MATRIX_MVP, x.vertex); 
                    //某一點的光源
                    //fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
                    fixed3 lightDir = normalize(WorldSpaceLightDir(mul(x.vertex, unity_WorldToObject)).xyz);
                    fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                    //直接使用方法從模型空間轉換為世界空間
                    fixed3 normalDir = normalize(UnityObjectToWorldNormal(x.normal));
                    //暫時沒有考慮alpha屬性 
                    fixed3 diffuse = _LightColor0.rgb*max(dot(lightDir, normalDir),0)*_Diffuse.rgb;//取得漫反射的顏色    

                    //計算高光
                    //計算反射光夾角
                    //fixed3 reflectDir = normalize(reflect(-lightDir,normalDir));
                    //fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(x.vertex, (float3x3)unity_WorldToObject).xyz);
                    fixed3 viewDir = normalize(UnityWorldSpaceViewDir(mul(x.vertex, (float3x3)unity_WorldToObject).xyz));
                    //平分線
                    fixed3 halfDir = normalize(viewDir + lightDir);
                    //控制高光顏色
                    fixed3 specular = _Specular * _LightColor0.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);

                    f.color = diffuse +ambient+ specular;
                    return f; 
                }