1. 程式人生 > >【shaderforge學習筆記】 Specular(高光)通道

【shaderforge學習筆記】 Specular(高光)通道

shaderforge Specular通道

一、官方介紹

這裡寫圖片描述
這是材質的高光的顏色。值越高越亮,如果高光值為黑色則完全不會影響shader的表現。

二、通道的輸入

1. Specular

可以是高光貼圖也可以是高光顏色
這裡寫圖片描述

2. Gloss

預設值為0.5
gloss用來調製亮斑的大小,一般來講,gloss越大,光斑越細小,gloss越大,光斑分佈越廣泛
這裡寫圖片描述

三、鏡面反射簡介

對於許多物體,漫反射不夠真實,比如擦亮的金屬、光滑的塑料。要模擬光滑表面,還需要新增鏡面高光反射的顏色,。在金屬這樣的光滑表現上,能模擬出光澤

1. 理想鏡面反射

一束平行光射到平面鏡上,入射光嚴格遵循光的反射定律平行的反射出去,這種反射叫做鏡面反射。
這裡寫圖片描述

2. 非理想鏡面反射

現實中的物體表面都不是完全光滑的。在微觀上物體的表面面元是由許多朝向的微小平面組成,其鏡面反射光分佈於物體鏡面反射方向的周圍。
這裡寫圖片描述

3. 鏡面反射光特點

  • 跟入射方向有關
  • 跟觀察的角度有關

黑暗的房間中,手電筒的光射向一面鏡子,只有眼睛與光線平行的時候才能看到光,改變觀察的角度看到不到光。

四、光照模型

1. Phong光照模型

Phong光照模型屬於經驗模型,它在Lambert模型(純漫反射模型)的基礎上考慮了鏡面反射的效果,該模型只考慮物體對方向光的鏡面反射作用,不考慮環境光的鏡面反射(認為環境光只發生漫反射),主要用來模擬光滑物體表面的光照現象。

此模型假設物體表面為非理想鏡面反射體(既會發生漫反射也會發生鏡面發射,且鏡面反射為非理想的鏡面反射),場景中存在兩種光,一種為環境光,一種為方向光,然後我們分別計算這兩種光照射到物體表面所產生的光照現象(注意:phong不考慮環境光的鏡面反射,也就是說我們要考慮到環境光的漫反射和方向光的漫反射和鏡面反射),最後再將兩個結果相加,得出反射後的光強值。

首先是計算環境光的公式:

I_inDirectionDiffuse = K_d * I_a;

其中,K_d為粗糙物體表面材質對光的反射係數,這個係數由程式編寫者在宿主程式中給出,I_a為環境光的光強,也就是環境光的顏色數值,一般是一個float3型的變數

然後是計算方向光的公式:

I_direction = I_directionDiffuse + I_directionSpecular;
I_directionDiffuse = K_d * I_l * max(0dot(N, L)); //方向光漫反射強度
I_directionSpecular = K_d * I_l * pow( max(0, dot(R,V)), n); //方向光鏡面射強度

其中I_l為方向光的光強,也就是其顏色值;N為頂點的單位法向量;L為入射光的單位法向量(注意,光照向量是從頂點指向光源的向量;也就是,它與線的傳播方向正好相反)。R表示反射光的單位向量,V為頂點到視點的向量。

我們知道,在理想狀況下,鏡面反射後的光之集中在一條線上,因此我們的視線離這條線的距離越近,射入我們眼中的光線就越多,我們看到的光強也就越強。同時,鏡面反射也與物體表面的高光指數(物體表面光澤程度)有關,其數值越大,反射後的光線越集中,反之則越分散。方向光的鏡面反射光照強度遵循Phong餘弦定理。

綜上,得出漫反射和鏡面反射疊加後的光強為:

I_diff = K_d * I_a + K_d * I_l * max(0dot(N, L)) + K_d * I_l * pow( max(0, dot(R,V)), n);

反射方向的計算

這裡寫圖片描述
L為入射光(頂點到光源)的單位法向量,N為頂點的單位法向量,R為反射光的單位法向量,V是觀察方向。

推導過程:
L在N方向上的投影是|Lcosθ|=cosθ,那麼投影向量N’=Ncosθ
L+S = N’ = Ncosθ
R = L+2S = L+2(Ncosθ-L) = 2Ncosθ-L

結果:

R=2(N•L)N-L

Phong餘弦定理

反射光的明亮度直接取決於反射光向量(R)和視角向量(V)兩個向量將夾角的餘弦值。
這裡寫圖片描述

f(θ) = max(cosθ,0) = max(RV,0)

對材質表面粗糙程度對反射光強的影響

非理想鏡面的表面並非完全光滑,也就是說會發生散射,其鏡面反射光分佈於物體鏡面反射方向的周圍,物體表面粗糙程度越大,散射越大。

理想鏡面的反射光照強度為:R dot V

非理想鏡面的反射光照強度為:(R dot V)^n
n為鏡面放射指數,也稱高光指數,反映物體表面的粗糙程度。n越大高光亮度隨α的增大衰減的越快。

n的取值於表面粗糙程度有關,
- n越大,表面越光滑(散射現象小,稍一偏離,明暗亮度急劇下降)
- n越小,表面越毛躁(散射現象嚴重)
這裡寫圖片描述

2. Blinn-phong光照模型

相比較Phong模型,Blinn-phong模型只適用N•H替換了V•R,但卻獲得了明顯的提高,它能提供比Phong更柔和、更平滑的高光,而且由於Blinn-phong的光照模型省去了計算反射光線方向向量的兩個乘法運算速度上也更快,因此成為很多CG軟體中預設的光照渲染方法,同時也被整合到大多數的圖形晶片中,在OpenGL和Direct3D渲染管線中,Blinn-Phong就是預設的渲染模型。

Blinn-phong光照模型中,用N•H的值取代了V•R。Blinn-phong光照模型的光強因子為:
(N•H)n
即H 越靠近N,光照越強。
由於這兩個光照模型公式基本相同,所以只解釋一下N•H:
N與前面相同,是頂點的單位法向量,而H則是入射光L和頂點到視點的單位向量的角平分線單位向量,通常也成為半形向量。其計算方法為:
H = (L + V) / (|L + V|)

Blinn-phong餘弦定理

反射光的明亮度直接取決於入射光和視角向量的半形(H)和法線向量(N)兩個向量將夾角的餘弦值。
這裡寫圖片描述

f(θ) = max(cosθ,0) = max(HN,0)

五、自定義UnityShader實現Blinn-phong光照模型

寫法

Shader "Hidden/NewImageEffectShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
        _DiffusePower("Diffuse Power", Float) = 1.0
        //_SpecularTex("Specular Tex", 2D) = "white"{}
        _Gloss("Specular Gloss", Float) = 0.5
        _SpecularColor("Specular Color", Color) = (1,1,1,1)

    }
    SubShader
    {

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "UnityCG.cginc"
            #include "Lighting.cginc"
            #include "AutoLight.cginc"
            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
                float3 normal : NORMAL;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                float4 vertex : SV_POSITION;
                float3 normalDir : TEXCOORD1;
                float4 posWorld : TEXCOORD2;
            };

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = v.uv;

                // 將物體法線從物體座標系轉換到世界座標系
                o.normalDir = UnityObjectToWorldNormal(v.normal);

                o.posWorld = mul(unity_ObjectToWorld, v.vertex);
                return o;
            }

            sampler2D _MainTex;
            float _DiffusePower;
            // sampler2D _SpecularTex; // 這裡我們直接用viewDirection和lightDirection的點積來計算反射光強度而不是使用高光圖
            float _Gloss;
            float4 _SpecularColor;

            fixed4 frag (v2f i) : SV_Target
            {
                // 法線方向
                float3 normalDirection = normalize(i.normalDir);
                // 燈光方向
                float lightDirection = normalize(_WorldSpaceLightPos0.xyz);
                // 燈光顏色
                float3 lightColor = _LightColor0.rgb;
                // 視線方向
                float3 viewDirection = normalize(_WorldSpaceCameraPos.xyz - i.posWorld.xyz);
                // 視線方先與法線方向的中間向量
                float3 halfDirection = normalize(viewDirection+lightDirection);

                // 計算燈光衰減
                float attenuation = LIGHT_ATTENUATION(i);
                float3 attenColor = attenuation * _LightColor0.xyz;

                // 基於蘭伯特模型計算漫反射燈光
                float NdotL = max(0,dot(normalDirection,lightDirection));
                // 方向光
                float3 directionDiffuse = pow(NdotL, _DiffusePower) * attenColor;
                // 環境光  
                float3 inDirectionDiffuse = float3(0,0,0)+UNITY_LIGHTMODEL_AMBIENT.rgb;


                // 基於Blinn Phong計算鏡面反射燈光
                float specPow = exp2( _Gloss * 10.0 + 1.0 );
                float3 directionSpecular = attenColor*  _SpecularColor*(pow(max(0,dot(halfDirection,normalDirection)),specPow));

                // 燈光與材質球表面顏色進行作用
                float3 texColor = tex2D(_MainTex, i.uv).rgb;
                float3 diffuseColor = texColor *(directionDiffuse+inDirectionDiffuse);
                float3 specularColor = directionSpecular;
                float4 finalColor = float4(diffuseColor+specularColor,1);

                return finalColor;
            }
            ENDCG
        }
    }
}

效果展示

這裡寫圖片描述

參考