五、渲染:光和材质的数学魔法

5.1 渲染方程及挑战

5.1.1 渲染方程

image-20230710111512770 \[ L_o(x,\omega_o)=L_e(x,\omega_o)+\int_{H^2}f_r(x,\omega_o,\omega_i)L_i(x,\omega_i)\cos\theta_id\omega_i \] radiance:辐射度;光照到物体上,物体反射出去的能量

irradiance:入射的能量

\(L_o(x,\omega_o)\):观察点为\(x\),观察方向为\(\omega_o\),观察到的radiance即为\(L_o(x,\omega_o)\)

  1. \(L_e(x,\omega_o)\):观察点\(x\),在\(\omega_o\)方向的自发光
  2. \(\int_{H^2}f_r(x,\omega_o,\omega_i)L_i(x,\omega_i)\cos\theta_id\omega_i\):观察点\(x\)接受所有方向射来的光,反射到\(\omega_o\)方向的光
    1. \(f_r(x,\omega_o,\omega_i)\):观察点\(x\),入射方向为\(\omega_i\),反射方向为\(\omega_o\),反射光能量占入射光能量的比例
    2. \(L_i(x,\omega_i)\):观察点\(x\),接受到的入射方向为\(\omega_i\)的光的能量
    3. \(\cos\theta_i\):光线与平面的夹角

5.1.2 挑战1:如何得到irradiance

对光源的可见性(阴影)

image-20230710113016584

光源本身的复杂性

image-20230710113045374

5.1.4 挑战2:如何快速地在球面上进行积分

如何计算光与表面的作用,即材质的影响

image-20230710113110253

5.1.5 挑战3:所有的物体都可能是光源

image-20230710113218876

5.2 基础光照解决方案

5.2.1 简化光源:Main Light + Ambient Light + Environment Map

image-20230710114440708

  1. 简化光源的种类:主光dominant light

    1. 要么是点光源,要么是方向光源,要么是锥形光源
  2. 简化光场的表示:环境光ambient light

    1. 将四面八方的光,用平均值代替
  3. 环境光贴图:Environment Map

    1. 贴图是一个立方体
    2. 根据顶点的法线方向,计算观察到的颜色
    void main(){
    vec3 N = normalize(normal);
    vec3 V = normalize(camera_position - world_position);
    vec3 R = reflect(V,N);
    FragColor = texture(cube_texture, R);
    }

5.2.2 简化积分:Blinn-Phong 材质

image-20230710115348670

将材质分为三类:\(L=L_a+L_d+L_s\)

  1. 环境光项Ambient\(L_a=k_aI_a\)
  2. 漫反射项Diffuse\(L_d=k_d\frac{I}{r^2}\max(0,\textbf{n}·\textbf{l})\)
  3. 镜面反射项Specular\(L_s=k_s\frac{I}{r^2}\max(0,\textbf{n}·\textbf{h})^p\)

image-20230710115748614

5.2.3 阴影

目标问题:场景中看到的每个点,对于光源是否可见

image-20230710115844674

解决方法:shadow map

  1. 从光的角度渲染一张深度图
  2. 观察时,每个点投影回光源的视角,根据深度判断是否可见
// 将当前点投影到shadow map
vec4 proj_pos = shadow_viewproj * pos;
// 从 homogeneous 转化为 clip space
vec2 shadow_uv = proj_pos.xy / proj_pos.w;
// 从 clip space 转化为 uv space
shadow_uv = shadow_ucv * 0.5 + vec2(0.5);
// 获取当前点的深度 [-1, +1]
float real_depth = proj_pos.z / proj_pos.w;
// 标准化到[0, 1]
real_depth = real_depth * 0.5 + 0.5;
// 从深度缓冲中读取深度
float shadow_depth = texture(shadowmap, shadow_uv).x;
// 计算是否在阴影中
float shadow_factor = 1.0;
if(shadow_depth < real_depth)
shadow_factor = 0.0;

image-20230710120120070

image-20230710120752077

5.3 基于预计算的全局光照(空间换时间)

假设场景中90%的物体是不动的,每个场景太阳的角度是锁死的,那么就可以通过预计算,简化计算

5.3.1 全局光照:直接光照+间接光照

image-20230710121655099

5.3.2 如何表示间接光照\(L_i(x,\vec{\omega_i})\)

5.3.2.1 傅里叶变换:

image-20230710122010062

5.3.2.2 卷积定理:

image-20230710122240919

5.3.2.3 球谐函数 Spherical Harmonics:

球谐函数本质上是一组基于球坐标系\((\theta,\phi)\)的基函数,基函数的计算公式为:\(Y_{lm}(\theta,\phi) &= N_{lm}P_{lm}(\cos\theta)e^{Im\phi} \\\)

  1. 正交:任意两个基函数卷积为0
  2. 二阶导永远为0:函数永远是光滑的
$$ \[\begin{aligned} Y_{0, 0}(\theta,\phi) &= 1 \\ \\ Y_{1,-1}(\theta,\phi) &= y = \sin\theta\sin\phi \\ Y_{1, 0}(\theta,\phi) &= z = \cos\theta \\ Y_{1, 1}(\theta,\phi) &= x = \sin\theta\cos\phi \\ \\ Y_{2,-2}(\theta,\phi) &= xy = \sin^2\theta\sin\phi\cos\phi \\ Y_{2,-1}(\theta,\phi) &= yz = \sin\theta\cos\theta\sin\phi \\ Y_{2, 0}(\theta,\phi) &= 3z^2-1 = 3\cos^2\theta-1 \\ Y_{2, 1}(\theta,\phi) &= zx = \sin\theta\cos\theta\cos\phi \\ Y_{2, 2}(\theta,\phi) &= x^2-y^2 = \sin^2\theta(\cos^2\phi-\sin^2\phi) \\ \end{aligned}\]

$$

image-20230710122403478

image-20230710142506684

image-20230710142724812

由于环境光通常是低频的,因此只需要用二阶函数即可近似表达

image-20230710143143488

image-20230710143341600

5.3.3 光照贴图 Lightmap

优点:

  1. runtime的时候效率很高
  2. 离线渲染,可以表示很多细节

缺点:

  1. 预计算时间非常长
  2. 只能处理静态物体、静态光
  3. 占用存储空间,通常在几十MB~一百MB

image-20230710143550991

image-20230710143647404

image-20230710144115534

image-20230710144140615

image-20230710144204813

5.3.4 光照探针 Light Probe

空间体素化

  1. 在场景中添加若干探针,针对每个探针计算全局光照
  2. 当需要某个点的全局光照时,对周围的探针进行插值

优点:

  1. runtime时效率高
  2. 可以处理静态物体和动态物体,可以动态更新
  3. 处理了diffuse、specular的渲染

缺点:

  1. 无法实现类似lightmap的细节
  2. SH light probe需要预计算

image-20230710144730913

image-20230710145020056

image-20230710145217849

5.4 基于物理的材质 Physical-Based Material PBR

5.4.1 微平面理论

  1. 假设任意面都由很多小的微平面组成,每个微平面都是镜面反射
  2. 如果微平面的法线方向集中,则是镜面反射;如果发散,则是漫反射

image-20230710145814669

5.4.2 基于微平面的BRDF模型

image-20230710145923736

image-20230710150553439

image-20230710150927605

image-20230710151100153

5.4.3 Disney Principled BRDF

五个原则

  1. 参数的含义要足够直观
  2. 参数要尽可能少
  3. 参数的范围要尽可能在[0,1]之间
  4. 参数的范围也可以超过[0,1],但是这必须是有意义的
  5. 参数的组合不会产生非常诡异的结果,每个参数的组合都是有意义的

image-20230710152058624

5.4.4 PBR Specular Glossiness

image-20230710152319575

image-20230710152356799

5.4.5 PBR Metallic Roughness

在SG模型的基础上进行封装,不是直接给出菲涅尔项的参数,而是通过metallic属性,对base_color进行插值

image-20230710152711140

image-20230710152730662

5.5 基于图像的光照:Image-Based Lighting(IBL)

基础想法:对环境光照做一些预处理,从而快速计算环境光照与材质的卷积运算

image-20230710153710856

image-20230710153723495

image-20230710153902074

image-20230710153919760

image-20230710153937924

5.6 经典阴影方法

5.6.1 Cascade Shadow

  1. 将环境分为不同层,离玩家越近,shadow map的精度越高

image-20230710154217910

image-20230710154229321

image-20230710154423464

5.6.2 PCF

image-20230710154627569

5.6.3 PCSS

image-20230710154641878

5.6.4 Variance Soft Shadow Map(VSSM)

image-20230710154731623

5.7 总结

  1. 光:Lightmap + Lightporbe
  2. 材质:PBR + IBL
  3. 阴影:Cascade shadow + VSSM