GAMES202.闫令琪.03.实时阴影(1)

  • https://www.bilibili.com/video/BV1YK4y1T7yY

Real-Time Shadows

  • 实时阴影

Shadow Mapping

  • SM
  • 经典的两趟算法
    • 2-pass

算法步骤

  • 第一遍渲染,转换到以光源为视点的观察坐标系,记录下深度图
  • 第二遍正常渲染,对每个点,转换到以光源为视点的观察坐标系中判断可见性
    • 判断可见性的时候深度一致即可,可以使用原来的线性距离或者投影之后的 z 值

评价

  • 图像空间的算法

优点

  • 绘制阴影的时候,不需要知道场景的几何信息

缺点

  • artifacts
    • self occlusion 自遮挡
    • aliasing 走样

自遮挡问题

  • self occlusion

  • 地板上的平面出现了自遮挡现象
  • 由于数值精度造成的(分辨率)
    • shadow map 精度有限,投射出去就是图中的红色小片
    • 例如蓝色的视线应该是能看到红色的部分的,但是由于精度问题,shadow map 中记录的是橙色的值,导致我们判断红色部分不可见,从而产生自遮挡现象

  • 如果光线和物体表面垂直,此时没有这个问题
    • 光线和法线重合
  • 如果光线和物体表面成一个很小的角度时,自遮挡问题很严重
    • 光线和法线夹角很大
    • grazing angle

解决方案

  • 增加一个 bias
  • 中间的黄色那段我们不算
    • 也就是说我们对计算得到的深度减去一个 bias

  • 一些技巧:动态的 bias
    • 当光线和物体表面法线夹角比较大时,bias 也需要比较大
    • 当光线和物体表面法线夹角比较小时,bias 比较小即可

引入 bias 带来的问题

  • detached shadow
    • 不接触的阴影
  • 鞋子部分的阴影消失了

  • 工业界 100% 解决这个问题的方法现在还没有
    • 解决方法还是通过找一个合适的 bias,让自遮挡和detached shadow 都不出现
    • 最简单的方式
  • 学术界的方法(工业界用的人少)
    • second-depth shadow mapping

second-depth shadow mapping

  • 不使用 bias
  • 记录最小深度的同时,还记录第二小的深度
  • 实际比较的时候使用这两个深度的平均值
    • 能够解决之前的问题

  • 这个方法的问题
    • 每个物体都得是一个几何实体
      • 不能是一张平面,如果是一张纸的话,得描述成一个很扁的长方体
    • 实现这个算法很困难,复杂度没有变化,但是无法并行
  • 一些 trick
    • 如果是最外层的地板,在做第一趟渲染深度图的时候不渲染地板
  • 实时渲染不相信复杂度,只相信实时运行的速度
    • RTR does not trust in COMPLEXITY
    • 常数也很重要

走样问题

  • 阴影分辨率的问题
  • 高端的做法
    • 给不同位置不同的分辨率
  • 软影

SM 的数学原理

常用不等式

  • Schwarz Inequality
    • 施瓦茨不等式

\[ \left[\int_a^bf(x)g(x)dx\right]^2\le\int_a^bf^2(x)dx\cdot \int_a^bg^2(x)dx \]

  • Minkowski Inequality
    • 明可夫斯基不等式

\[ \left[\int_a^b\left[f(x)+g(x)\right]^2dx\right]^{\frac{1}{2}}\le\left[\int_a^bf^2(x)dx\right]^{\frac{1}{2}}\cdot \left[\int_a^bg^2(x)dx\right]^{\frac{1}{2}} \]

  • 实时渲染中我们关心约等,而不是不等
    • 在近似条件下约等

RTR 中的一个常用近似

\[ {\color{red}\int_\Omega f(x)g(x)dx\approx \dfrac{\int_\Omega f(x)dx}{\int_\Omega dx}\cdot {\int_\Omega g(x)dx}} \]

  • 分母的部分表示归一化常数
    • 例如 \(f(x)=2\)
  • 什么时候近似正确呢?(满足一个条件即可)
    • small support
      • \(g(x)\) 的积分域很小的时候
    • smooth integrand
      • \(g(x)\) 在积分域内变化不大
      • 图形学学术界中的 smooth:min、max 差别不大

应用于渲染方程

  • 忽略自发光

\[ L_o(p,\omega_o)= \int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)V(p,\omega_i)\cos\theta_id\omega_i \]

  • 利用上面的近似

\[ L_o(p,\omega_o)\approx \dfrac{\int_{\Omega^+}V(p,\omega_i)d\omega_i}{\int_{\Omega^+}d\omega_i}\cdot\int_{\Omega^+}L_i(p,\omega_i)f_r(p,\omega_i,\omega_o)\cos\theta_id\omega_i \]

  • 这样子的近似把可见性从 shading 部分抽离出来了
    • 我们做 shadow mapping 方法的原理

shadow mapping 的原理

  • 什么时候是准确的?
  • small support
    • 积分范围只有一个点 \(\to\) 点光源、方向光源
    • 做硬阴影的数学基础
  • smooth integrand
    • diffuse bsdf / constant radiance area lighting
    • diffuse BSDF + 面光源
    • 解释 shadow mapping 算法不适合的场景
      • 环境光照(可以理解为超级大的面光源)+ glossy BRDF

PCSS

  • Percentage Closer Soft Shadows
  • 生成软阴影的一种算法

软阴影

  • 生活中的现象:太阳
    • 半影

PCF

  • Percentage Closer Filtering
  • 这个技术是用于于抗锯齿的,而不是用于生成软阴影的(PCSS 是用于生成软阴影的)
  • Filtering the results of shadow comparisons
    • 先采样,后做平均(filter)
  • 为什么不能先对 SM 做 filter,后采样?
    • Texture filtering just averages color components, i.e. you’ll get blurred shadow map first
    • Averaging depth values, then comparing, you still get a binary visibility
    • 这样的操作最后得到的结果还是非 0 即 1 的,没有意义
  • 原来判断一个点的可见性是直接做一次判断
    • shading point 和在深度图中对应位置记录的深度值作比较
  • PCF 的想法是对其周围的几个点都做阴影判断,最后把得到的结果平均起来
    • shading point 和在深度图中对应位置周围的几个点的深度作比较
    • 最后得到的可见性是一个 0 - 1 之间的值
    • 可以加权

  • PCF 的结果

  • 时间开销:变成原来的 k 倍(k 为核的大小)
  • filter size
    • small:sharper
    • large:softer
    • 决定分辨率,核越大,量化越细
  • 核的大小应该怎么确定?
  • 核的大小决定了阴影的软硬程度,根据阴影的软硬需求动态调整核的大小

PCSS

  • PCF 的思想,动态调整核的大小
  • 什么地方需要硬阴影,什么地方需要硬阴影?
  • 遮挡物和阴影的距离
    • 距离越大,阴影越软
    • 距离越小,阴影越硬

  • 根据上面的启发,定义一个距离函数,通过这个距离函数计算核的大小
    • Filter size <-> blocker distance
    • More accurately, relative average projected blocker depth!

  • 根据相似三角形
    • penumbra:半影

\[ w_{penumbra}=\dfrac{d_{Receiver}-d_{Blocker}}{d_{Blocker}}\cdot w_{Light} \]

  • 这个式子也符合我们生活中的观察
    • Blocker 离 Receiver 越近,那么阴影越硬(\(w_{penumbra}\) 越小)
  • \(d_{Blocker}\):average blocker depth
    • blocker 可能并不是一个点,有一定的范围,计算他们的平均值
    • 看 SM 中对应位置有多少个点能挡住 shading point,求他们的平均值
  • 面光源本身是没法生成一个 SM 的,我们模拟面光源的软阴影,用一个点光源代替它生成一个 SM

PCSS 流程

  • Step 1: Blocker search
    • getting the average blocker depth in a certain region
    • 将这些点在 SM 中的深度值和我们计算出来的深度值作比较
      • 感觉这个时候还是当作点光源操作
    • 考虑的是平均的 blocker 的深度,如果不是 blocker 则不管这个像素
  • Step 2: Penumbra estimation
    • use the average blocker depth to determine filter size
  • Step 3: Percentage Closer Filtering

问题

  • 怎么决定第一步搜索 blocker 的范围
  • 可以设置为一个常数,例如 5x5
  • 可以通过启发式计算出搜索范围

  • 比较慢,时间开销大

其他

  • 多光源,如果利用 SM,需要对每个光源计算一个 SM