- BxDF 基本概念
- LambertianDiffuseBRDF(漫反射)
- PhongSpecularBRDF(高光反射)
- MirrorSpecularBRDF(镜面反射)
- TransmissiveBRDF(透射)
- BSDF 混合机制
BxDF(Bidirectional Reflectance Distribution Function,双向反射分布函数)描述了光线如何在表面上反射的物理规律。
物理意义:给定入射方向 wo 和出射方向 wi,BxDF告诉我们:
- 有多少光线从
wo方向反射到wi方向 - 反射光线的颜色和强度
class BxDF {
// 1. PDF:概率密度函数
virtual float pdf(const vec3& wo, const vec3& wi, const vec3& n) const = 0;
// 2. eval:BRDF值(反射率)
virtual vec3 eval(const vec3& wo, const vec3& wi, const vec3& n) const = 0;
// 3. sampleDir:采样反射方向
virtual vec3 sampleDir(const vec3& wo, const vec3& n) const = 0;
};蒙特卡洛近似:
其中:
-
$L_o$ :出射辐射亮度 -
$L_e$ :自发光辐射亮度 -
$f_r$ :BRDF(双向反射分布函数) -
$L_i$ :入射辐射亮度 -
$p(\omega_i)$ :采样方向的概率密度函数(PDF)
漫反射遵循兰伯特余弦定律:反射光强度与入射角的余弦成正比。
float pdf(const vec3& wo, const vec3& wi, const vec3& n) const {
if (dot(n, wi) < 0.f) return 0.f; // 背面,无效
return 1.0f / M_PI; // 投影面积空间的均匀分布
}为什么是 1/π?
- 半球在平面上的投影面积是
π - 在投影面积空间中均匀采样
- 与BRDF的归一化一致
能量守恒验证:
vec3 eval(const vec3& wo, const vec3& wi, const vec3& n) const {
return radiance / M_PI;
}兰伯特BRDF公式:
其中:
-
$\rho$ 是反照率(albedo),即材质颜色 -
$\pi$ 是归一化因子
能量守恒验证:
计算积分:
所以:
vec3 sampleDir(const vec3& wo, const vec3& n) const {
// 在单位圆盘上均匀采样
vec2 offset = 2.f * vec2(genRandomFloat(), genRandomFloat()) - vec2(1, 1);
// 将正方形映射到圆盘(平方根映射)
vec2 disk;
if (abs(offset.x) > abs(offset.y)) {
float r = offset.x;
float theta = M_PI / 4.f * (offset.y / offset.x);
disk = r * vec2(cos(theta), sin(theta));
} else {
float r = offset.y;
float theta = M_PI / 2.f - M_PI / 4.f * (offset.x / offset.y);
disk = r * vec2(cos(theta), sin(theta));
}
// 投影到半球
float z = sqrt(1 - disk.x * disk.x - disk.y * disk.y);
vec3 localDir = vec3(disk.x, disk.y, z);
// 转换到世界坐标系
return toWorld(localDir, n);
}采样步骤:
- 在单位圆盘上均匀采样:使用平方根映射避免中心聚集
- 投影到半球:$z = \sqrt{1 - x^2 - y^2}$
- 转换到世界坐标:使用切线空间变换
为什么这样采样?
- 确保在半球上均匀分布
- PDF =
1/π - 避免拒绝采样,效率高
| 特性 | 值 |
|------|-----|
| PDF | 1/π |
| BRDF | radiance / π |
| 采样 | 圆盘投影到半球 |
| 能量守恒 | 完全抵消 |
| 应用场景 | 纸张、墙壁、布料等粗糙表面 |
Phong模型是一种经验模型,用于模拟光滑表面的镜面反射。它基于半程向量(halfway vector)来计算高光强度。
核心思想:
- 当观察方向与完美反射方向接近时,高光强度最大
- 使用指数参数控制高光的集中程度
半程向量是入射方向和出射方向的角平分线:
几何意义:
- 当
$\omega_i$ 是完美反射方向时,$\mathbf{h}$ 与法向量$\mathbf{n}$ 重合 -
$\mathbf{n} \cdot \mathbf{h}$ 越接近1,高光越强
float pdf(const vec3& wo, const vec3& wi, const vec3& n) const {
if (dot(n, wi) < 0.f) return 0.f; // 背面,无效
const vec3 h = normalize(wi - wo); // 半程向量
const float nh = dot(n, h);
return (alpha + 1.0f) * pow(nh, alpha) / (2.0f * M_PI);
}Phong分布公式:
参数说明:
-
$\alpha$ :高光指数(shininess)-
$\alpha = 1$ :宽大、柔和的高光 -
$\alpha = 100$ :尖锐、明亮的高光
-
-
$(\mathbf{n} \cdot \mathbf{h})^\alpha$ :高光强度随角度衰减 -
$\frac{\alpha + 1}{2\pi}$ :归一化因子
能量守恒验证:
vec3 eval(const vec3& wo, const vec3& wi, const vec3& n) const {
const vec3 h = normalize(wi - wo);
const float nh = dot(h, n);
const float spec = pow(nh, alpha);
const float normFactor = (alpha + 2.0f) / (2.0f * M_PI);
return radiance * spec * normFactor;
}Phong BRDF公式:
参数说明:
-
$k_s$ :镜面反射系数(材质颜色) -
$(\mathbf{n} \cdot \mathbf{h})^\alpha$ :高光强度 -
$\frac{\alpha + 2}{2\pi}$ :归一化因子
为什么归一化因子是
PDF归一化:$\frac{\alpha + 1}{2\pi}$(半程向量空间) BRDF归一化:$\frac{\alpha + 2}{2\pi}$(入射方向空间)
两个空间的雅可比行列式不同,所以归一化因子也不同。
vec3 sampleDir(const vec3& wo, const vec3& n) const {
const float u = genRandomFloat();
const float v = genRandomFloat();
// 计算球坐标
const float phi = 2.0f * M_PI * u;
const float cosTheta = pow(v, 1.0f / (alpha + 1.0f));
const float sinTheta = sqrt(1.0f - cosTheta * cosTheta);
// 转换为笛卡尔坐标
auto h = vec3(sinTheta * cos(phi), sinTheta * sin(phi), cosTheta);
h = toWorld(h, n);
// 根据反射定律计算出射方向
const vec3 wi = wo - 2.0f * dot(wo, h) * h;
return normalize(wi);
}采样步骤:
-
在半程向量空间中采样:
-
$\phi = 2\pi u$ (方位角,均匀分布) -
$\cos\theta = v^{1/(\alpha+1)}$ (极角,Phong分布)
-
-
转换到世界坐标系:
h = toWorld(h, n);
-
根据反射定律计算出射方向:
wi = wo - 2(wo·h)h
为什么
这是逆变换采样的结果。
Phong分布的累积分布函数(CDF):
逆变换:
由于
| alpha 值 | 高光特性 | 应用场景 |
|---|---|---|
| 1-10 | 宽大、柔和的高光 | 塑料、橡胶 |
| 10-50 | 中等高光 | 油漆、皮革 |
| 50-200 | 尖锐、明亮的高光 | 金属、玻璃 |
镜面反射是理想化的镜面反射,光线只在一个方向上反射,即完美反射方向。
核心特点:
- 只有一个反射方向(完美反射定律)
- 反射角等于入射角
- 包含Fresnel效应(角度相关的反射率)
float pdf(const vec3& wo, const vec3& wi, const vec3& n) const {
if (dot(wi, n) < 0.f) return 0.f; // 背面,无效
return 1.0f; // Delta分布
}Delta分布:
镜面反射的PDF是Delta分布(狄拉克δ函数),因为只有一个反射方向。
其中
为什么是1.0?
- Delta分布在完美反射方向上可以是任何值
- 选择1.0是为了简化计算
- 避免复杂的Delta函数处理
蒙特卡洛积分中的处理:
在代码中:
// 采样方向就是完美反射方向
vec3 wi = sampleDir(wo, n);
// PDF = 1.0
float pdf = 1.0;
// 蒙特卡洛估计
vec3 contribution = eval * Li * cosTheta / pdf;
= eval * Li * cosTheta / 1.0
= eval * Li * cosThetavec3 eval(const vec3& wo, const vec3& wi, const vec3& n) const {
const float cosTheta = dot(wi, n);
const vec3 fresnelSchlick = radiance + (vec3(1.0f) - radiance) * pow(1.0f - cosTheta, 5.0f);
return fresnelSchlick / cosTheta;
}镜面反射BRDF公式:
其中:
-
$F(\omega_o)$ 是Fresnel反射率 -
$\cos(\theta_o) = \mathbf{n} \cdot \omega_o$ 是出射角的余弦
vec3 fresnelSchlick = radiance + (vec3(1.0f) - radiance) * pow(1.0f - cosTheta, 5.0f);数学表达式:
参数说明:
-
$F_0 = \text{radiance}$ :垂直入射时的反射率(材质颜色) -
$\cos\theta = \mathbf{n} \cdot \omega_o$ :出射角的余弦 -
$5$ :Schlick近似的指数
为什么是5次方?
- 这是Schlick提出的经验公式
- 近似Fresnel方程的精确解
- 计算效率高,效果好
Fresnel效应描述了反射率随角度变化的现象:
| 入射角 | 反射率 | 现象 |
|---|---|---|
| 0°(垂直) | 材质固有反射率 | |
| 30° | 反射率增加 | |
| 60° | 反射率显著增加 | |
| 90°(掠射) | 1.0 | 几乎完全反射 |
实际例子:
- 水面:垂直看下去透明,掠射看像镜子
- 玻璃:垂直看透明,掠射看反射
- 金属:所有角度都有较强反射
vec3 sampleDir(const vec3& wo, const vec3& n) const {
return normalize(wo - 2.0f * dot(wo, n) * n);
}完美反射定律的向量形式:
$$ \omega_i = \omega_o - 2(\omega_o \cdot \mathbf{n})\mathbf{n}