Cesium阴影技术

【以前的文章存档】

Cesium的阴影技术用的是Cascaded Shadow Mapping(简称CSM),
先来说下两种阴影类型,
一种是硬阴影

基本光线跟踪阴影:

  • 从相机发出射线
  • 射线交于物体表面
  • 发射阴影射线检查光线是否到达这一点
  • 如果阴影射线被阻挡,那么该点就处在阴影中

另一种是软阴影

  • 比硬阴影更真实
  • 光源作为物理对象
  • 阴影射线现在更像锥体
  • Penumbra:部分阴影
  • Umbra:全部阴影

常用的阴影技术

体积阴影(Shadow Volumes)

  • 阴影用空间中的多边形体来表示
  • 优点:准确的硬阴影
  • 缺点:速度慢,需要大量的光栅化

基本方法:

  • 对于场景中的每个三角形,将其边缘投射到沿着光的方向无限延伸的地方
  • 这个截去头部的金字塔就是阴影体
  • 位于阴影体中的场景的任何部分都属于阴影

实现方法:

  • 深度预先计算: 将场景渲染到深度缓冲中。
  • 渲染所有的阴影体
    • 如果阴影体片段通过深度测试:
      • 如果三角形是正面的,增加模板缓冲
      • 如果三角形是背面的,减少模板缓冲
    • 如果模板缓冲区在特定像素处的值为0,则该像素不处在阴影中
  • 现在再次渲染场景,启用颜色缓冲
    • 使用模板缓冲区作为掩码。如果模板缓冲对于特定的片段值大于0,则丢弃该片段
    • 否则,像往常一样计算漫反射和高光照明

阴影贴图(Shadow Maps)

  • 阴影是通过检测深度缓冲来确定的
  • 优点:速度快,支持软阴影
  • 缺点:占用内存高,有锯齿

基本方法:

  • 从光源的角度渲染这个场景
    • 把光源当作照相机
    • 渲染出一个深度纹理来创建阴影贴图
  • 从照相机的角度渲染这个场景
    • 在顶点着色器中将每个顶点从世界空间坐标转换为光源空间坐标。
    • 把光源空间位置传送到片元着色器。
    • 将片元的深度与存储在阴影贴图中的深度进行比较。如果深度较大,则被阴影覆盖。

光源类型

  • 平行光(太阳)————使用正交投影
  • 聚光灯————使用透视投影
  • 点光源————类似于聚光灯,但是需要一个全向阴影贴图
    • 创建六个光视锥,并在几何着色器中渲染到立方体贴图(cube map)。
  • 阴影贴图是昂贵的;在一个标准的视频游戏中通常只有一个灯源投射阴影。

阴影贴图存在的问题

  • 投影走样(Projective Aliasing)
  • 透视走样(Perspective Aliasing)
  • 纹理分辨率限制(Texture Resolution Limits)

投影走样

  • 当几何图形的斜率平行于光的方向时候发生
    • 最好的情况: 头顶光源,平坦的地板
    • 最坏的情况: 头顶光源,竖直的墙壁
  • 即使在最好的情况下,深度缓冲的精度,阴影贴图的分辨率和浮点数比较也会导致该问题。
    • 深度冲突(z-fighting)
  • 会严重影响圆的边缘地方

深度偏移

  • 应用一个恒定的深度偏移
    • 在光通过时,把深度稍微往深推一点
    • 现在在最好的情况下深度比较测试会成功
    • 但是在最坏的情况下仍然有问题

  • 深度偏差过小会导致z-fighting
  • 深度偏差过大会导致光线泄漏
  • 偏差大小应该取决于三角形的斜率
  • 使用由硬件计算出来的屏幕空间导数
    • glPolygonOffset利用屏幕空间导数自动计算偏移量
      • 取一个常量参数和一个斜率缩放参数
      • 仍然需要调整特定场景的参数
    • 使用GLSL命令dFdx和dFdy将屏幕空间相邻像素转换为光空间斜率
      • 更复杂,计算成本更高

其他实现

  • 增加深度缓冲的精度
    • GL_DEPTH_COMPONENT16
    • GL_DEPTH_COMPONENT24
    • GL_DEPTH_COMPONENT32F
  • 选用光视锥合适的近平面和远平面,来适应场景
  • 如果可能的话,增加阴影贴图的分辨率
  • 线性化深度缓冲
    • 相机可能朝向距离光源很远的一部分场景,我们想要看到和靠近灯光时一样的细节。

法线偏移

  • 向法线方向推进深度
  • 补充恒定深度偏差

法线着色

  • 应用lambert shading可以隐藏走样

  • 此外,适当的阴影看起来更真实。这是日落的照片

纹理分辨率

  • 画面质量取决于纹理分辨率
    • 低分辨率阴影贴图产生块状的结果
    • 高分辨率的阴影贴图看起来更好,但是会占用大量内存
    • 如果场景真的很大怎么办?
      • 一种单一纹理不可能延伸到整个世界

透视走样

  • 视图空间中的像素大小不匹配阴影贴图中的纹素大小

高级阴影贴图

  • 修正透视走样
    • 眼睛附近需要更多的细节,远离眼睛的地方可以用更少的细节。
  • 要处理纹理分辨率限制
    • 保持纹理分辨率不变,独立于场景大小
    • 眼睛周围的中心阴影贴图
    • 阴影贴图不应该覆盖视角外面的区域
  • 解决方案:
    • 扭曲技术
      • 透视阴影贴图(PSM)
      • 光源空间透视阴影贴图(LiSPSM)
      • 对数透视阴影贴图(LogPSM)
    • 视锥体分割技术
      • 级联阴影贴图
        • aka Z-partitioning,平行分割贴图
      • 样本分布阴影贴图

透视阴影贴图

  • 先将透视变换应用到场景中,然后再进行渲染阴影贴图
    • 只需替换标准的视图-投影矩阵
  • 倾斜阴影贴图,使靠近眼睛的区域有更多的密度
  • 仍然使用相同分辨率的单个阴影贴图,但获取更多

光源空间透视阴影贴图

  • 修正了透视阴影贴图的局限性
    • 透视变换应用于光的视投影矩阵而不是眼睛的视投影矩阵
      • 处理位于viewer后面的能投射阴影的物体
      • 灯不改变他们的类型(PSM可能不能正确地把方向光转换成点光源)
      • 总体上更稳定,误差分布更好

对数透视阴影贴图

  • 透视投影+对数变换
  • 最佳常数误差
  • 需要对数光栅化,目前不支持当前的GPU硬件

结果

  • 标准的阴影贴图
  • 透视扭曲(LiPSM)
  • 对数透视扭曲

级联阴影贴图

  • 将光源视锥分割成多个视锥块
  • 靠近眼睛的密度高,远离眼睛的密度低
  • 每个子视锥体都有自己的阴影贴图。它们的尺寸都一样
  • 片段着色器采样适当的阴影贴图

如何选择划分方式

  • 基于特定视图选择静态分区
    • 鸟瞰视图只需要分很少段
    • 当场景延伸很远时标准行走视图需要多个级联视段
    • 总是调整参数比较麻烦
  • 分类型
    • 自己决定
    • uniform
    • 对数
    • 找中点
  • 在Cesium里,倾向于用对数分割(0.9 lambda)
  • 在某些情况下使用显式级联距离
    • 照相机靠近一个很小的物体时
  • 最好有一个动态的方法
  • 样本分布阴影贴图
    • 使用几何信息和遮挡测试创建紧密绑定的frusta
    • 近似对数分裂

结果

拟合的阴影贴图

  • 从深度缓冲区中获取深度范围是理想的,但代价昂贵
  • 下一个最好的方法是从包围盒里获取近/远平面
  • 深度范围最初为10,000米
  • 实际深度范围小于500米
  • 适用于自顶向下视图,地平线视图没有区别

平滑硬阴影

  • 线性过滤
  • 百分比渐进过滤
  • 方差阴影贴图

线性过滤

  • 启用阴影贴图纹理的线性过滤
    • 插值2x2像素区域的深度而不是仅仅选择最近像素的深度
  • 真的很简单。但并不完全正确。

百分比渐进过滤

  • 通过观察邻近阴影纹素来模拟软阴影
  • 取阴影贴图中4个邻近样本点
    • 使用GLSL命令textureGather
  • 比较每一个样本点的表面的深度
    • 为textureGather调用提供表面深度
  • 双线性插值结果
  • 不同于线性过滤,它是对深度值进行插值,而不是结果比较
  • 为了得到更大的半影,调用4个textureGather命令来采样16个邻近点

方差阴影贴图

  • 在一张贴图中存储深度,在另一张贴图中存储深度的平方
  • 根据你的喜好过滤这些贴图
    • Mip-map
    • 高斯模糊
    • 区域求和表
  • 通过一个概率函数确定片元的阴影强度
  • 优点:
    • 能够用比PCF小得多的成本来捕捉大的半影

实现细节(Cesium)

需要妥协的地方

  • 精度/效率
  • 当工作在不同的3D模型、相机视角下,需要选择合适的bias值
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

搞GIS图形的sky.

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值