本文描述的是基于Shadow Mapping的软阴影技术,读者应该对Shadow Mapping 有基本的理解。

(本文同步发表在我的博客)

关于阴影走样

Shadow Mapping 有一个严重的问题就是走样(Aliasing),这一点在我第一次实现Shadow Mapping的时候也非常明显

图中阴影边缘明显的锯齿就是走样

这是一个老生常谈的问题了,多年来,相比于Shadow Volume,Shadow Mapping技术更加流行,而走样问题是由于Shadow Mapping的技术特点产生的,因此在学界近30年都在讨论如何解决,也确实取得了不少的进展。由于篇幅所限,我将介绍一些有代表性的技术。

我们首先还是来分析一下Shadow Mapping产生锯齿的原因。以聚光灯为例,如果光源和遮挡物离场景很远,当我们对场景中的一点找到对应shadow map中的深度,我们会在很大的矩形范围内,得到同样的深度值,这样就形成了锯齿。

除了锯齿的问题,我们发现无论遮挡物有多远,阴影的边界依然很明显,这就是所谓的硬阴影。

综上,我们需要解决两个问题锯齿和明显的阴影边界

为了更好地分析问题,我们定义一个shadow test function

是摄像机看到的场景中某一点,
是这一点到光源的距离,
是在阴影纹理中遮挡物的位置,
则是遮挡物的深度。

显然,对于标准阴影贴图技术

这个函数是离散的,我们希望对这个函数做一个变换,使其连续化。
我们可以直接对

滤波,达到连续化的目的,然而这样的操作效率并不高,我们希望先一步对z进行滤波处理,这样我们就能在shadow pre-pass阶段进行滤波,不用在光照计算阶段进行重复的fetching和计算(这样效率很低)。

但是如果我们真的直接对z滤波,我们得不到想要的结果。原因很简单:,对

滤波和对
滤波是完全不一样的,这里的
是一个阶跃函数,由卷积定理,下面的式子不成立:

因此,我们看起来只能从

入手,但是有些方法可以先把
稍微变换一下,使其能满足卷积定理,从而应用pre-filtering

软阴影算法概览

软阴影主要解决两个问题,1:如何高效的blur,也就是如何生成一致的软阴影 2:如何利用距离控制阴影软硬程度,并与1中的算法结合

一致性软阴影:

  • Percentage-Closer Filter (PCF)
    PCF是直接对

    滤波的方法,效率相对较低
  • Convolution Shadow Mapping (CSM)
    CSM把ff变成傅里叶级数求和的形式,从而得到一系列函数的和,对这些函数就有可能应用pre-filtering
  • Variance Shadow Mapping (VSM)
    VSM巧妙地利用了切比雪夫不等式,

    是一个单边的近似公式,在这种情况下,对z进行pre-filter的效果是可以接受的
  • Exponential Shadow Mapping (ESM)
    ESM相当简洁,使用的是一个单边指数函数来近似,效率和空间都要好的多,支持pre-filtering

距离相关的阴影:
这个方面最著名的是Percentage-Closer Soft Shadows (PCSS)

本文将逐一介绍这些技术

PCF滤波

既然要模糊阴影边缘,那我们就可以利用多重采样和插值函数,并将插值的结果作为

的值。

这样,在阴影的边缘,离阴影越远的未被阴影照射到的部分采样结果就越接近照亮部分。

由于多重采样的平均值相对于单次采样更接近期望深度,因此这个滤波算法也被称为Percentage-Closer Filter

PCF是一种很朴素的思想,带来的问题也很多。

  • 首先,多重采样非常影响性能,单次采样的开销取决于GPU是否支持Pre-Fetch Texture和这个采样是否是Simple Texturing(不依赖其他采样结果的采样)。但无论单次采样效率如何,多重采样在算法层面效率就低下。

通过控制多重采样的范围和步长,我们可以控制采样的效率和阴影的总体软硬程度,在相同步长下,一个3x3的PCF不如一个一个5x5的PCF“软”

  • 其次,PCF还不能支持pre-filtering(所谓pre-filtering是指在渲染阴影纹理的时候就进行filter滤波处理),无法利用mipmap做完美的三线性插值
  • 最后,基于采样的阴影模糊一般都会有二次走样的问题,这是基于sampling的方法无法绕过的问题

效率和存储

存储:32F
PCF是是完完全全的非pre-filtering的方法,性能取决于场景复杂度和过滤核的大小,通常来说性能表现较差

Convolution Shadow Maps:卷积阴影

CSM的

表达式为:

其中
ck=π(2k−1)ck=π(2k−1)

当M取不同的值的时候,

的曲线如下:

考虑到效率和实现效果,M应该取大于4的值,

当d=z的时候,

取值0.5,这显然是不对的,因为当d=z,我们应该得到一个完全无遮挡的表面,所以应对原始的函数取一点偏移, 使d=z时
取值1,得到的函数图像如下:

我们也可以对

做一下变形,通过简单的
,并裁剪超过1的部分,

这样也能得到可以接受的图像

CSM存在的问题

观察CSM的图像,在d=z的两边曲线都出现了抖动,反应在渲染结果上就是物体在和光源连线上的忽明忽暗

抖动和漏光对CSM来说就像双刃剑的两面,要么抖动多一点,要么漏光多一点,

效率和存储

存储:

CSM支持pre-filtering,M越大,效率越低,但比起PCF来说要好很多

Variance Shadow Mapping: 深度概率分布和切比雪夫不等式

为了分析阴影边缘分布的统计规律,我们考虑光源视角的深度为一个随机变量x。根据切比雪夫不等式的单边公式,深度大于给定值t的概率有一个上界

这个上界有多接近于

?我们可以假设一个被称为single-bounded的理想模型。

假设把深度为d1的平面投影到深度为d2的平行平面,那么在深度为d1的平面边缘,可以假设投影深度正好为d2的概率为p(这个p也可以认为是pcf的值)那么可以得到深度的期望为

期望的方差:

这样我们就可以根据上面提到的切比雪夫的单边公式来计算深度小于d2的概率的上界

这个结果刚好就是p的值, 这表明

在理想情况下是一样的

虽然这是一种非常特殊的情况,只有一次投影,一个遮挡物,但是这个结果表明,由切比雪夫不等式计算出来的上界很可能只有一点点的偏移,我们可以直接采用这个上界作为阴影测试函数

然而,当对VSM应用一个较大核的滤波,也就是像基于VSM生成软阴影的时候,将会导致结果有较大偏差

VSM的实现

VSM的实现关键在于渲染到深度纹理的同时渲染深度的平方,并在shading pass计算期望和方差。这个深度贴图会占用比之前多一倍的内存

实现需要注意几个地方:一是在shadow pass 的时候就利用mipmap,高斯模糊等技术做好预处理,减少锯齿和突变。二是在shading 的时候,注意只有深度大于期望的时候才利用切比雪夫公式进行近似计算(这是由于计算的深度是一个上界)

效率和存储

存储:32+32
VSM同样支持pre-filtering但效率比CSM稳定,开销也更小

Exponential Shadow Mapping(ESM)

在ESM中,

的形式是:

显然这个表达式是单边有效的,对于

的情况,
将会爆炸式增长,我们暂时先假定

得到ESM的图像表示:

有了这个表达式,我们就有可能实现pre-filtering,从而提高性能。
下面的式子证明了经过ESM处理之后filtering和pre-filtering是等价的

常数指数c的选择会影响ESM的

曲线:c 越大,
越陡峭,也就越接近真实的阴影;当c的值偏小的时候,会造成漏光。

c 的上限受到浮点数精度的影响。32位float对应的c的经验最佳值大概在 80 左右,这个值生成的阴影比

的 CSM 更好

ESM存在的问题

下面这个图很好的反应了由shadow map精度带来的问题

观察图中的边缘,发现有过亮的现象产生,我们用一张图可以清晰明了的解释

对于阴影纹理,存储和采样点都在纹素中心,当我们计算d(x)-z(p)时就会出现小于0的情况。于是我们得到一个全白的结果,而正确结果应该是有50%的阴影。

这种情况出现在多重阴影的边缘,原论文中有两种解决方案,一种是增加一个

的pre-pass,还有一种是对
设置一个阈值
。这两者都很好理解,
的方法通过找找到附近的最大深度值来判断是否取样到了比d还远的表面,阈值检查
是否超过某个值,若超过某个值就有可能遇到了上面的情况

总的来说,阈值

的效率比
要好,没有多出来的pre-pass,更何况要计算
免不了要采样多次;阈值的方法虽然不能保证所有的情况都能处理,但胜在效率高,存储小。

ESM的实现

ESM只需要一个32F的浮点纹理,存储

的值,之后可以直接对阴影纹理进行线性插值,高斯模糊等操作

存储和效率

存储:32F
ESM比之前提到的几乎所有方法都要好,不需要MRT,支持pre-filtering,没有震荡现象,可以说是最优的方案

PCSS

PCSS是Percentage-Closer Soft Shadows的简称。PCSS和上面的算法解决的是不同的问题。
PCSS解决了前述算法软阴影的一致性问题。原本的滤波算法不管遮挡物和阴影接收物体相距多远,都生成一致的“软”的阴影。

PCSS 通过一个和光源位置相关的相似三角形来控制软阴影的采样搜索范围。下面这个图展示了PCSS如何根据光源,遮挡物,和阴影投射目标三者确定搜索范围

PCSS首先假定光源是一个区域光(area light),这看起来好像很不可思议,但其实这才是我们最常见的光源。

传统的点光源,聚光灯和平行光,其实都不过是某种模拟,比如点光源模拟小灯泡,聚光灯模拟手电筒,平行光模拟太阳,其实严格来说这些都是区域光,软阴影形成的很大一个原因就是区域光的存在。
为了在旧的光源体系中加入区域的概念,我们假定光源是一个始终平行于接收面的圆形,并用uniform变量控制这个圆形的半径
这样,我们就可以估算根据光源,遮挡物,被遮挡物三者的距离估算阴影的软硬程度

计算公式如下:

原理非常简单,就是利用了一下三角形的相似性

PCSS的主要贡献在于形成了所谓“动态”的阴影,PCSS确定的这个搜索范围,也可以看做是某种模糊半径,或者卷积核的大小,并没有要求一定按照多重采样(PCF)的方式来实现,因此可以很好地和VSM,ESM等技术结合

参考文献

[1] Fernando, Randima. “Percentage-closer soft shadows.” ACM SIGGRAPH 2005 Sketches. ACM, 2005. Percentage-closer soft shadows

[2] Donnelly, William, and Andrew Lauritzen. “Variance shadow maps.” Proceedings of the 2006 symposium on Interactive 3D graphics and games. ACM, 2006. Variance shadow maps

[3] Annen, Thomas, et al. “Convolution shadow maps.” Proceedings of the 18th Eurographics conference on Rendering Techniques. Eurographics Association, 2007. Convolution shadow maps

[4] Annen, Thomas, et al. “Exponential shadow maps.” Proceedings of graphics interface 2008. Canadian Information Processing Society, 2008. Exponential shadow maps

[5] Bavoil, Louis. “Advanced soft shadow mapping techniques.” Presentation at the game developers conference. Vol. 2008. 2008. http://developer.download.nvidia.com/presentations/2008/GDC/GDC08_SoftShadowMapping.pdf

[6] Shen, Li, Jieqing Feng, and Baoguang Yang. “Exponential soft shadow mapping.” Computer graphics forum. Vol. 32. No. 4. Blackwell Publishing Ltd, 2013. Exponential Soft Shadow Mapping

中blur函数_实时渲染中的软阴影技术相关推荐

  1. matlab中blur函数_游戏中的PostProcessing(后处理)

    PostProcessing是现代游戏中必不可少的技术之一,本文简单来总结下PostProcessing的实现原理和应用.因为详细写起来需要很大篇幅且很费时间,这里只简单介绍下原理. 1.基础部分 P ...

  2. sql中len函数_在SQL中使用LEN(),ROUND()函数

    sql中len函数 LEN() and ROUND() are scalar functions which return a single value, based in the input val ...

  3. java中readline函数_自定义BufferedReader中read和readLine方法

    BufferedReader中read和readLine方法总结 实例如下所示: package day0208; import java.io.FileReader; import java.io. ...

  4. matlab中find函数_在R中使用Matlab函数

    R, Matlab MATLAB是一款商业数学软件, R是一个拥有庞大工具库的数据统计.建模.可视化分析软件.R 不仅支持C/C++, python代码的运行和工程移植, 也支持在R中使用MATLAB ...

  5. python中transpose函数_对numpy中的transpose和swapaxes函数详解

    transpose() 这个函数如果括号内不带参数,就相当于转置,和.T效果一样,而今天主要来讲解其带参数. 我们看如下一个numpy的数组: `arr=np.arange(16).reshape(( ...

  6. python中编函数_在python中编写函数

    Aaron Hall.. 10 递归实现 这是一个相当优雅的递归实现,为了清晰起见,它使用了Python 3的功能: def strict_compose(*funcs): *funcs, penul ...

  7. python中execute函数_在excel中调用python函数

    效果: 通过excel引用在py文件中写好的load_settle()函数,可以快捷的获取对应的历史结算价. 使用方法: 1.首先安装office,我用的是2016版本. 2.安装python,推荐使 ...

  8. python中strptime函数_在Python中操作时间之strptime()方法的使用

    strptime()方法分析表示根据格式的时间字符串.返回值是一个struct_time所返回gmtime()或localtime(). 格式参数使用相同的指令使用strftime();它默认为&qu ...

  9. python中fill函数_在figu中旋转matplotlib的fill函数

    我在试着做一个三人合谋.其中一个绘图框相对于另一个旋转90度,并垂直于另一个绘图的轴.所以我可以在这个帧中绘制一个直方图图,但是当我使用kde生成数据并使用fill覆盖到{}时,它不会旋转.在impo ...

  10. python中execute函数_在python中函数的调用

    """BMI指数(即身体质量指数,简称体质指数又称体重,英文为BodyMassIndex,简称BMI)"""def fun_bmi(pers ...

最新文章

  1. 聚焦第三届世界智能大会|大佬们讲了哪些干货?
  2. Android抓包方法(一)之Fiddler代理
  3. IBM copy service--flashcopy 实验
  4. 教你获取WindowsNT的Admin权限的方法
  5. 多个vue项目合并成一个_集美们,快看如何一步将多个PDF合并成一个PDF
  6. spark之4:编程指南
  7. octave修改选中的代码行的颜色
  8. 9-[记录操作]--数据的增删改,权限管理
  9. 【OpenJ_Bailian - 2711 】 合唱队形(dp,枚举中间顶点)
  10. 在 Mac 上通过 Docker 运行 Asp.net Core 简易教程
  11. VS工作笔记-C++在release模式下可以进行调试
  12. 在没有插件的情况下为Chrome设置Proxy
  13. android笔记--与服务器交互更改简历状态
  14. 高清壁纸:60款可爱的圣诞节电脑桌面壁纸《中篇》
  15. mybatis pageHelper 不分页
  16. 主板找不到SSD解决一例
  17. VS2017 安装xamarin 开发安卓程序教程
  18. 帝国Empire采集-帝国如何免费采集
  19. python程序扩展名 py、pyc、pyo、pyd文件区别
  20. 一个程序员的多年珍藏--收藏

热门文章

  1. 记录一次手机联系人整理(XML文件格式处理)
  2. OSX 鼠标和键盘事件
  3. 20170830 - A - Java IO操作
  4. XML学习总结(一)——XML介绍
  5. erlang中遍历取出某个位置的最大值
  6. 关于apache的重启
  7. [JAVA][Eclipse]JVM terminated. Exit code=13
  8. python批量删除文件名_用python批量删掉文件名中共同存在的字符
  9. 拓端tecdat|R语言用回归构建配对交易(Pairs Trading)策略量化模型分析股票收益和价格
  10. 拓端tecdat|R语言自定义两种统计量度:平均值和中位数,何时去使用?