当前位置: 代码迷 >> 综合 >> Real-Time Rendering 第七章 shadow
  详细解决方案

Real-Time Rendering 第七章 shadow

热度:89   发布时间:2023-11-21 16:09:58.0

如果使用体积光或区域光 ,阴影就会由 完全阴影(umbra)和半阴影(penumbra)组成

半阴影如果仅仅是模糊阴影的边缘是不行的,得不到正确的半阴影

真确的soft shadow ,离阴影投射物体越近,阴影就越sharp,soft阴影的大小和hard 阴影的大小并不一样,它随着光源大小的增加而减小,soft shadow 让物体看起来更真实一些,但是hard shadow 渲染的更快

7.1 Planar Shadows

7.1.1 Projection Shadows

投射到平面y=0的阴影投影矩阵为:

然后并不都是投影到y=0的平面,而是π=n*x+d的平面 ,则投影矩阵是:

当 y = 0,  n = (0, 1, 0), d = 0的时候,这二者的就相等了

为了渲染阴影,就是把这个矩阵和阴影接收者相乘,然后颜色是黑色

第一个问题要避免阴影渲染到receiver的下面,一个方法就是先渲染 ground,然后关闭深度写入,再渲染阴影

第二个问题是如果receiver 没有阴影那么大,也就是阴影超出了receiver的范围,解决这个可以使用stencil buffer. 首先渲染receiver ,然后把stencil 值存储在 stencil buffer. 然后关闭深度写入,然后只在stencil buffer 有值的地方绘制阴影。

第三个问题是如果shadow caster 在光源之上

另一种阴影投射的方法就是把阴影渲染到texture中,然后把receiver对该纹理采样,这样做不好的地方,是纹理可以被放大,也就是第六章说的一个texel 覆盖多个pixel.

如果阴影投射者和receiver都没有发生改变,则这个阴影纹理就可以重复使用

7.1.2 Soft Shadows

产生soft 阴影的方法有好多种。

当光源有区域的时候就会出现Soft shadows . 每一个 punctual 光源, 都会渲染一张image,并把它添加到缓存里面,多个光源产生了多个image,这些image 一平均就会生成一张带有soft shadow的image. 理论上,任何生成硬阴影的算法都可以与这种累积技术一起使用来生成半影. 但是比较耗时

Heckbert and Herf 使用一个基于视锥体的方法生成阴影frustum-based method to produce their shadows. 把光源当成一个viewer,然后地面当做视锥体的远切面,把阴影投射到上面。

这种方法的问题:一组光源产生了多个阴影,每一个阴影都是一个pass,非常耗时。

另一个比较效率比较高的方法是利用卷积 convolution,比如filtering.对一张硬阴影从一个点开始模糊,产成一个渐变 . 比如:

但是这种方法没有那么真实,并不能准确反映出物体的轮廓。

还有许多其他方法可以提供更好的近似值,但要付出额外的代价. 比如, Haines [644] 对硬阴影进行渐变,比如:

 然而这种方法产生的半影在物理上是不对的,因为它仍然是对边缘内部进行渐变。

7.2 Shadows on Curved Surfaces

在曲面上产生阴影,最简单的方法就是生成一张阴影贴图。从光源的角度看物体,看到的物体就是在光照下的,看不到的就是在阴影中的. 然后receiver 对这张贴图进行采样,receiver的顶点uv可以在application阶段计算 . 这与上一节说的 ground shadow texture有点不同, 上一节说 物体 被投射到一个特殊的平面,这儿,阴影贴图是从光源的视角产生的。

这种方法可以和其它阴影投射方法一起使用,有时主要用来感知物体的位置sometimes is used primarily for helping aid perception of an object’s location. ,比如一个平台跳跃游戏,人物的阴影通常在它下面。 Eisemann and D ?ecoret 假设在人物头上有一个矩形区域的光源,产生一个阴影贴图的堆栈,然后根据离光源的距离产生mipmap阴影贴图 。

但是对于这种对贴图采样的方法也有缺点,首先 application 必须识别哪些物体是遮挡物,哪些是接收物,并且receiver 必须一直维持接收者的状态,从而接收从遮挡物投射出来的阴影,否则阴影就会反投射,其次,遮挡物不能遮挡它自己。

可以通过预渲染 投射的纹理  组成各种各样的光照模式.比如:一个聚光灯只是一个正方形的投射纹理,里面有一个圆圈定义光. 通过由水平线条构成的投影纹理,可以创造出百叶窗效果. 这种贴图叫, cookie texture或者 gobo map.

7.3 Shadow Volumes

Presented by Heidmann in 1991 [701], 提出了基于 Crow’s shadow volumes [311] 通过使用stencil buffer 投射阴影 . 这种方法任何GPU上都可以使用,因为只需要一个stencil buffer,它不是使用阴影纹理,所以避免了采样问题,从而产生.正确的sharp shadows. 缺点是 比如 一个角色的衣服有褶皱,很薄,硬阴影效果不好 , Shadow volumes 因为性能不稳定,所以当下很少用了

它的原理大概如下:

 从一个点向下投射成一个锥体,被一个三角面分割成两部分,上面部分包括顶点,下面的是一个被截断的锥体,这个顶点就好比光源,任何在截断锥体的物体,就认为在阴影当中,下面的锥体就叫shadow volume.

也就是说我们观到某些scene时,从眼睛投射一条射线,穿过你看的像素,然后  遇到了遮挡物,然后每次穿过shadow volume 正面的时候增加一个计数,穿过背面时 减去一个计数,直到射线到达要在屏幕上展示的物体,如果计数大于0 说明在阴影中,否则在光源下,比如:

 

用射线来检测是非常耗时的,我们有更好的方法,利用stencil buffer,它可以帮我们计数,首先清空stencil buffer,然后 绘制场景中没有被光照的物体到Framebuffer中,再从 color buffer 中获取颜色信息,从 z-buffer 中获取深度信息. 然后关闭深度写入,但是仍然深度测试,绘制  shadow volumes 的前面的三角面.在这个过程中  stencil operation 模板测试 当绘制三角形的时候增加它的值 ,第四 执行另一个pass ,这次只绘制三角面的背面,然后这时stencil buffer 减少它的值。

只有当渲染的shadow-volume face  是可见的时候,stencil  buffer 才会自增或者自减,从这一点来说  stencil buffer  里面存储了 每个像素的 阴影状态,最后再渲染一遍场景,这次只渲染光照下  且  stencil buffer =0的物体, 0 表示在光照下

为每一个三角形生成一个四边形面片,造成了大量的overdraw,每一个三角形都会生成三个四边形面片 一个球有上千个三角形,就会生成上千个四边形面片.一种方法是指绘制 物体边缘的四边形,比如 球体有五十个边缘三角形,就只绘制五十个四边形,geometry shader 可以自动生成这些轮廓边缘,Culling and clamping 技术也可以用来降低性能消耗[1061].、

然而 shadow volume 算法仍然有缺点 : extreme variability. 假如摄像机和light在一个位置,这样消耗是最小的,因为 quadrilaterals 没有覆盖任何像素,假如摄像机围着三角形转, shadow-volume quadrilaterals 就会变得可见,然后会覆盖屏幕上的很多像素,然后造成大量的计算.如果摄像机进入shadow 内部,又会造成大量计算,它不实用的原因就是  它会造成帧率不稳定

7.4 Shadow Maps

In 1978, Williams [1888] 提出了z-buffer-based renderer 可以快速的生成阴影. 这个想法是使用z-buffer来渲染场景,从光源的位置来投射阴影,凡是光源看见的就是在光照中,看不见的就在阴影中,这只需要z-buffer就够了.Lighting, texturing, and writing values into the color buffer 都可以被关闭

每一个z-buffer中的像素,包含了一个物体上离光源最近的点的深度值,也就是 能看到的 物体表面的点,就像下图一样.我们把整个z-buffer叫做  shadow map, 也叫 shadow depth map 或者shadow buffer. 为了使用阴影贴图,场景需要第二次渲染,但这一次是针对viewer.然后把它在像素上的位置和  shadow map上的位置作比较.如果它的位置要比shadow map 的值大,说明它在阴影中 ,这个技术是通过纹理映射实现的,比如:

 

阴影映射是一种流行的算法,因为它是相对可预测的. 构建阴影贴图的成本与渲染物体的数量大致呈线性关系,而访问时间是恒定的. 只要物体在光照下不移动, shadow map 就是可以被重复使用的。

当生成单个 z-buffer 的时候,也就是一张shadow map,也就是只在光照的位置朝一个方向观看, 对于一个遥远的方向光,如太阳,只要是视野中viewing volume  中能看到的物体,都可以被投射阴影.光源使用 正交投影  orthographic projection,在 x, y 方向上足够宽高,来覆盖所有看得到的物体,Local light sources需要尽可能进行类似的调整。如果local light 离阴影投射物足够远,一个view frustum 可能足以包含所有这些物体. 另一种情况是,如果局部光线是聚光灯,在它光锥之外的都被认为是不被照亮的。

如果the local light 在场景中被阴影投射物包围,就是物体围绕在光源周围,一个解决办法是使用six-view cube, 类似于cubic environment mapping [865]. 这叫 全向性 阴影贴图omnidirectional shadow maps,使用它的问题时  要避免 两张shadow map 在接缝处的问题

场景中并不是所有的物体都被渲染到shadow map 中,只有可以投射阴影的物体才被渲染.  比如 如果物体超出一定的范围 就不投射阴影了,一些很小的物体也不投射阴影.如下图:

如上图中.凡是超出 view frustum 的物体也不投射阴影,来动态的调节light volume 的大小. 只渲染看得见的物体的阴影,不仅仅能节省时间,还能提高阴影贴图的分辨率,从而提高阴影的质量.

一个缺点是 阴影的质量取决于  shadow mapping 的分辨率 和  z-buffer 的精度,因为shadow map 是在深度值比较之后 再进行采样的,, 该算法容易出现混叠问题,特别是接近物体之间的接触点. 一个普遍问题是 self-shadow aliasing,也叫 “surface acne” or “shadow acne,” ,一个三角形本应该给其他物体投射阴影的,但是它自己也被它产生的阴影投射了,也就是它采样了它自己投射的阴影,这个问题的根源有两个:一是处理器的精度所致,它的深度值和shadow map中的深度值一比较,确实它就应该在阴影中 ,另一个原因是 geometric, 从shadow map  中采样点的深度值,并不能代表该点的深度值,也就是视角观看的点和shadow map 中采样点的位置不是一个,比如 像素通常对它的中心点采样. 如下图:

一个通用的方法,但是不能根除这个问题的解法是利用bias. 对该物体的深度值进行一定的增加或减少,比如:

这些  bias 是一个常量值,但是当receiver 不是面向光源的时候,就会失败,一个更有效的方法是,偏移量根据receiver  和光源的角度成正比变化,表面越偏离光线,偏差就越大. 这种类型的偏差被称为坡度尺度偏差( slope scale bias). 这两种 biases 都可以使用同一种命令来执行,比如 OpenGL的glPolygonOffset. 注意如果receiver 直面光源,则它是不会产生bias 的,因为角度为0,这时就需要constant bias 和它一起使用 Slope scale bias 同时也是有限制的,因为因为当表面在光线下接近边缘时,切线值可能会非常高。

Holbert [759, 760] 提出了 normal offset bias, 它首先是朝着 表面的法线方向,偏移receiver 世界空间下的位置,和 法线与光源角度的sin 值成正比,比如:

 这改变的不仅仅是深度值,还有在shadow map 中进行采样点的uv 坐标,当光源与法线的角度增加时,bias 也会增加,从而避免self-shadow

如果 bias 太大 ,会造成light leaks 也叫 Peter Panning 的问题,阴影和caster  分离了。

另一种解决 self-shadowing 问题的方法是: shadow map 中只渲染 物体的背面,也叫 second-depth shadow mapping [1845], 当bias 不好用时用这个,但是当一个物体很薄,正反面都可见时,就会出问题,因为它背面和正面的位置是一样的,这时 添加一个 bias 可以解决这个问题,但该方案更容易受到光泄漏的影响,因为在接触点接收器和闭塞器的背面之间没有分离

比如:

注意   shadow mapping, 渲染的物体必须是不透明的,比如固体,或者必须正面和反面都被渲染到shadow map中,否则物体可能不会完全投射阴影. Woo [1900] 提出一个方法,用正面和反面之间的平面,这种方法是,把固体渲染到shadow map 中,并追踪表面上距离光源最近的两个点 . 这个过程可以通过 深度剥离或其他与透明相关的技术执行,两个物体之间的平均深度形成一个中间层,其深度被用作阴影贴图,有时称为双阴影贴图dual shadow map [1865]. 如果一个物体足够厚,self-shadowing 和 light-leak 就会很小.它的缺点就是用了两张shadow map。

   随着视角的移动, light’s view volume 的大小也会随着投射物体的改变而改变,这样反过来也影响了阴影的改变,这是因为light’s shadow map 是通过从光源投射的不同方向采样的,对于平行光,解决方法是 force each succeeding shadow map generated to maintain the same relative texel beam locations in world space [927, 1227, 1792, 1810]. 也就是说,你可以把阴影纹理想象成一个二维的网格,每个网格单元代表贴图上的一个像素采样点. 也就是随着视角的移动,生成的阴影始终是阴影贴图上的这些点。

7.4.1 Resolution Enhancement

和纹理一样,实际上,我们希望一个阴影贴图texel覆盖大约一个图像像素,也就是一对一. 如果光源的位置和摄像机的位置相同,则就是一对一的关系,但是只要光源方向改变,就不是一对一了,就会降采样和超采样. 如下图:

. 左边的图阴影成块状,因为一个texel 对应了多个像素,这种叫锯齿. 如下图:

这种可以通过提高阴影贴图的分辨率来降低这种变现,但是需要额外的内存.

这有另一种方法使得光照的采样模式更接近于真实情况. 这是通过改变场景光线投射方向的方式来实现的. 通常我们任务视野是对称的,然而,视角方向仅仅定义了一个视角平面,并没有定义像素的采样规律,定义视锥体的窗口可以被shifted, skewed, or rotated on this plane, 创建一个世界到view space的不同映射的四边形 ,这个四边形仍然以正常间隔采样,采样的频率可以通过改变光源的方向和视锥体的边界改变,如下图:

 右图就是在靠近眼睛的地方,texel的密度更大。

在将光线的视角映射到眼睛的视角时,有22°的自由度 [896]. 通过这种方法解决问题有几种不同的算法,它们试图更好地匹配光线的采样率和眼睛的采样率,包括 perspective shadow maps (PSM) [1691], trapezoidal shadow maps (TSM) [1132], and light space perspective shadow maps (LiSPSM) [1893, 1895]. See Figure 7.15 and Figure 7.26 on page 254 for examples. 这类技术叫做 perspective warping methods.

这种matrix-warping algorithms的优点就是只需要更改light’s matrices不需要其它操作,这些方法在光源方向和物体垂直的时候很好as the perspective transform can then be shifted to put more samples closer to the eye。

但是这种方法当光源在摄像机前面,并且照向摄像机的时候就会失效, 更靠近眼睛的地方需要更多的阴影贴图样本,但是线性扭曲只会使情况更糟[1555]. 还有阴影贴图的质量的突变,也是它不受欢迎的地方

另一种在view 更近的地方创建更多的采样点,是生成多张shadow maps. 原理就是: 创建多张shadow map  (可能是不同的分辨率),覆盖场景中不同的区域. 比如四张.通过这种方式,视角靠近物体的地方,阴影贴图的分辨率高,距离视角越远的物体,阴影的分辨率越低。

In 2006 Engel [430], Lloyd et al. [1062, 1063], and Zhang et al. [1962, 1963] 提出了把 view frustum’s volume分段切开 比如:

随着深度的增加,每一个 volume 都有两到三张阴影贴图. 通过使用图集不同的阴影贴图可以被当做一张贴图. 效果如下图:

Engel’s 为这个方法取名叫, cascaded shadow maps (CSM)

这种方法合理,而且实用性好,所以被广泛使用。

上图显示了从摄像机视角,不同的区域覆盖的阴影贴图不一样,越靠近摄像机的视锥体,有更多的采样点, 确定如何在不同的距离划分不同的视锥体的方法叫做 z-partitioning,一种方法是  logarithmic partitioning [1062]

 

n 是近平面 f 是元平面, c 是生成shadow map 的数量,r 是最终的比例,比如:n= 1 米, f= 1000 米, 我们有三张shadow map, 则 r = 3 1000/1 = 10. 最近的视锥体则是 1 - 10, 下一个视锥体的范围是10-100, 最后一个是 100 到1000 ,但是如果n很小的话, 只有 0.1 米,则r= 21.54, 比如, 0.1 to 2.154 to 46.42 to 1000. 这意味着每一个shadow map 都会覆盖很大的一片区域,降低了阴影的分辨率,在实践中,这样的划分给近平面附近的区域提供了相当大的分辨率,如果该区域没有对象,这就浪费了. 一种避免这种不匹配的方法是将分割距离设置为对数和等距分布的加权混合(1962,1963),但如果我们能够确定场景的紧密视图边界,那将会更好。

问题在于设置n的大小,如果太远了,n 太大了,物体就会被裁切掉,一种方法是sample distribution shadow maps (SDSM), 它使用前一帧的z-depth值通过两种方法中的一种来确定一个更好的分区。

第一种方法是通过z深度寻找最小值和最大值,并使用这些值来设置近平面和远平面. 这是在GPU上使用reduce操作来执行的,在这个过程中,一系列越来越小的缓冲区被compute or other shader分析,output buffer区作为输入反馈,直到一个1 × 1缓冲区被留下. 通常情况下,这些值被挤出一点来调整场景中物体的移动速度。除非采取纠正措施,否则从屏幕边缘进入的物体可能仍然会对画面造成问题,但会在接下来的画面中迅速纠正。这种方法还是挺快的。

 

当阴影在级联阴影贴图中跳跃时,会产生闪烁,在世界空间中保持稳定采样点的方法有多种,每种方法都有各自的优点. 一个方法是. 让切分的 view volumes 稍微重叠一下,这样重叠的区域就会在两个shadow map中混合,另一种方法是使用抖动法在该区域内取单个采样点1381].

因为这种方法流行,所以更多的讨论是怎么让它更高效的执行,如果一个视锥体的阴影没有改变,则这个级联的阴影贴图就不用重新计算. 对于每一个光源,会预结算一系列的shadow casters  ,从而查找哪些物体是在光源下的,哪些可以接收阴影.由于很难判断一个阴影是否正确,可以采取一些适用于级联算法和其他算法的捷径 ,一种方法是,一种技术是使用低层次的细节模型作为代理来实际投射阴影 [652, 1812]. 另一种方法是移除微小的遮挡物[1381, 1811]. 离摄像机很远的shadow map  应很少被更新,因为它们不重要,但是对于大的移动的物体,需要特别关心, DOOM (2016) 游戏维持了一个很大的阴影贴图的图集,只有当物体移动时,会重新生成这些阴影贴图,越远的级联贴图可以设置为完全忽略动态对象,因为这样的阴影可能对场景贡献很小.  在某些环境中,可以使用高分辨率的静态阴影贴图来代替这些更远的级联,这可以显著减少工作负载[415, 1590]. Cascaded shadow mapping 可以和烘焙光照贴图一起使用。

创建多张阴影贴图,意味着需要需要执行多次pass ,需要优化的算法都是为了在一个pass中渲染多张shadow map. 几何着色器可以用来复制对象数据并将其发送到多个视图 [41],也就是MRT. Instanced geometry shaders allow an object to be output into up to 32 depth textures [1456].

利用遮挡剔除,剔除哪些被遮挡的物体,由于辐照度随距离的平方而下降,一种常见的技术是在一定的阈值距离后剔除光源.

7.5 Percentage-Closer Filtering

 shadow-map 技术的一个扩展是可以生成软阴影,这和 Section 6.2.1 提到的 magnification 很像,但是不是对shadow map 上的单个点就行采样,而是对邻近的四个点采样.这项技术并不是在深度本身之间插入,而是它们与表面深度比较的结果. 也就是表面的深度值,单独与四个阴影贴图中的texel 中的深度值比较,然后每次比较都决定了这个点是在阴影中还是在光照中,然后通过 bilinearly interpolated 计算该像素到底有多少光照, . 这种过滤的结果是一个柔和的阴影. 半影的改变取决于  shadow map的resolution, camera location, 和 other factors.比如一个高分辨率的阴影贴图,会有一个相对较窄的软影。

这个对shadow map 中的多个点进行采样,然后混合的方法叫做percentage-closer filtering (PCF) [1475].

它不是根据表面上的位置,来查找光照的可见区域,而是通过其附近的一些点来查找它的可见性,如下图:

 “percentage-closer filtering” 的最终目的是找到在光照下可见的百分比,也就是一个像素多少是暴露在光下面的。

在PCF中,在表面位置附近,大约相同的深度的位置,但在阴影地图上是不同的texel,检查每个位置的光照可见性,这些结果要么在阴影中要么不在,然后混合得到一个柔和的阴影. 注意这个过程是非物理的: 这个过程不是直接对光源进行采样,而是在表面上进行采样. 距离occluder不影响结果,所以阴影有相似大小的半影. 尽管如此,这种方法在许多情况下提供了一个合理的近似值。

一旦采样的区域的宽度确定了,剩下来的就是避免锯齿, 这些变量包括采样区域的宽度、使用多少个样本、采样模式以及如何对结果加权. 在api能力较差的情况下,采样过程可以通过一种类似于双线性插值的特殊纹理采样模式来加速,该模式访问四个相邻的节点. 不是混合结果,而是将四个样本中的每一个都与给定的值进行比较,并返回通过测试的比率 .  然而,在常规网格模式中执行最近邻采样可能会产生明显的伪影. Using a joint bilateral filter that blurs the result but respects object edges can improve quality while avoiding shadows leaking onto other surfaces [1343]. See Section 12.1.1 for more on this filtering technique.

DirectX 10  为PCF引入了单指令双线性滤波支持,给出了更平滑的结果 [53, 412, 1709, 1790].  这提供了相当大的视觉改善最近邻采样,但来自常规采样的伪迹仍然是一个问题. 一个最小化 grid patterns 的方法是使用预计算的 Poisson distribution pattern,如下图:

这种分布将样本分散开来,使它们既不彼此靠近,也不呈规则模式.  众所周知,对每个像素使用相同的采样位置,无论分布如何,都会导致所有的软影很相似,我们可以通过随机渲染采样点来避免.

Self-shadowing 和 light leaks问题,在使用pcf 时会变得更糟. Slope scale bias 仅仅基于它与光线的角度增加它与光源的距离, 假设一个样本在阴影贴图上不超过一个texel. 通过从一个表面的单一位置在一个更大的区域取样,这个区域可能会真的变成硬阴影。

为了避免self-shadow. Burley [212]描述了一个bias cone, 每一个采样点移动的距离和它与原采样点距离成正比. 如下图:

 PCF 的一个问题就是采样区域的宽度是个常量,这些阴影表现得软阴影都是一样的,这在一些场景中是对的,但是当occluder 与 receiver 相接处的地方,就会差错。

例如:

7.6 Percentage-Closer Soft Shadows

In 2005 Fernando [212, 467, 1252] 提出了一个具有影响力的方法叫 percentage- closer soft shadows (PCSS). 他尝试对 表面位置 在shadow map 中的邻近区域查找所有可能的 occluders. 这些点到 occluders 的平均距离,来决定了采样的宽度:

 dr 是receiver 到 light 的距离, do 是occluder 到灯光的平均距离,也就是说, 软影的宽度,随着occluder 离光源的位置越近而变得越大。

如果没有遮挡物,则该表面是在光照下的,如果完全被遮挡,就不会进行下面的平均值处理了,如果都不是,则就会进行软影计算,为了节省性能,. Other- wise, then the area of interest is sampled and the light’s approximate contribution is computed. 样本区域的宽度可以用来改变采样的数量。还有一些其它的技术:比如对远阴影降低采样频率。

这种方法的缺点就是采样点多,然后找出所有的occluder,使用rotated Poisson disk pattern可以隐藏降采样产生的效果,. Jimenez [832] 法线这种方法不稳定,然后他发现 使用介于抖动和随机之间的函数可以在帧与帧之间得到更好的结果

Sikachev et al. [1641] 发现了一种使该方法更有效的方法,叫 contact hardening shadows (CHS). 这个也解决了另一个偏移的问题,如上图,.首先通过生成阴影贴图的mipmap,然后选择最接近用户定义的世界空间内核大小的mip级别,可以最小化这个问题.一个  8 × 8 区域被采样,来查找该blocker 的平均深度,只需要调用 16 次GatherRed() ,一旦确定了半影的位置,一个高分辨率的mipmap 对那些sharp area的阴影使用,然后对软影使用低分辨率的mipmap的 shadow map。

一个已被证明的有助于加速计算的方法就是,记录两个mipmap 的最大z-depth,和一个最小的z-depth,它可以用来快速的决定该pixel 是在光照下,还是在阴影中,比如如果一个texel 的z-depth 还要大,说明该像素在阴影中,其它的采样点就不需要了。

它的缺点就是当do 非常大时,可能会出现问题。

7.7 Filtered Shadow Maps

一种允许过滤 shadow map 的算法 叫 variance shadow map (VSM) [368]. 这种算法 在一张map 中存储深度值,另一个map 中存储 深度值的平方,在生成map 时,可以使用 MSAA or 其他的抗锯齿算法,这些map 可以被blurred, mipmapped, 然后放进一个 summed area tables 里[988],  将这些地图视为可过滤纹理的能力是一个巨大的优势,因为当从这些地图中检索数据时,可以使用整个阵列的采样和过滤技术。

 我们将在这里深入描述VSM,以了解这个过程是如何工作的;同样,这类算法中的所有方法都使用相同类型的测试.

首先, VSM 的第一张map 被采样一次,然后返回距离光源最近的occluders的平均深度值,. 用 M1, 是否大于receiver  点t在shadow map 上的深度值 如果是,则该点在光照下,否则,进行下面的公式:

 pmax 是在光照下的最大采样点,σ2 是方差variance, t 是receiver 的深度值, M1 是shadow map 的平均深度值,然后在第二张map  采样得到的值用 M2表示,用来计算方差:

 

 pmax是receiver 可接受光照的最大值,实际光照的值,不会大于该值,这个方程试图用概率论来估计,在表面位置上有多少遮挡器的分布超出了表面与光线的距离. Donnelly和Lauritzen表明,对于固定深度的平面遮挡器和平面接收器,p = pmax,因此公式7.7可以用来作为许多真实阴影情况的很好的近似。

一个区域的方差,在阴影的边缘会增加,深度上的差异越大,差异也就越大.  (t ? M1)2 是能见度百分比的重要决定因素. 如果这个值略高于零, 这意味着occluder的平均深度比receiver更接近光,然后pmax接近1(全亮). 这将发生在半影完全亮的边缘. M1变得更接近光,所以 (t ? M1)2 变得更大,pmax下降.

总的来说,由于GPU的优化纹理能力被有效地使用,VSM在处理时间的数量上提供了显著的质量提高. PCF 需要更多的采样点,所以需要更多的时间 , VSM可以只使用一个单一的、高质量的采样点来确定整个区域的效果,并产生一个平滑的半影. 这种能力意味着在算法的限制下,阴影可以在不增加额外成本的情况下任意变成软阴影。

 VSM很适合从地形产生阴影,因为这种阴影很少涉及多个遮挡器[1227]。 VSM is good for producing shadows from terrain, since such shadows rarely involve multiple occluders [1227].

 Called an exponential shadow map (ESM) or exponential variance shadow map (EVSM), 这个方法把深度值得指数值保存到两个buffer中,指数函数更接近于shadow map 里执行的 step function ,所以它减少了 光的泄露。它是经常用的。.

7.8 Volumetric Shadow Techniques

光线穿过透明物体,会让光衰减或者改变光的颜色,比如:在某些情况下会生成第二种类型的shadow map ,如果是渲染透明物体,shadow map 存储了最近点的深度值和颜色值,或者是alpha值,如果receiver 没有被不透明的物体遮挡,则透明的shadow map 的depth 就会和表面作比较,如果遮挡了,就和不透明的shadow map 深度值作比较。

Self-shadowing 对于云或者头发来说很重要,当物体很小或者透明的时候,单个shadow map 不能适应这种情况.

 

 

 

  相关解决方案