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
             }
       }
}