Unity3D學習(六):《Unity Shader入門精要》——Unity的基礎光照

前言
光學中,咱們是用輻射度來量化光。
光照按照不一樣的散射方向分爲:漫反射(diffuse)和高光反射(specular)。高光反射描述物體是如何反射光線的,漫反射則表示有多少光線會被折射、吸取和散射出表面。根據入射光線的數量和方向,咱們能夠計算出射光線的數量和方向,一般使用出射度描述它。輻射度和出射度之間是線性關係的,它們之間的比值就是材質的漫反射和高光反射屬性。
BRDF模型
早期的遊戲引擎通常只有一個光照模型,BRDF模型,即標準光照模型(Bidirectional Reflectance Distribution Function),又稱Phong模型。
它的基本方法是,把進入到攝像機內的光線分爲4部分,每部分使用一種方法來計算它的貢獻度。
  • 自發光  描述當給定一個方向時,一個表面自己會向該方向發射多少輻射量。注意,若是沒有使用全局光照,這些自發光的表面並不會照亮周圍的物體,只是他本體看起來更亮而已。
  • 高光反射(金屬之類的)  描述當光線從光源照射到模型表面時,該表面會在徹底鏡面反射方向散射多少輻射量。
  • 漫反射  該表面會向四周散射多少輻射量
  • 環境光  描述其餘全部的間接光照(就是其餘物體的發射的光線)。 
蘭伯特定律
發射光線的強度與表面法線和光源方向之間的夾角餘弦值成正比。
 
漫發射的計算公式
   C = (c * m) * max( 0 , n * l )。
小寫c爲光源顏色,m爲漫反射顏色,n是表面法線,l是指向光源的單位矢量。須要注意應該防止發現和光源點乘的結果爲負值(避免物體被從後面來的光源照亮),因此用max函數限制其爲正數。
 
高光反射的計算公式
  •   r = 2(n * l)n - l
  •  C = (c * m) * (max(0,v * r)) ^ gloss
n,l表明意義與漫反射公式相同,r爲光的反射方向矢量,m爲高光反射顏色, v 爲 視角方向矢量,gloss爲材質的光澤度,gloss越大亮點就越小。
 
 
Blinn模型的高光反射計算公式
  與上述Phong模型不一樣的是,Blinn模型引入了一個新矢量h,經過對 v 和 l的取平均後再歸一化獲得:h = ( v + l ) / | v + l |。
  公式爲:C = ( c * m) * (max( 0, n * h ))^gloss
 
逐像素與逐頂點光照
 在片元着色器中計算,稱爲逐像素光照。在頂點着色器中計算,稱爲逐頂點光照(高羅德着色)。逐頂點光照是在每一個頂點上計算光照,而後在渲染圖元內部進行線性插值,最後輸出成像素顏色。因爲頂點數目小於像素數目,因此其計算量小於逐像素光照,所以在陰影交界處會出現鋸齒,精細度不如逐像素光照。
 
Unity的環境光和自發光
環境光能夠經過Shader的內置變量UNITY_LIGHTMODEL_AMBIENT訪問
自發光只須要在片元着色器輸出最後的顏色以前,把材質的自發光顏色添加到輸出顏色上就行。
 
漫反射和高光反射的Shader實現
漫反射光照模型(逐頂點)
Shader "Unity Shader Book/Chapter6/Diffuse Vertex-Level"
{
       Properties
       {
             _Diffuse("Diffuse",Color) = (1.0,1.0,1.0,1.0)
       }
       SubShader
       {
             Tags { "LightMode"="ForwardBase" }   
 
             Pass
             {
                    CGPROGRAM
                    #pragma vertex vert
                    #pragma fragment frag
                    
                    #include "Lighting.cginc"
 
                    fixed4 _Diffuse;
                    struct appdata
                    {
                          float4 vertex : POSITION; //頂點在模型空間的座標
                          float3 normal : NORMAL;  //法線
                    } ;
 
                    struct v2f
                    {
                          fixed3 color : COLOR;  //輸出顏色
                          float4 pos : SV_POSITION;  //輸出位置
                    } ;
 
                    
                    v2f vert (appdata v)
                    {
                          v2f o;
                          o.pos = UnityObjectToClipPos(v.vertex);  //模型空間轉換到裁剪空間
 
                          fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;  //環境光
 
                          fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject)); //法線方向n
 
                          fixed3 worldLight  = normalize(_WorldSpaceLightPos0.xyz); //光源位置
 
                          fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLight));  //公式計算
 
                          o.color = ambient + diffuse;
 
                          return o;
                    }
                    
                    fixed4 frag (v2f i) : SV_Target
                    {
                          return fixed4(i.color,1.0);
                    }
                    ENDCG
             }
       }
}

 

高光反射模型(逐頂點)
Shader "Unity Shader Book/Chapter6/SpecularVertexLevel"
{
       Properties
       {
             _Diffuse("Diffuse",Color) = (1.0,1.0,1.0,1.0)
             _Specular("Specular",Color) = (1.0,1.0,1.0,1.0)
             _Gloss("Gloss",Range(8.0,256)) = 20 //size of specular area
       }
       SubShader
       {
             Tags { "LightMode"="ForwardBase" }   //Be careful ,The light direction will be opposite without this
 
             Pass
             {
                    CGPROGRAM
                    #pragma vertex vert
                    #pragma fragment frag
                    
                    #include "Lighting.cginc"
 
                    fixed4 _Diffuse;
                    fixed4 _Specular;
                    float  _Gloss;
                    struct appdata
                    {
                          float4 vertex : POSITION;
                          float3 normal : NORMAL;
                    } ;
 
                    struct v2f
                    {
                          float3 color: COLOR;
                          float4 pos : SV_POSITION;
                    } ;
                    
                    v2f vert (appdata v)
                    {
                          v2f o;
                          o.pos = UnityObjectToClipPos(v.vertex);
                          //equal to "o.pos = mul(UNITY_MATRIX_MVP,v.vertex);"
 
                          fixed3 ambient = UNITY_LIGHTMODEL_AMBIENT.xyz;
 
                          fixed3 worldNormal = normalize(mul(v.normal,(float3x3)unity_WorldToObject));
                          // or use UnityObjectToWorldNormal(v.normal)
                          fixed3 worldLightDir = normalize(_WorldSpaceLightPos0.xyz);
 
                          fixed3 diffuse = _LightColor0.rgb * _Diffuse.rgb * saturate(dot(worldNormal,worldLightDir));
 
                          fixed3 reflectDir = normalize(reflect(-worldLightDir,worldNormal)); //反射方向矢量r
 
                          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
             }
       }
}
相關文章
相關標籤/搜索