Lambert 中的光照模型 即漫反射模型 C_diffuse=(C_lightcdotM_diffuse)saturate(vecncdotvecl) C_diffuse 是物体的漫反射颜色,C_light 是入射光线颜色,M_diffuse 为物体材质的漫反射颜色,vecn 是物体的表面法线.vecl 是物体指向灯光的方向 根据点积的定义,表面法线与灯光方向的夹角越小,点积结果就越大,漫反射越强. saturate 函数可以将结果截取到 [0,1] 的区间内,避免负值,其实就是 max (0,data)
# Lambert
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| Shader "ShaderLab/Lambert" { Properties { _MainColor("Main Color", Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //声明包含灯光变量的文件 #include "UnityLightingCommon.cginc" struct v2f { float4 pos : SV_POSITION; fixed4 dif : COLOR0; }; fixed4 _MainColor; v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex);
//法线向量 float3 n = UnityObjectToWorldNormal(v.normal); n = normalize(n);
//灯光方向向量 fixed3 l = normalize(_WorldSpaceLightPos0.xyz);
//按照公式计算漫反射 fixed ndot1 = dot(n, 1); o.dif = _LightColor0 * _MainColor * saturate(ndot1);
return o;
}
fixed4 frag(v2f i) :SV_Target { return i.dif; } ENDCG } } }
|
# 结果:
![]()
# HalfLambert
由上面可以发现,Lambert 模型有一个致命缺点就是物体背面完全是黑的,看不到表面任何细节,为了解决这个问题。人们提出了 HalfLambert 模型. C\_{diffuse} = (C\_{light} \\cdot M\_{diffuse})\[0.5(\\vec{n} \\cdot \\vec{l}) + 0.5\]
# 代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49
| Shader "ShaderLab/HalfLambert" { Properties { _MainColor("Main Color", Color) = (1,1,1,1) } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //声明包含灯光变量的文件 #include "UnityLightingCommon.cginc" struct v2f { float4 pos : SV_POSITION; fixed4 dif : COLOR0; }; fixed4 _MainColor; v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex);
//法线向量 float3 n = UnityObjectToWorldNormal(v.normal); n = normalize(n);
//灯光方向向量 fixed3 l = normalize(_WorldSpaceLightPos0.xyz);
//按照公式计算漫反射 fixed ndot1 = dot(n, 1); o.dif = _LightColor0 * _MainColor * (0.5* ndot1 +0.5);
return o;
}
fixed4 frag(v2f i) :SV_Target { return i.dif; } ENDCG } } }
|
# 最终效果
![]()
# Phong 光照模型
表面光滑的物体需要使用 Phong 光照模型 Phong 教授认为物体表面反射光线主要由三部分组成: SurfaceColor=C_Ambient+C_Diffuse+C_Specular 其中: C_Ambient 是环境光
C_Diffuse 是漫反射
C_Specular 是镜面反射
# 镜面反射计算公式
C_Specular=(C_lightcdotM_specular)saturate(vecvcdotvecr)M_shininess 其中,C_light 是灯光亮度,M_specular 是物体材质的镜面反射颜色,vecv 是视角方向.vecr 是光线的反射方向,M_shininess 是物体表面光泽度
# Unity 提供的环境光变量
-
unity_AmbientSky fixed4 Gradient 类型环境中的 Sky Color
-
unity_AmbientEquator fixed4 Gradient 类型环境中的 Equator Color
-
unity_AmbientGround fixed4 Gradient 类型环境中的 Ground Color
-
UNITY_LIGHTMODEL_AMBIENT fixed4 Gradient 类型环境中的 Sky Color, 将被 unity_AmbientSky 取代
# Phong Shader
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| Shader "ShaderLab/Phong" { Properties { _MainColor("Main Color", Color) = (1,1,1,1) _SpecularColor("Specular Color",Color) = (0,0,0,0) _Shininess("Shininess",Range(1,100)) = 1 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //声明包含灯光变量的文件 #include "Lighting.cginc" struct v2f { float4 pos : SV_POSITION; fixed4 color : COLOR0; }; fixed4 _MainColor; fixed4 _SpecularColor; //光滑度 half _Shininess;
v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex);
//计算公式中的各个变量 float3 n = UnityObjectToWorldNormal(v.normal); n = normalize(n); fixed3 l = normalize(_WorldSpaceLightPos0.xyz); fixed3 view = normalize(WorldSpaceViewDir(v.vertex));
//漫反射部分 fixed ndot1 = saturate(dot(n, 1)); fixed4 dif = _LightColor0 * _MainColor * ndot1;
//镜面反射部分 float3 ref = reflect(-1, n); ref = normalize(ref); fixed rdotv = saturate(dot(ref, view)); fixed4 spec = _LightColor0 * _SpecularColor * pow(rdotv, _Shininess);
//环境光+漫反射+镜面反射 o.color = unity_AmbientSky + dif + spec;
return o; }
fixed4 frag(v2f i) :SV_Target { return i.color; } ENDCG } } }
|
# Phong 效果
![]()
# 逐像素光照
上面的 Phong 模型很难看到高光范围,
是因为使用的是逐顶点光照,插值得到所有像素的颜色。逐像素着色器的计算量是根据屏幕分辨率大小确定的.
# 修改 Phong 模型到逐像素
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63
| Shader "ShaderLab/PhongPerPixel" { Properties { _MainColor("Main Color", Color) = (1,1,1,1) _SpecularColor("Specular Color",Color) = (0,0,0,0) _Shininess("Shininess",Range(1,100)) = 1 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //声明包含灯光变量的文件 #include "Lighting.cginc" struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float4 vertex : TEXCOORD1; }; fixed4 _MainColor; fixed4 _SpecularColor; //光滑度 half _Shininess;
v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.normal = v.normal; o.vertex = v.vertex;
return o; }
fixed4 frag(v2f i) :SV_Target { //计算公式中的各个变量 float3 n = UnityObjectToWorldNormal(i.normal); n = normalize(n); fixed3 l = normalize(_WorldSpaceLightPos0.xyz); fixed3 view = normalize(WorldSpaceViewDir(i.vertex));
//漫反射部分 fixed ndotl = saturate(dot(n, l)); fixed4 dif = _LightColor0 * _MainColor * ndotl;
//镜面反射部分 float3 ref = reflect(-l, n); ref = normalize(ref); fixed rdotv = saturate(dot(ref, view)); fixed4 spec = _LightColor0 * _SpecularColor * pow(rdotv, _Shininess);
return unity_AmbientSky + dif + spec;
} ENDCG } } }
|
# 效果
![]()
# Blinn-Phong 光照模型
只是改进了算法,即将计算反射向量改成了计算半角向量,降低了计算复杂度
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
| Shader "ShaderLab/BlinnPhong" { Properties { _MainColor("Main Color", Color) = (1,1,1,1) _SpecularColor("Specular Color",Color) = (0,0,0,0) _Shininess("Shininess",Range(1,100)) = 1 } SubShader { Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag #include "UnityCG.cginc" //声明包含灯光变量的文件 #include "Lighting.cginc" struct v2f { float4 pos : SV_POSITION; float3 normal : TEXCOORD0; float4 vertex : TEXCOORD1; }; fixed4 _MainColor; fixed4 _SpecularColor; //光滑度 half _Shininess;
v2f vert(appdata_base v) { v2f o; o.pos = UnityObjectToClipPos(v.vertex); o.normal = v.normal; o.vertex = v.vertex;
return o; }
fixed4 frag(v2f i) :SV_Target { //计算公式中的各个变量 float3 n = UnityObjectToWorldNormal(i.normal); n = normalize(n); fixed3 l = normalize(_WorldSpaceLightPos0.xyz); fixed3 view = normalize(WorldSpaceViewDir(i.vertex));
//漫反射部分 fixed ndotl = saturate(dot(n, l)); fixed4 dif = _LightColor0 * _MainColor * ndotl;
//镜面反射部分 fixed3 h = normalize(l + view); fixed ndoth = saturate(dot(n, h)); fixed4 spec = _LightColor0 * _SpecularColor * pow(ndoth, _Shininess);
return unity_AmbientSky + dif + spec;
} ENDCG } } }
|
# 效果
![]()