shader篇-高光反射模型

shader篇-高光反射模型


背景

這裏的高光反射模型,並不是現實意義上的高光反射,而是隻是用來計算物理上那些沿着完全鏡面反射方向被反射的光線,讓物體看起來有光澤,例如金屬材質
高光反射計算公式如下

Cdiffuse=(ClightMdiffuse)max(0,v⃗ r⃗ )Mgloss

v是視角方向,r是反射方向
Mgloss
是高光反射係數
反射方向可用表面法線n和光照方向l計算
r⃗ =l⃗ 2(n⃗ l⃗ )n⃗ 

逐頂點光照

Shader "Test Shader/SpecularVertexLevel{
Properties
{
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    //控制高光反射顏色
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    //控制高光區域大小
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
    Pass
    {
        Tags { "LightMode"="ForwardBase" }

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        #include "Lighting.cginc"

        fixed4 _Diffuse;
        fixed4 _Specular;
        float _Gloss;

        struct a2v
        {
            //模型空間的頂點座標
            float4 vertex:POSITION;
            //模型空間的法線方向
            float3 normal:NORMAL;

        };

        struct v2f
        {
            //輸出的是裁剪空間的頂點座標
            float4 pos:SV_POSITION;

            float3 color:COLOR;

        };

        v2f vert(a2v v){
            v2f o;

            //利用unity內置的模型-觀察-投影矩陣將頂點座標轉換到裁剪空間
            o.pos=UnityObjectToClipPos(v.vertex);
            //通過內置變量獲取環境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
            //法線轉換到世界座標
            //unity_WorldToObject爲模型空間到世界空間的變換矩陣的逆矩陣
            fixed3 worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
            //獲取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式計算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
            //利用Cg內置反射光線方向計算函數計算反射光線
            fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
            //mul(unity_ObjectToWorld, v.vertex)將頂點座標轉換爲世界座標
            //視角方向=攝像頭位置-頂點位置
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - mul(unity_ObjectToWorld, v.vertex).xyz);
            //根據公式計算高光反射
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
            o.color=ambient+diffuse+specular;

            return o;
        }

        fixed4 frag(v2f i):SV_Target
        {
            return fixed4(i.color,1.0);
        }

        ENDCG
    }
}
FallBack "Specular"}

這裏高光部分明顯不平滑,這是因爲高光反射的計算是非線性的,而在頂點着色器中計算光照再進行差值的過程是線性的,破壞了原有的視覺問題

逐像素光照

 Shader "Test Shader/SpecularPixelLevel"{
Properties
{
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    //控制高光反射顏色
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    //控制高光區域大小
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
    Pass
    {
        Tags { "LightMode"="ForwardBase" }
        CGPROGRAM
        #pragma vertex vert
        #pragma fragment frag
        #include "Lighting.cginc"
        fixed4 _Diffuse;
        fixed4 _Specular;
        float _Gloss;
        struct a2v
        {
            //模型空間的頂點座標
            float4 vertex:POSITION;
            //模型空間的法線方向
            float3 normal:NORMAL;
        };
        struct v2f
        {
            //輸出的是裁剪空間的頂點座標
            float4 pos:SV_POSITION;
            float3 worldNormal:TEXCOORD0;
            float3 worldPos:TEXCOORD1; 
        };
        v2f vert(a2v v){
            v2f o;
            //利用unity內置的模型-觀察-投影矩陣將頂點座標轉換到裁剪空間
            o.pos=UnityObjectToClipPos(v.vertex);
            //法線轉換到世界座標
            //unity_WorldToObject爲模型空間到世界空間的變換矩陣的逆矩陣
            o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));
            o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;
            return o;
        }
        fixed4 frag(v2f i):SV_Target
        {
            //通過內置變量獲取環境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldNormal=normalize(i.worldNormal);
            //獲取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式計算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
            //利用Cg內置反射光線方向計算函數計算反射光線
            fixed3 reflectDir = normalize(reflect(-worldLight, worldNormal));
            //mul(unity_ObjectToWorld, v.vertex)將頂點座標轉換爲世界座標
            //視角方向=攝像頭位置-頂點位置
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
            //根據公式計算高光反射
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(reflectDir, viewDir)), _Gloss);
            return fixed4(ambient+diffuse+specular,1.0);
        }
        ENDCG
    }
}
FallBack "Specular"}

這樣的光照反射模型更平滑,而這一光照模型就是著名的phong光照模型

Blinn-Phong光照模型

blinn模型沒有使用反射方向。而是引入了矢量h

h⃗ =(v⃗ +l⃗ )|v⃗ +l⃗ |

blinn模型光照公式如下
Cdiffuse=(ClightMdiffuse)max(0,n⃗ h⃗ )Mgloss
Shader "Test Shader/BlinnPhone"{
Properties
{
    _Diffuse ("Diffuse", Color) = (1, 1, 1, 1)
    //控制高光反射顏色
    _Specular ("Specular", Color) = (1, 1, 1, 1)
    //控制高光區域大小
    _Gloss ("Gloss", Range(8.0, 256)) = 20
}
SubShader
{
    Pass
    {
        Tags { "LightMode"="ForwardBase" }

        CGPROGRAM

        #pragma vertex vert
        #pragma fragment frag

        #include "Lighting.cginc"

        fixed4 _Diffuse;
        fixed4 _Specular;
        float _Gloss;

        struct a2v
        {
            //模型空間的頂點座標
            float4 vertex:POSITION;
            //模型空間的法線方向
            float3 normal:NORMAL;

        };

        struct v2f
        {
            //輸出的是裁剪空間的頂點座標
            float4 pos:SV_POSITION;

            float3 worldNormal:TEXCOORD0;
            float3 worldPos:TEXCOORD1; 

        };

        v2f vert(a2v v){
            v2f o;

            //利用unity內置的模型-觀察-投影矩陣將頂點座標轉換到裁剪空間
            o.pos=UnityObjectToClipPos(v.vertex);

            //法線轉換到世界座標
            //unity_WorldToObject爲模型空間到世界空間的變換矩陣的逆矩陣
            o.worldNormal=normalize(mul(v.normal,(float3x3)unity_WorldToObject));

            o.worldPos=mul(unity_ObjectToWorld, v.vertex).xyz;


            return o;
        }

        fixed4 frag(v2f i):SV_Target
        {
            //通過內置變量獲取環境光
            fixed3 ambient=UNITY_LIGHTMODEL_AMBIENT.xyz;
            fixed3 worldNormal=normalize(i.worldNormal);
            //獲取光源方向
            fixed3 worldLight=normalize(_WorldSpaceLightPos0.xyz);
            //利用漫反射光照公式計算漫反射
            fixed3 diffuse=_LightColor0.rgb*_Diffuse.rgb*max(0,dot(worldNormal,worldLight));
            //mul(unity_ObjectToWorld, v.vertex)將頂點座標轉換爲世界座標
            //視角方向=攝像頭位置-頂點位置
            fixed3 viewDir = normalize(_WorldSpaceCameraPos.xyz - i.worldPos.xyz);
            //計算矢量h
            fixed3 h=normalize(worldLight+viewDir);
            //根據公式計算高光反射
            fixed3 specular = _LightColor0.rgb * _Specular.rgb * pow(saturate(dot(worldNormal, h)), _Gloss);
            return fixed4(ambient+diffuse+specular,1.0);
        }

        ENDCG
    }
}
FallBack "Specular"}

這樣的高光反射模型反射部分更大更亮些,大多數情況下,我們都會選擇這一高光反射模型
最後結果如下
這裏寫圖片描述
從右到左分別是逐頂點光照,逐像素光照phong模型,blinn模型

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章