Lambert 中的光照模型 即漫反射模型 C_diffuse=(C_lightcdotM_diffuse)saturate(vecncdotvecl)C\_{diffuse} = (C\_{light} \\cdot M\_{diffuse})saturate(\\vec{n} \\cdot \\vec{l}) C_diffuseC\_{diffuse} 是物体的漫反射颜色,C_lightC\_{light} 是入射光线颜色,M_diffuseM\_{diffuse} 为物体材质的漫反射颜色,vecn\\vec{n} 是物体的表面法线.vecl\\vec{l} 是物体指向灯光的方向 根据点积的定义,表面法线与灯光方向的夹角越小,点积结果就越大,漫反射越强. 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_SpecularSurfaceColor = C\_{Ambient} + C\_{Diffuse} + C\_{Specular} 其中: C_AmbientC\_{Ambient} 是环境光
C_DiffuseC\_{Diffuse} 是漫反射
C_SpecularC\_{Specular} 是镜面反射

# 镜面反射计算公式

C_Specular=(C_lightcdotM_specular)saturate(vecvcdotvecr)M_shininessC\_{Specular} = (C\_{light} \\cdot M\_{specular})saturate(\\vec{v} \\cdot \\vec{r})^{M\_{shininess}} 其中,C_lightC\_{light} 是灯光亮度,M_specularM\_{specular} 是物体材质的镜面反射颜色,vecv\\vec{v} 是视角方向.vecr\\vec{r} 是光线的反射方向,M_shininessM\_{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
}
}
}

# 效果

編集日

*~( ̄▽ ̄)~[お茶]を一杯ください

Solvarg WeChat 支払う

WeChat 支払う

Solvarg Alipay

Alipay

Solvarg PayPal

PayPal