首页 > 技术文章 > Lambert/Diffuse 光照模型

naturelight 2016-05-10 18:24 原文

Lambert/Diffuse光照模型的特点:各向同性,即与观察的方向无关,反射光只与入射光和入射角度相关。

1.光源垂直照射平面

 

 如图,设入射光量为Ф, 平面面积为A, 则可以认为平面上每一点获取的光量为 Ф/A

 

2.光源斜射平面, 与平面法线N成角度θ

如图,设入射光量为Ф,光线与法线的交角为θ, 光源的横截面的宽度为L,受光面的宽度为L',则有 L/L' = cosθ.相对于垂直照射,斜照的时候,受光面更大了,光就更分散了,平均每一点获取到的光量就变小了,只有垂直照射的百分之cosθ. 由于cos0° = 1, 两种情况可以统一在一起。另外, cosθ的数值会存在小于0的情况,所以光照模型在实现时一般会取 max(0, cosθ)值。

3.Lambert/Diffuse 的 Unity Shader 实现。

 (Unity5.3.4)Shader代码:

 1 Shader "Custom/SimpleDiffuse"
 2 {
 3     Properties
 4     {
 5         [NoScaleOffset]_MainTex ("Texture", 2D) = "white" {}
 6     }
 7     SubShader
 8     {
 9         LOD 100
10 
11         Pass
12         {
13             // indicate that our pass is the "base" pass in forward
14             // rendering pipeline. It gets ambient and main directional
15             // light data set up; light direction in _WorldSpaceLightPos0
16             // and color in _LightColor0
17             Tags{ "LightMode"="ForwardBase" }
18 
19             CGPROGRAM
20             #pragma vertex vert
21             #pragma fragment frag
22             
23             #include "UnityCG.cginc"
24             #include "UnityLightingCommon.cginc" //for _LightColor0
25 
26             struct v2f
27             {
28                 float2 uv : TEXCOORD0;
29                 float4 diff: COLOR0;
30                 float4 vertex : SV_POSITION;
31             };
32 
33             sampler2D _MainTex;
34             float4 _MainTex_ST;
35 
36             v2f vert (appdata_base v)
37             {
38                 v2f o;
39 
40                 o.vertex = mul(UNITY_MATRIX_MVP, v.vertex);
41                 o.uv = v.texcoord;
42 
43                 half3 worldNormal = UnityObjectToWorldNormal(v.normal);
44         
45                 half diffuse = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));//calculate  cosθ
46                 
47                 o.diff = diffuse * _LightColor0 ;//diffuse
48 
49                 return o;
50             }
51             
52             fixed4 frag (v2f i) : SV_Target
53             {
54                 // sample the texture
55                 fixed4 col = tex2D(_MainTex, i.uv);
56 
57                 col *= i.diff;
58 
59                 return col;
60             }
61 
62             ENDCG
63         }
64     }
65 }

此Shader是Vertex/Fragment Shader而不是SurfaceShader,因为涉及到与光源交互,需要包含UnityLightingComming.cginc文件(第24行代码)。 Tags{ "LightMode"="ForwardBase" } (第17行代码)的目的是声明使用的是Forward Rendering Path, 并且LightMode是ForwardBase,这意味着如果你场景有Directional Light的话,Unity的ShaderLab会把它的位置和颜色和_WorldSpaceLightPos0与_LightColor0这个两个内置变量关联起来,提供给Shader代码引用。所以如果你的场景中并没有Direction Light的话,而是只有Spot Light或者是只有Area Light甚至没有任何光源的话,下图中的猩猩表面会是一片黑,不会有现在这种带毛发效果的图像。(http://docs.unity3d.com/Manual/RenderTech-ForwardRendering.html是关于Forward Rendering的介绍)。

效果图:

 

4.Half Lambert (主要作用是“让较暗的地方看起来不会过于太暗”)

half diffuse = max(0, dot(worldNormal, _WorldSpaceLightPos0.xyz));
这里得到的diffuse范围在0~1,而且很时候存在很多点的diffuse值很小、很接近0,反映在画面的话就是模型存在局部地方很暗。
Half Lambert 将diffuse的值映射到0.5~1.0,从而将较暗的地方亮度提高。

代码:diffuse = diffuse * 0.5f + 0.5f;
效果:


左边的是Half Lambert,相对右图整体会亮一点,更主要的是猩猩的臀部和胸口不会显得特别暗,更接近真实。

5.Lambert/Diffuse 优点:简单高效,比较适合“建筑物”一类的物体材质。缺点:无高光,没有考虑环境光,不适合带镜面反射的物体材质。

推荐阅读