Lambert光照模型
Lambert光照模型简介
Lambert光照模型是一种简单的漫反射光照模型,由Johann Heinrich Lambert在18世纪提出。它描述了理想漫反射表面的光照行为,即光线在所有方向上均匀散射。
基本原理
Lambert定律指出:
- 表面反射的光强与入射光方向和表面法线夹角的余弦成正比
- 反射光在所有观察方向上是相同的(各向同性)
数学表示
Lambert漫反射光照公式:
\[I_d = I_p \cdot k_d \cdot (\mathbf{L} \cdot \mathbf{N})\]其中:
- $I_d$: 漫反射光强
- $I_p$: 入射光强
- $k_d$: 漫反射系数(表面颜色)
- $\mathbf{L}$: 指向光源的单位向量
- $\mathbf{N}$: 表面法线单位向量
实现方法
在着色器中实现Lambert光照的基本步骤:
- 计算表面法线$\mathbf{N}$
- 计算指向光源的向量$\mathbf{L}$并归一化
- 计算点积$\mathbf{L} \cdot \mathbf{N}$
- 应用漫反射公式计算最终颜色
// GLSL示例代码
vec3 calculateLambert(vec3 normal, vec3 lightDir, vec3 lightColor, vec3 surfaceColor) {
float diff = max(dot(normal, lightDir), 0.0);
return lightColor * surfaceColor * diff;
}
特点与应用
- 简单高效,适合实时渲染
- 适用于粗糙、无光泽的表面(如纸张、布料等)
- 常与Phong或Blinn-Phong镜面反射模型结合使用
- 是许多更复杂光照模型的基础
局限性
- 无法表现镜面高光
- 无法表现菲涅尔效应
- 无法表现次表面散射效果
- 在掠射角度($\mathbf{L} \cdot \mathbf{N}$接近0)时表现不自然
扩展与改进
-
半Lambert模型:通过重新映射点积结果改善掠射角度表现 \(I_{half} = 0.5 \cdot (\mathbf{L} \cdot \mathbf{N}) + 0.5\)
-
环境光项:添加环境光项$I_a$来模拟间接光照 \(I_{total} = I_a + I_d\)
-
距离衰减:考虑光源距离的影响 \(I_d = \frac{I_p}{d^2} \cdot k_d \cdot (\mathbf{L} \cdot \mathbf{N})\)
光照衰减实现细节
- 衰减公式:
实际应用中常使用更稳定的衰减公式:
\(\text{attenuation} = \frac{1}{k_c + k_l \cdot d + k_q \cdot d^2}\)
其中:
- $k_c$: 常数项(通常1.0)
- $k_l$: 线性项
- $k_q$: 二次项
- 不同类型光源的衰减:
- 点光源:使用完整的二次衰减
- 聚光灯:额外考虑角度衰减
- 平行光:通常不考虑距离衰减
- GLSL实现: ```glsl // 点光源衰减计算 float calculateAttenuation(float distance, float constant, float linear, float quadratic) { return 1.0 / (constant + linear * distance + quadratic * (distance * distance)); }
// 在光照计算中应用 float atten = calculateAttenuation(length(lightPos - fragPos), 1.0, 0.09, 0.032); vec3 result = (ambient + diffuse + specular) * atten;
4. **衰减系数调整**:
- 近距离光源:增大二次项系数
- 远距离光源:增大线性项系数
- 常用预设:
- 近距离: (1.0, 0.09, 0.032)
- 中距离:(1.0, 0.07, 0.017)
- 远距离:(1.0, 0.04, 0.007)
## Phong光照模型
Phong模型在Lambert漫反射基础上增加了镜面反射项,由Bui Tuong Phong于1975年提出。
### 数学表示
$$
I = I_a + I_d + I_s = k_a + k_d(\mathbf{L} \cdot \mathbf{N}) + k_s(\mathbf{R} \cdot \mathbf{V})^n
$$
其中:
- $\mathbf{R}$: 反射光向量
- $\mathbf{V}$: 视线向量
- $n$: 镜面反射指数(控制高光锐利度)
- $k_s$: 镜面反射系数
### 实现方法
```glsl
vec3 calculatePhong(vec3 normal, vec3 lightDir, vec3 viewDir,
vec3 lightColor, vec3 surfaceColor, float shininess) {
// 漫反射项(Lambert)
float diff = max(dot(normal, lightDir), 0.0);
// 镜面反射项
vec3 reflectDir = reflect(-lightDir, normal);
float spec = pow(max(dot(viewDir, reflectDir), 0.0), shininess);
return lightColor * (surfaceColor * diff + spec);
}
局限性
- 计算反射向量$\mathbf{R}$开销较大
- 当视线与反射方向夹角大于90°时,镜面项会突然消失
- 高光形状不够自然
Blinn-Phong光照模型
Jim Blinn对Phong模型的改进,使用半角向量替代反射向量计算。
改进点
- 用半角向量$\mathbf{H}$代替反射向量$\mathbf{R}$: \(\mathbf{H} = \frac{\mathbf{L} + \mathbf{V}}{||\mathbf{L} + \mathbf{V}||}\)
- 镜面项公式变为: \(I_s = k_s(\mathbf{N} \cdot \mathbf{H})^n\)
优势
- 计算效率更高:避免了反射向量的计算
- 视觉效果更平滑:没有Phong模型的突然消失问题
- 高光更自然:特别是对于低多边形模型
- 物理更准确:更接近微表面理论
实现方法
vec3 calculateBlinnPhong(vec3 normal, vec3 lightDir, vec3 viewDir,
vec3 lightColor, vec3 surfaceColor, float shininess) {
// 漫反射项(Lambert)
float diff = max(dot(normal, lightDir), 0.0);
// 镜面反射项(Blinn-Phong)
vec3 halfwayDir = normalize(lightDir + viewDir);
float spec = pow(max(dot(normal, halfwayDir), 0.0), shininess);
return lightColor * (surfaceColor * diff + spec);
}
颜色空间:sRGB与线性空间
sRGB颜色空间
- 定义:sRGB是标准的RGB颜色空间,由HP和微软于1996年制定
- 特性:
- 非线性编码,近似gamma=2.2的曲线
- 直接对应显示器的响应曲线
- 广泛用于图像存储和显示设备
- 数学表示: 对于$C_{linear} \leq 0.0031308$: \(C_{srgb} = 12.92 \times C_{linear}\) 否则: \(C_{srgb} = 1.055 \times C_{linear}^{1/2.4} - 0.055\)
线性颜色空间
- 定义:颜色值与物理光强呈线性关系的空间
- 特性:
- 颜色值直接对应实际光强度
- 适合进行光照计算和物理模拟
- 需要gamma校正才能在标准显示器上正确显示
- 重要性:
- 光照计算必须在线性空间进行才能得到正确结果
- 纹理需要在线性空间采样
- 混合/过滤操作需要在线性空间执行
空间转换
- sRGB到线性空间:
- 通过逆sRGB转换或gamma≈1/2.2的幂运算(去gamma校正)
- 纹理采样时可使用硬件sRGB扩展自动转换
- 数学表示:$C_{linear} = C_{srgb}^{2.2}$
- 线性空间到sRGB:
- 通过sRGB转换或gamma≈2.2的幂运算(gamma校正)
- 通常在最终输出前应用
- 数学表示:$C_{srgb} = C_{linear}^{1/2.2}$
注意:
- 精确的sRGB标准使用分段线性+幂函数转换
- 实际应用中常用近似gamma=2.2的幂运算简化计算
Gamma校正
Gamma校正是连接线性空间和sRGB空间的桥梁,用于正确处理颜色值的非线性显示特性。
基本概念
- CRT显示特性:传统显示器对输入电压的响应呈非线性关系(约V^2.2)
- 人眼感知:人眼对暗部变化更敏感,与显示器的非线性响应相匹配
- 线性工作流:在渲染计算中使用线性颜色空间,最后通过gamma校正转换到显示空间
数学原理
Gamma校正公式:
\[C_{out} = C_{in}^{1/\gamma}\]其中:
- $C_{in}$:线性空间颜色值
- $C_{out}$:显示空间颜色值
- $\gamma$:通常取值2.2
逆Gamma校正(线性化):
\[C_{linear} = C_{display}^{\gamma}\]实现方法
- 手动Gamma校正:
// 在片段着色器最后添加 fragColor.rgb = pow(fragColor.rgb, vec3(1.0/2.2)); - sRGB纹理:
// 使用GL_SRGB8_ALPHA8格式的纹理 // 硬件会自动在采样时进行线性化 - 帧缓冲区设置:
// 使用sRGB帧缓冲区 glEnable(GL_FRAMEBUFFER_SRGB);
现代应用
- HDR渲染:gamma校正与色调映射结合使用
- PBR工作流:必须使用线性空间和gamma校正
- 移动设备:虽然现代屏幕更线性,但仍需考虑gamma校正
注意事项
- 纹理资源应根据用途选择正确的颜色空间
- 光照计算必须在线性空间进行
- 最后输出前应用gamma校正
- UI元素通常不需要gamma校正
模型比较与选择建议
| 特性 | Lambert | Phong | Blinn-Phong |
|---|---|---|---|
| 计算开销 | 最低 | 中等 | 中等 |
| 视觉效果 | 平坦 | 锐利高光 | 柔和高光 |
| 物理准确性 | 低 | 中等 | 较高 |
| 适用场景 | 粗糙表面 | 需要精确高光 | 大多数情况 |
实际应用建议:
- 优先使用Blinn-Phong模型
- 对性能极度敏感的场景可考虑Lambert
- 需要特定艺术风格时可考虑Phong