Shader 編程(二)使用光照模型來計(jì)算某個(gè)點(diǎn)的光照效果

什么是光照模型
光照模型就是一個(gè)公式,使用這個(gè)公式來計(jì)算在某個(gè)點(diǎn)的光照效果

標(biāo)準(zhǔn)光照模型
在標(biāo)準(zhǔn)光照模型里面,我們把進(jìn)入攝像機(jī)的光分為下面四個(gè)部分
自發(fā)光
高光反射
Blinn光照模型
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和視野方向的夾角
Blinn-Phong光照模型
Specular=直射光 * pow( max(cosθ,0),10) θ:是法線和x的夾角 x 是平行光和視野方向的平分線
漫反射 Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
環(huán)境光

Tags{ "LightMode"="ForwardBase" }
只有定義了正確的LightMode才能得到一些Unity的內(nèi)置光照變量
include "Lighting.cginc"
包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量

normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
max() 用來取得函數(shù)中最大的一個(gè)
dot 用來取得兩個(gè)向量的點(diǎn)積
_WorldSpaceLightPos0 取得平行光的位置
_LightColor0取得平行光的顏色
UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
UNITY_LIGHTMODEL_AMBIENT用來獲取環(huán)境光

(1)蘭伯特光照模型計(jì)算公式(漫反射計(jì)算方式)

(漫反射)Diffuse = 直射光顏色 * max(0,cos夾角(光和法線的夾角) ) cosθ = 光方向· 法線方向
下面我們使用逐頂點(diǎn)光照和逐像素光照來實(shí)現(xiàn)蘭伯特光照模型計(jì)算公式。

逐頂點(diǎn)光照

兩種方式可以實(shí)現(xiàn)漫反射 第一種是逐頂點(diǎn)實(shí)現(xiàn)漫反射,第二種方式是逐像素實(shí)現(xiàn)漫反射。
逐頂點(diǎn)就是將光照的計(jì)算方式放在頂點(diǎn)函數(shù)里面,因?yàn)轫旤c(diǎn)個(gè)數(shù)是有限的,而像素個(gè)數(shù)是無限的,逐頂點(diǎn)計(jì)算是頂點(diǎn)與頂點(diǎn)之間的計(jì)算時(shí)是使用了插值的計(jì)算方式計(jì)算,這樣比較平和,但是頂點(diǎn)是直接計(jì)算出來的所以頂點(diǎn)上不會(huì)太平和。下面我們來實(shí)現(xiàn)第一種逐頂點(diǎn)。

Shader"Diffuse"{
    properties{
        _Diffuse("Color",Color) = (1,1,1,1)
    }
        SubShader{

        Pass {

        //定義正確的LightMode得到內(nèi)置的光照變量
        Tags{"LightMode" = "ForwardBase"}

        CGPROGRAM
        //包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量  相當(dāng)于引入內(nèi)置的光照模型
        #include "Lighting.cginc"http://取得第一個(gè)直射光的顏色  _LightColor0第一個(gè)值是光的位置_WorldSpaceLightPos0

        #pragma vertex vert
        #pragma fragment frag

                   fixed4 _Diffuse;

            struct a2v {
               float4 vertex:POSITION;
               float3 normal:NORMAL; 
              };
        struct v2f {
        
            float4 position:SV_POSITION;
            fixed3 color : COLOR;
        }; 
        v2f vert(a2v v) {
                v2f f;
                //UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
                f.position = UnityObjectToClipPos(v.vertex);
                //環(huán)境光
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

                //_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
                fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
                //normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
                //_WorldSpaceLightPos0 取得平行光的位置
                fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向  因?yàn)楣馐瞧叫泄?                //_LightColor0取得平行光的顏色
                //max() 用來取得函數(shù)中最大的一個(gè)
                //dot 用來取得兩個(gè)向量的點(diǎn)積
                fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
                    f.color = diffuse+ ambient;
                return f;
            }

        fixed4 frag(v2f f) :SV_Target{
            return fixed4(f.color,1);

        }
            ENDCG
       }
    }
        Fallback"VertexLit"
}

因?yàn)樵诔绦蚶锩嬖O(shè)置了環(huán)境光所以在暗部會(huì)受到環(huán)境光的影響


效果展示.png

這里設(shè)置受SkyBox環(huán)境光的影響,也可以自己將其他物體或者顏色設(shè)置成環(huán)境光顏色


Paste_Image.png

逐像素光照

逐頂點(diǎn)光照沒有逐像素光照渲染的平和 ,但是逐像素光照渲染更加耗費(fèi)性能
Shader"Fragment"{
    properties{
        _Diffuse("Color",Color) = (1,1,1,1)
    }
        SubShader{
        Pass {
        //定義正確的LightMode得到內(nèi)置的光照變量
        Tags{"LightMode" = "ForwardBase"}
        CGPROGRAM
        //包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量  相當(dāng)于引入內(nèi)置的光照模型
        #include "Lighting.cginc"http://取得第一個(gè)直射光的顏色  _LightColor0第一個(gè)值是光的位置_WorldSpaceLightPos0
        #pragma vertex vert
        #pragma fragment frag

                   fixed4 _Diffuse;

            struct a2v {
               float4 vertex:POSITION;
               float3 normal:NORMAL; 
              };
        struct v2f {
        
            float4 position:SV_POSITION;
            fixed3 worldNormalDir : COLOR0;

        }; 
        v2f vert(a2v v) {
                v2f f;
                //UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
                f.position = mul(UNITY_MATRIX_MVP, v.vertex);   
                f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);
                return f;
            }

        fixed4 frag(v2f f) :SV_Target{
            //環(huán)境光
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

        //_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
        fixed3 normalDir = normalize(f.worldNormalDir);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間

        //normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
        //_WorldSpaceLightPos0 取得平行光的位置
        fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向  因?yàn)楣馐瞧叫泄?
         //_LightColor0取得平行光的顏色
        //max() 用來取得函數(shù)中最大的一個(gè)
        //dot 用來取得兩個(gè)向量的點(diǎn)積
        fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
        fixed3 tempColor  = diffuse + ambient;
            return fixed4(tempColor,1);
        }
            ENDCG
       }
    }
        Fallback"Diffuse"
}
逐頂點(diǎn)逐像素對(duì)比.png

(2)半蘭伯特光照模型計(jì)算公式(漫反射計(jì)算方式)

Diffuse = 直射光顏色 *( cosθ *0.5 +0.5 )
因?yàn)樘m伯特光照模型在背光面會(huì)完全看不清,所以半蘭伯特光照模型應(yīng)時(shí)而生。


蘭伯特光照模型.png
Shader"Lambert"{
    properties{
        _Diffuse("Color",Color) = (1,1,1,1)
    }
        SubShader{

        Pass {

        //定義正確的LightMode得到內(nèi)置的光照變量
        Tags{"LightMode" = "ForwardBase"}

        CGPROGRAM
        //包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量  相當(dāng)于引入內(nèi)置的光照模型
        #include "Lighting.cginc"http://取得第一個(gè)直射光的顏色  _LightColor0第一個(gè)值是光的位置_WorldSpaceLightPos0

        #pragma vertex vert
        #pragma fragment frag

                   fixed4 _Diffuse;

            struct a2v {
               float4 vertex:POSITION;
               float3 normal:NORMAL; 
              };
        struct v2f {
        
            float4 position:SV_POSITION;
            fixed3 worldNormalDir : COLOR0;

        }; 
        v2f vert(a2v v) {
                v2f f;
                //UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
                f.position = mul(UNITY_MATRIX_MVP, v.vertex);
                
                f.worldNormalDir = mul(v.normal, (float3x3)unity_WorldToObject);

                return f;
            }

        fixed4 frag(v2f f) :SV_Target{
            //環(huán)境光
            fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;

        //_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
        fixed3 normalDir = normalize(f.worldNormalDir);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間


        //normalize() 用來把一個(gè)向量,單位化(原來方向保持不變,長度變?yōu)?)
        //_WorldSpaceLightPos0 取得平行光的位置
        fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向  因?yàn)楣馐瞧叫泄?
        float halfLambert = dot(normalDir, lightDir)*0.5 + 0.5;

         //_LightColor0取得平行光的顏色
        //max() 用來取得函數(shù)中最大的一個(gè)
        //dot 用來取得兩個(gè)向量的點(diǎn)積
        fixed3 diffuse = _LightColor0.rgb*halfLambert*_Diffuse.rgb;//取得漫反射的顏色
        fixed3 tempColor  = diffuse + ambient;
            return fixed4(tempColor,1);

        }
            ENDCG
       }
    }
        Fallback"Diffuse"
}
Paste_Image.png

這樣對(duì)比一下背光面蘭伯特光照模型和半蘭伯特光照模型??梢钥吹胶苊黠@的差別,半蘭伯特光照模型即使暗部就不會(huì)是全黑。

(3)高光反射計(jì)算方式

Blinn光照模型 計(jì)算公式
Specular=直射光 * pow( max(cosθ,0),10) θ:是反射光方向和視野方向的夾角
這里使用逐頂點(diǎn)光照模型寫出高光反射。

Shader"Specular"{
    properties{
        _Diffuse("Color",Color) = (1,1,1,1)
        _Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
       _Gloss("Gloss",Range(8,200))=10//控制高光強(qiáng)度

    }
        SubShader{

        Pass {

        //定義正確的LightMode得到內(nèi)置的光照變量
        Tags{"LightMode" = "ForwardBase"}

        CGPROGRAM
        //包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量  相當(dāng)于引入內(nèi)置的光照模型
        #include "Lighting.cginc"http://取得第一個(gè)直射光的顏色  _LightColor0第一個(gè)值是光的位置_WorldSpaceLightPos0

        #pragma vertex vert
        #pragma fragment frag

                   fixed4 _Diffuse;
                   half _Gloss;
                   fixed4 _Specular;

            struct a2v {
               float4 vertex:POSITION;
               float3 normal:NORMAL; 
              };
        struct v2f {
        
            float4 position:SV_POSITION;
            fixed3 color : COLOR;

        }; 
        v2f vert(a2v v) {
                v2f f;
                //UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
                f.position = mul(UNITY_MATRIX_MVP, v.vertex);
                
                fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環(huán)境光

                //_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
                fixed3 normalDir = normalize(mul(v.normal, (float3x3)unity_WorldToObject));//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間
             
                fixed3 lightDir = normalize( _WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向  因?yàn)楣馐瞧叫泄?        
                fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色

                fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
                fixed3 vlewDir = normalize(_WorldSpaceCameraPos.xyz - mul(v.vertex, unity_WorldToObject).xyz);

                fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot( reflectDir, vlewDir), 0), _Gloss);//計(jì)算高光

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

        fixed4 frag(v2f f) :SV_Target{
            return fixed4(f.color,1);

        }
            ENDCG
       }
    }
        Fallback"Diffuse"
}
高光強(qiáng)度調(diào)節(jié).gif

高光顏色調(diào)節(jié).gif

下面我們用逐像素來實(shí)現(xiàn)高光反射

Shader"Specular Fragment"{
    properties{
        _Diffuse("Color",Color) = (1,1,1,1)
        _Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
       _Gloss("Gloss",Range(8,200))=10//控制高光強(qiáng)度

    }
        SubShader{

        Pass {

        //定義正確的LightMode得到內(nèi)置的光照變量
        Tags{"LightMode" = "ForwardBase"}

        CGPROGRAM
        //包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量  相當(dāng)于引入內(nèi)置的光照模型
        #include "Lighting.cginc"http://取得第一個(gè)直射光的顏色  _LightColor0第一個(gè)值是光的位置_WorldSpaceLightPos0

        #pragma vertex vert
        #pragma fragment frag

                   fixed4 _Diffuse;
                   half _Gloss;
                   fixed4 _Specular;

            struct a2v {
               float4 vertex:POSITION;
               float3 normal:NORMAL; 
              };
        struct v2f {
        
            float4 position:SV_POSITION;
            float3 WorldNomormal : TEXCOORD0;
            float3 WorldVertex:TEXCOORD1;
        }; 
        v2f vert(a2v v) {
                v2f f;
                //UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
                f.position = mul(UNITY_MATRIX_MVP, v.vertex);
    
                f.WorldNomormal = mul(v.normal, (float3x3)unity_WorldToObject);
                f.WorldVertex = mul(v.vertex,_World2Object).xyz;
                return f;
        };

        fixed4 frag(v2f f) :SV_Target{

        fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環(huán)境光
         //_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
        fixed3 normalDir = normalize(f.WorldNomormal);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間

        fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向  因?yàn)楣馐瞧叫泄?
        fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色

        fixed3 reflectDir = normalize(reflect(-lightDir, normalDir));
        fixed3 vlewDir = normalize(_WorldSpaceCameraPos.xyz - f.WorldVertex);

        fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot(reflectDir, vlewDir), 0), _Gloss);//計(jì)算高光

        fixed3 tempColor = diffuse + ambient + specular;
    

            return fixed4(tempColor,1);

        }
            ENDCG
       }
    }
        Fallback"Diffuse"
}

可以看到逐像素更加柔和平滑

對(duì)比圖.png

Blinn-Phong光照模型
下面對(duì)我們對(duì)Blinn光照模型又作了一種改進(jìn),其計(jì)算方式為:
Specular=直射光 * pow( max(cosθ,0),10) θ:是法線和x的夾角 x 是平行光和視野方向的平分線。

Shader"Learning/Specular Fragment BlinnPhone"{
   properties{
       _Diffuse("Color",Color) = (1,1,1,1)
       _Specular("Specular Color",Color)=(1,1,1,1)//控制高光顏色
      _Gloss("Gloss",Range(8,200))=10//控制高光強(qiáng)度

   }
       SubShader{

       Pass {

       //定義正確的LightMode得到內(nèi)置的光照變量
       Tags{"LightMode" = "ForwardBase"}

       CGPROGRAM
       //包含unity的內(nèi)置的文件,才可以使用unity內(nèi)置的一些變量  相當(dāng)于引入內(nèi)置的光照模型
       #include "Lighting.cginc"http://取得第一個(gè)直射光的顏色  _LightColor0第一個(gè)值是光的位置_WorldSpaceLightPos0

       #pragma vertex vert
       #pragma fragment frag

                  fixed4 _Diffuse;
                  half _Gloss;
                  fixed4 _Specular;

           struct a2v {
              float4 vertex:POSITION;
              float3 normal:NORMAL; 
             };
       struct v2f {
       
           float4 position:SV_POSITION;
           float3 WorldNomormal : TEXCOORD0;
           float4 WorldVertex:TEXCOORD1;
       }; 
       v2f vert(a2v v) {
               v2f f;
               //UNITY_MATRIX_MVP 這個(gè)矩陣用來把一個(gè)坐標(biāo)從模型空間轉(zhuǎn)換到剪裁空間
               f.position = mul(UNITY_MATRIX_MVP, v.vertex);
   
               /*f.WorldNomormal = mul(v.normal, (float3x3)unity_WorldToObject);*/

               //UnityObjectToWorldNormal(float3 norm)  把法線方向 模型空間 == 》世界空間
               f.WorldNomormal = UnityObjectToWorldNormal(v.normal);

               f.WorldVertex = mul(v.vertex,unity_WorldToObject);
               return f;
       };

       fixed4 frag(v2f f) :SV_Target{
       fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.rgb;//環(huán)境光
        //_World2Object 這個(gè)矩陣用來把一個(gè)方向從世界空間轉(zhuǎn)換到模型空間
       fixed3 normalDir = normalize(f.WorldNomormal);//相當(dāng)于把模型空間轉(zhuǎn)換到世界空間

       //fixed3 lightDir = normalize(_WorldSpaceLightPos0.xyz);//對(duì)于每一個(gè)頂點(diǎn)來說 光的位置就是光的方向  因?yàn)楣馐瞧叫泄?
       //WorldSpaceLightDir(float4 v)  模型空間中的頂點(diǎn)坐標(biāo) == 》世界空間中從這個(gè)點(diǎn)到光源的方向
       fixed3 lightDir = normalize(WorldSpaceLightDir(f.WorldVertex).xyz);

       fixed3 diffuse = _LightColor0.rgb*max(0, dot(normalDir, lightDir))*_Diffuse.rgb;//取得漫反射的顏色
   
       //fixed3 vlewDir = normalize(_WorldSpaceCameraPos.xyz - f.WorldVertex);

       //UnityWorldSpaceViewDir(float4 v) 世界空間中的頂點(diǎn)坐標(biāo)==》世界空間從這個(gè)點(diǎn)到攝像機(jī)的觀察方向
       fixed3 vlewDir = normalize(UnityWorldSpaceViewDir(f.WorldVertex));

       fixed3 halfDir = normalize(vlewDir + lightDir);
       fixed3 specular = _LightColor0.rgb*_Specular.rgb*pow(max(dot(normalDir, halfDir), 0), _Gloss);//計(jì)算高光
       fixed3 tempColor = diffuse + ambient + specular;
           return fixed4(tempColor,1);
       }
           ENDCG
      }
   }
       Fallback"Diffuse"
}

在這里我們可以看到Blinn-Phong光照模型的高光更加大更加亮,并且在背光面B模型因?yàn)橐恍┯?jì)算所以背光面也會(huì)有一些高光,但是B—P模型就不會(huì)出現(xiàn)這種現(xiàn)象,一般我們更傾向于Blinn-Phong的計(jì)算方式.


Paste_Image.png
Paste_Image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 一.標(biāo)準(zhǔn)光照模型OpenGL與Direct3D提供了幾乎相同的固定功能光照模型。什么是固定功能光照模型?在過去只有...
    karma085閱讀 3,844評(píng)論 0 2
  • 一、Surface Output (表面著色器的標(biāo)準(zhǔn)輸出結(jié)構(gòu))Surface Shader的標(biāo)準(zhǔn)輸出結(jié)構(gòu)-第一要素...
    CarlDonitz閱讀 1,030評(píng)論 0 1
  • 版本記錄 前言 OpenGL 圖形庫項(xiàng)目中一直也沒用過,最近也想學(xué)著使用這個(gè)圖形庫,感覺還是很有意思,也就自然想著...
    刀客傳奇閱讀 6,454評(píng)論 0 2
  • 現(xiàn)實(shí)世界的光照是極其復(fù)雜的,而且會(huì)受到諸多因素的影響,這是以目前我們所擁有的處理能力無法模擬的。因此OpenGL的...
    IceMJ閱讀 2,130評(píng)論 1 6
  • 如果有一天我要去流浪不是因?yàn)槲覅捑肓思亦l(xiāng)不是難忍這里的冬天太長而是我終于得知了你的方向如果有一天我不再感傷不是因?yàn)?..
    lulucia閱讀 199評(píng)論 0 0

友情鏈接更多精彩內(nèi)容