欢迎关注公众号:sumsmile /专注图形学
内容参考learnopengl、games101整理,加入了自己的理解和补充
理论
基于物理的着色,必须满足
- 基于微平面(Microfacet)的表面模型。
- 能量守恒。
- 应用基于物理的BRDF
微平面模型
达到微观尺度之后,任何平面用无数细小镜面来描述,粗糙度决定细小镜面的排列方向:
基于粗糙度来计算出众多微平面中,沿着某个向量h的比例,h为半程向量,即光照方向和观察方向的中间向量
能量守恒
光线照在物体表面,产生反射和折射
- 反射:镜面反射
- 折射:一部分能量被物体吸收,一部分又射出物体表面,形成漫反射
注意,光子根据波长的不同,被物体吸收的程度也不同,所以白色光经过折射后丢失了部分波长,产生了颜色光,一般是物体的颜色。
反射和折射的比例总和为1,保证了能量守恒
float kS = calculateSpecularComponent(...); // 反射/镜面 部分
float kD = 1.0 - ks; // 折射/漫反射 部分
渲染方程
渲染方程包含自发光(emit)和反射(reflect)两部分,反射分漫反射(折射)和镜面反射
反射率方程
- 辐射通量Φ: 单位瓦特,可以理解为功率,单位时间的能量。
处理光照时,简化了光的成分,按照RGB来度量,并没有考虑所有波长的光照
- 立体角(Solid Angle):用ω(或Ω)表示
BRDF-双向反射分布函数
全称双向反射分布函数(Bidirectional Reflective Distribution Function)
BRDF是PBR最核心的内容,甚至可以说BRDF定义了物体的材质,或者说物体表面对光的反射特性决定了物体的材质。
同样采用ωi和ωo作为输入参数的 Blinn-Phong光照模型也被认为是一个BRDF。然而由于Blinn-Phong模型并没有遵循能量守恒定律,因此它不被认为是基于物理的渲染
Cook-Torrance BRDF,最常用的BRDF模型,分折射(漫反射)和镜面反射两部分:
c表示表面颜色(回想一下漫反射表面纹理)。除以π是为了对漫反射光进行标准化,因为前面含有BRDF的积分方程是对π(半球)范围积分的。漫反射计算及推导
镜面反射方程
DFG表示三个函数,法线分布函数、菲涅尔方程、几何函数
- 法线分布函数(Normal Distribution Function)
微表面是凹凸不平的,这其中有多少比例的细小表面能将光线反射到观察方向。实际上对纹理采样,得到粗糙度值,根据粗糙度值转换成比例因子.
这个转换公式用Trowbridge-Reitz GGX,没有特殊的讲究,就是一种类似高斯曲线的数学实现,而且Disney整合了这些曲线,称为GTR(Generalized-Trowbridge-Reitz),TR-GGX是其中的一种
TR-GGX shader中的实现
float D_GGX_TR(vec3 N, vec3 H, float a)
{
float a2 = a*a;
float NdotH = max(dot(N, H), 0.0);
float NdotH2 = NdotH*NdotH;
float nom = a2;
float denom = (NdotH2 * (a2 - 1.0) + 1.0);
denom = PI * denom * denom;
return nom / denom;
}
- 几何函数(Geometry Function)
物体自身相互遮挡,也会造成一定亮度的损失,这个损失是多少?这就是几何函数要考虑的。
遮挡分两种:
- 和入射光方向有关:光线照射进来打到一个凸起的地方,和宏观反射方向不同
- 和观察方向有关:你恰好观察的方向上,被凸起挡住了,光线打到表面反弹后
G项的函数设计借鉴了GGX与Schlick-Beckmann,称为Schlick-GGX:
k也是基于粗糙度α的重映射,取决于我们要用的是针对直接光照还是针对IBL光照的几何函数:
考虑观察方向、光线方向,使用史密斯法(Smith’s method):
shader中的实现
float GeometrySchlickGGX(float NdotV, float k)
{
float nom = NdotV;
float denom = NdotV * (1.0 - k) + k;
return nom / denom;
}
float GeometrySmith(vec3 N, vec3 V, vec3 L, float k)
{
float NdotV = max(dot(N, V), 0.0);
float NdotL = max(dot(N, L), 0.0);
float ggx1 = GeometrySchlickGGX(NdotV, k);
float ggx2 = GeometrySchlickGGX(NdotL, k);
return ggx1 * ggx2;
}
- 菲涅尔方程(Fresnel Rquation)
表示光从不同的角度(from normal)打在表面上,反射率是不同的。
越靠近法线方向(垂直表面),反射越少,大部分都穿透、进入物体内,发生了折射
越偏离法线(和表面平行),反射越大
注意!导体(金属)的菲涅尔项不一样,即使是垂直照射金属,反射率也很高,可以认为是金属的密度比较高,光线难以穿透金属表面,光子撞击表面都被弹回去了吧
导数和绝缘体的差异,导致统一成一个F项有点难度。以Fresnel-Schlick近似法求得近似解:
非常聪明的提出,加上一个基础反射率F0,金属的基础反射率高,整个函数曲线就上移了,上移的多就是金属,上移的少就是绝缘体
实际工程上的材质非常复杂,用金属度来控制实现导体和绝缘体的中间态,可以得到更丰富的材质效果。
金属度metalness的作用,就是控制最终的F0参数(0.04~color)
vec3 F0 = vec3(0.04);
F0 = mix(F0, surfaceColor.rgb, metalness);
最终的Fresnel Schlick的函数实现:
vec3 fresnelSchlick(float cosTheta, vec3 F0)
{
return F0 + (1.0 - F0) * pow(1.0 - cosTheta, 5.0);
}
至此,Cook-Torrance反射率方程系数介绍完毕(除了DFG下面分母项的归一化)
注意,对cos项,可以用Lambert’s Cosine Law来解释。不能按照phong光照理解。
编写(使用)PBR材质
材质一般由美术或者TA编写,开发需要理解每个材质的意义。
对着材质,可以更深入的理解BRDF方程的函数
- ALBEDO:反照率,用于漫反射项color项-物体的表面颜色
- NORMAL:涉及到cos计算的地方都用的上
- METALLIC:金属度,控制菲涅尔项的函数中的基础反射率F0
- ROUGHNESS:粗糙度,控制法线分布和几何项函数
- AO:环境光遮蔽,在光照计算结束后,加强明暗效果