Sorry, your browser cannot access this site
This page requires browser support (enable) JavaScript
Learn more >

兰伯特光照模型

一、必备知识点

1.两个颜色相乘

  1. 通常用于计算光照、材质混合、纹理混合等需求
  2. 它一种用于混合颜色叠加在一起的效果
  3. 计算结果 可以理解为两种颜色叠加在一起的效果
  4. 计算方式 通常使用颜色通道的值来执行逐通道的乘法
  5. 例子 颜色 A、B 都是 fixed4 类型的变量 颜色 A * 颜色 B =(A.r*B.r , A.g*B.g , A.b* B.b , A.a* B.a); 那么得到的这个结果就是 A、B 颜色看加后的结果

2.漫反射基本概念

漫反射(Diffuse Reflection)是光线撞击一个物体表面后以各个方向均匀地反射出去的过程在漫反射下,光线以无规律的方式散射,而不像镜面反射那样按照特定的角度反射。这种散射导致了物体表面看起来均匀而不闪烁的效果。

二、兰伯特光照模型的来历和原理

兰伯特(Lambert)光照模型,也称为朗伯反射模型,是由瑞士数学家约翰·海因里希·朗伯(Johann Heinrich Lambert)于 1768 年左右首次提出,朗伯是 18 世纪的一个杰出科学家,他在光学和数学领域作出了众多贡献。兰伯特光照模型描述了漫反射表面对光线的反射行为,它成为计算机图形学和渲染中重要的基础模型之一 原理: 兰伯特光照模型的理论是:

认为漫反射光的强度仅与入射光的方向和反射点处表面法线的夹角的余弦成正比

原理图

三、兰伯特光照模型的公式

公式: 漫反射的颜色 = 光源颜色 * 材质漫反射的颜色 * max(0,标准化后物体表面法向量 · 标准化后光源方向向量)

公式推导: 其中:

  1. 标准化后物体表面法线向量 · 标准化后光源方向的向量 得到的结果就是 cosθ
  2. max(0,cosθ)的目的是避免负数,对于模型背面部分,认为照不到光照,直接乘以 0,变为黑色 公式推导解释图

四、如何在 Shader 中公式中获取关键信息

1.光源颜色

1
Light.cginc 内置文件中的 _LightColor0

2.光源的方向

1
_WorldSpaceLightPos0 //光源在世界坐标下的位置

3.向量归一化

1
nomalize

4.取最大值的方法

1
max

5.点乘的方法

1
dot

6.兰伯特光照模型环境光变量(用于模拟环境光堆物体的影响,避免物体阴影部分完全黑暗)

1
UNITY_LIGHTMODEL_AMBIENT.rgb

7.将法线从模型空间转换到世界空间

1
UnityObjectToWorldNormal

五、逐顶点光照的实现

  1. 材质漫反射颜色属性声明
  2. 渲染标签 Tags 设置 将 LightMode 光照模式 设置为 ForWardBase 向前渲染(通常用于不透明物体的基本渲染)
  3. 引用内置文件 UnityCG.cginc、Lighting.cginc
  4. 结构体声明
  5. 基于基本公式实现逻辑
  6. 演示代码
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
Properties
{
//材质的漫反射颜色
_MainColor("MainColor",Color) = (1,1,1,1)
}
SubShader
{
//书写光照的 逻辑,一般都要设置光照模式
//设置光照模式 Forward这种模式 主要是用来处理 不透明物体的 关照渲染
Tags { "LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
//材质的漫反射颜色
fixed4 _MainColor;
//顶点着色器传递给片元着色器的内容
struct v2f
{
//裁剪空间下的顶点坐标信息
float4 pos:SV_POSITION;
//对应顶点的漫反射反射光照颜色
fixed3 color:COLOR;
};
//逐顶点光照,所以相关的逻辑 需要书写在顶点着色器回调函数中
v2f vert (appdata_base v)
{
v2f v2fData;
//将模型空间下的 顶点转换到裁剪空间下
v2fData.pos = UnityObjectToClipPos(v.vertex);
//公式:漫反射光照颜色 = 光源颜色 * 材质的漫反射颜色 * max(0,标准化后物体表面的法向量 · 标准化后的光源方向向量)
//光照颜色 _LightColor0
//模型表面的法线的向量 v.normal
//获取 相对于世界坐标下的 法线信息
float3 normal = UnityObjectToWorldNormal(v.normal);
//光源方向 _WorldSpaceLightPos0
//获取 光源方向的单位向量
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);

//参数全部获取 进行计算
fixed3 color = _LightColor0.rgb * _MainColor * max(0,dot(normal,lightDir));
//加上兰伯特光照模型的 环境光变量的目的是 希望阴影处不要全黑 不然 看起来有一些不自然
//目的是为了让表现 效果更接近于真实
v2fData.color = UNITY_LIGHTMODEL_AMBIENT.rgb + color;

return v2fData;
}

fixed4 frag (v2f i) : SV_Target
{
//只要 把计算好的兰伯特关照颜色 传递出去就好了
return fixed4(i.color.rgb,1);
}
ENDCG
}
}
  1. 实现效果 实现效果图

注意: 为了阴影不全黑,需要加上兰伯特环境光公共颜色公共变量

六、逐片元关照的实现

  1. 材质漫反射颜色属性声明
  2. 渲染标签 Tags 设置 将 LightMode 光照模式 设置为 ForWardBase 向前渲染(通常用于不透明物体的基本渲染)
  3. 引用内置文件 UnityCG.cginc、Lighting.cginc
  4. 结构体声明
  5. 基于基本公式实现逻辑
  6. 演示代码
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
Properties
{
_MainColor("MainColor",Color) = (1,1,1,1)
}
SubShader
{
Tags { "LightMode"="ForwardBase" }
Pass
{
CGPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "UnityCG.cginc"
#include "Lighting.cginc"
float3 _MainColor;
//顶点着色器返回出去的内容
struct v2f
{
//裁剪空间下的顶点位置
float4 pos:SV_POSITION;
//世界空间下的法线位置
float3 normal:NORMAL;
};
//注意 顶点着色器回调函数
//最主要的作用
//是 处理顶点、法线、切线的等坐标数据转换
v2f vert (appdata_base v)
{
v2f v2fData;
//转换 模型空间下的顶点 到 裁剪空间中
v2fData.pos =UnityObjectToClipPos(v.vertex);
//转换 模型空间下的法线 到 裁剪空间中
v2fData.normal = UnityObjectToWorldNormal(v.normal);
return v2fData;
}

fixed4 frag (v2f i) : SV_Target
{
//得到光源的单位向量
float3 lightDir = normalize(_WorldSpaceLightPos0.xyz);
fixed3 color = _MainColor.rgb * _LightColor0.rgb * max(0,dot(lightDir,i.normal));
//加上兰伯特光照模型的 环境光变量的目的是 希望阴影处不要全黑 不然 看起来有一些不自然
//目的是为了让表现 效果更接近于真实
color = UNITY_LIGHTMODEL_AMBIENT.rgb + color;
return fixed4(color.rgb,1);
}
ENDCG
}
}
  1. 实现效果 实现效果

评论