Chapter 7 Shadows

(个人笔记,由于刚开始学习再加上英语不太好,所以有的地理解的可能不太对,望指正)

Chapter 7 Shadows

本章阴影相关术语:

在这里插入图片描述

柔和阴影不是简单地将阴影边进行模糊处理,而是离投影面越近越锐利,越远边界越模糊。

Planar Shadows

Projection Shadows

如果将阴影投射到y=0平面上可以使用矩阵:

$$

M = \begin{pmatrix}
    l_y & -l_x & 0 & 0 \\
    0 & 0 & 0 & 0 \\
    0 & -l_z & l_y & 0 \\
    0 & -1 & 0 & l_y
\end{pmatrix}

$$

推广到任意平面:

$$
p = l - \frac{d + n \bullet l}{n \bullet (v - l)}(v - l) \
\
\
M = \begin{pmatrix}
n \bullet l + d - l_xn_x & -l_xn_y & -l_xn_z & -l_xd \
-l_yn_z & n \bullet l + d - l_yn_y & -l_yn_z & -l_yd \
-l_zn_x & -l_zn_y & n \bullet l + d - l_zn_z & -l_zd \
-n_x & -n_y & -n_z & n \bullet l
\end{pmatrix}

$$

示意图:

在这里插入图片描述

使用平面阴影需要注意避免将阴影显示到投影面以下,一种安全方法是先渲染投影面,关闭深度缓冲写入,再渲染阴影。为防止阴影投射到平面之外可以再渲染投影面时开启模板写入,渲染阴影时通过模板测试进行剔除。

另一个需要注意的问题是如果光源位与模型最高顶点以下,会产生antishadow,需要在投影前进行剔除。

在这里插入图片描述

Soft Shadows

当光源不仅仅是一个点的时候,会产生软阴影。一种的方法是在光源的范围上放置几个点光源,对于每一个点光源都生成渲染图然后累积到一个缓冲当中,将这些渲染结果求平均值得到的结果就是渲染结果。在实践中这种方式一般是不适用的。

软阴影可以通过生成一系列地面纹理来创建。在光源表面采样,每个采样点渲染一张地面纹理,然后平均求得地面的纹理贴图,这个方法的问题是所有纹理的重叠很多,虽然结果很准确,但是消耗太大。

另一种方法是使用卷积,从一个点开始对硬阴影进行滤波。

Shadows on Curved Surfaces

曲面上使用阴影是通过投射纹理,它有两个缺点:第一,应用程序必须识别出哪些物品是遮挡物体哪些是他们的投影接受物体,投影接受物体距离光源的位置必须必遮挡物体要更远;第二,遮挡物体不能对于自身进行投影。

Shadow Volumes

shadow volume可以通过使用模板缓存来处理,它不是基于图像所以可以避免采样问题。它的缺点是如果对角色服装发生折叠就会产生走样的阴影,并且它的消耗是不可预测的。

shadow volume的示意图:

在这里插入图片描述

当观察场景中的某个物体时,视线进入阴影体时计数器加一,离开阴影体时计数器减一直到视线到达指定pixel,如果计数器大于0则在阴影中,否则不在阴影中。如图所示:

在这里插入图片描述

如果对于物体的每一个三角面都生成一个阴影体会有很大的计算量,一个解决方案是使用物体的剪影轮廓生成阴影体。shadow volume还有另外一个严重的问题:极大地不确定性,例如场景中只有一个三角形,如果摄像机和光源位置重合,计算量将为最小,当观察点绕着三角形旋转时shadow volume将占据屏幕显示比例将越来越大,消耗也越来越大。

Shadow Maps

shadow map是一种常用的阴影生成方式。首先从光源位置出发,渲染获取场景针对于光源的深度值,生成shadow map;之后正常渲染整个场景,每个图元在渲染时,它的每个pixel都转换到光源坐标系中获取shadow map中对应的深度值,如果比获取到的深度值更大,那么它就在阴影中。

在这里插入图片描述

shadow map的消耗是随着投影物体的增加而现行增加的,所以相对来说是可以预测的,不同类型的光源有不同的投影方式。

场景中的物体没必要都渲染到光源的视锥中,例如地面确定只会接受投影而不会产生投影,那么就不需添加到shadow map中。为了包含所有投影物体,光源视锥可以被扩大或者缩小来安全的丢弃一些投影物体。调整光源视锥不仅可以节省渲染的时间,并且可以提高shadow map的分辨率。总的来说对于光源视锥来说近平面离光源越远越好,远平面离光源越近越好。

在这里插入图片描述

shadow map的一个缺点是比较依赖shadow map的分辨率和深度缓存的精度。常见的问题是self-shadow aliasing,导致的原因一个是处理器的精度,另一个是采样的位置与取深度的位置不完全重合,一般来讲深度值会比采样位置小一些。一种常用的解决方式是添加一个偏移量,这个偏移量可以是一个常量,也可以根据平面的斜率来变化(glPolygonOffset),如图所示:

在这里插入图片描述

另一种解决方式称为normal offset bias,根据平面与光线方向的夹角的大小来将平面沿其法线方向移动一段距离,将该平面认为是一个virtual surface。如果偏移量不合适会产生light leak的问题,物体看起来在光之上。

在这里插入图片描述

解决self-shadow的方法是在生成shadow map的时候渲染背面,渲染场景时渲染正面。

在使用shadow map时,物体必须时封闭的或者将正面和背面都渲染到shadow map中,否则物体可能会不产生阴影。

Resolution Enhancement

由于透视原因,离观察点近的位置在shadowmap采样时,多个点会映射到相同的坐标上,所以会产生走样:

在这里插入图片描述

perspective shadow maps、trapezoidal shadow maps、light space perspective shadow maps是使用矩阵变换来改变映射关系,它的好处是只需要修改光源矩阵。《Warping and Partitioning for Low Error Shadow Maps》和《Logarithmic Perspective Shadow Maps》描述了这些方法的优缺点。当观察点与光源相对时,这种方法会失效。

另一种方式是生成shadow maps时,生成一系列不同分辨率的shadow map,将视锥进行分割,不同的部分使用不同的shadow map。一种常用的方式是级联阴影(cascaded shadow map)。

一种分割视锥的方式:

在这里插入图片描述

分割视锥的比例可以使用对数分割:

r = f n c r = \sqrt[c]{\frac{f}{n}} r=cnf

其中 r 和 f r和f rf是场景的远近截面,c是分割数量。比较重要的因素是近平面的设置,如果过大则会裁切掉很多物体。一般的确定方式是使用累积的方式,利用前一帧的z值来决定分割方式。第一种方式是使用GPU支持的一种叫reduce的方式来确定z值得最大与最小值,第二种方式是使用直方图的方式来确定。通常使用第一种方式。

一般来讲,这些分割区域是部分重合的,当一个物体横跨两个区域时,会将两个结果进行混合使用。

在进行阴影计算时,可以使用一个简单的模型作为物体的代理进行shadow map的计算。也可以移除掉小型物体的阴影。较远区域的阴影更新速率会比较低,但是为了防止突然地变化,可以将远距离地区再分区,每一帧更新一部分,或者是直接屏蔽掉移动物体的阴影,只保留静态物体的阴影。

创建多个分离的shadow map需要对于每一个shadow map都遍历一遍几何体,通过将渲染每个遮挡物放到一个单独的pass中来提高效率(原文:A number of approaches to improve efficiency have been built on the idea of rendering occluders to a set of shadow maps in a single pass)。几何着色器可以将物体数据复制到多个view 中。多实例几何着色器可以将物体渲染输入到32深度纹理中。多视口扩展可以将物体渲染到纹理数组的不同切片中。

在实时渲染中可能会有很多光源,如果计算所有光源那么消耗将很高。如果位于视锥中但是看不到的物体,不用加入计算;也可以使用裁剪(Shadow Caster
Culling for Efficient Shadow Mapping)来找到所有可见的阴影接受物体,之后将这些物体渲染到模板缓存中生成一个mask,当生成shadow map时,使用这个mask来移除没有阴影接受物体的遮挡物。由于光线的衰减,在一定阈值之外的阴影可以不计算。

Percentage-Closer Filtering

在使用shadow-map时,如果需要生成半影,可以使用类似于纹理放大的方式,在shadow map上进行采样时,获取采样点周围四个值作为采样结果,对采样值得比较后的结果进行插值,来作为阴影值,这种方式称为percentage-closer filtering(PCF)。为了视线类似于范围光对点光源也产生半影的效果,可以在采样点周围多个点对于点光源进行可见性测试,根据通过测试的比例对阴影进行模糊,如图:

在这里插入图片描述

为了降低半影的走样,可以使用随机旋转的Poission分布模式进行周围点的选取:

在这里插入图片描述

Percentage-Closer Soft Shadows

PCSS提供了一个在shadow map上查找周围区域来确定所有可能的遮挡物的解决方案,距离遮挡物的平均值可以决定采样范围的大小

ω s a m p l e = ω l i g h t d r − d o d r \omega_{sample} = \omega_{light}\frac{d_r - d_o}{d_r} ωsample=ωlightdrdrdo

其中 d r d_r dr是接受阴影物体距离光源的距离, d o d_o do是距离遮挡物距离的平均值。

Filtered Shadow Maps

variance shadow map(VSM)使用统计的方法计算接受者的最大可见度,用该值可以直接模拟半影效果。VSM使用两个缓存,一个保存深度值,一个保存深度平方值,这两者都可以使用一般的滤波方式进行处理。
VSM首先在接受位置进行采样,计算遮挡物体距离光源的平均深度 M 1 M_1 M1,在于接受位置的深度值 t t t比较,如果 M 1 M_1 M1大于 t t t则完全被光照,否则使用公式:

p m a x ( t ) = σ 2 σ 2 + ( t − M 1 ) 2 σ 2 = M 2 − M 1 2 p_{max}(t) = \frac{\sigma^2}{\sigma^2 + (t - M_1)^2} \\ \sigma^2 = M_2 - M_1^2 pmax(t)=σ2+(tM1)2σ2σ2=M2M12

M 2 M_2 M2是从深度平方map中的采样结果。

VSM的一个作用是解决由于表面偏移导致的问题。

详见《Variance Shadow Maps》论文

为了减少漏光问题,convolution shadow map(Convolution Shadow Maps)被提出,但是他的一个缺点是消耗可能比较大,之后exponential shadow map(Exponential Shadow Maps)和exponential variance shadow map(Rendering Filtered Shadows with Exponential Shadow Maps)被提出,它将深度的指数和第二个moment保存到两个缓存中,但是他的问题是第二个moment值可能会变得很大从而超出浮点值的范围,所以为了提升精确度可以使用z-depths(Advanced Soft Shadow Mapping Techniques、Lighting Research at Bungie)来让指数函数下降的更快。级联阴影也可以通过使用滤波来提高精确度(Layered Variance Shadow Maps)

Volumetric Shadow Techniques

自投影:deep shadow maps、opacity shadow maps

Irregular Z-Buffer Shadows

shadow有很多优点比如消耗的可预测性,随着场景的增大而有一个良好的增长幅度,适应GPU的计算方式。但是他也有很多走样的问题。除了这个方法之外,shadow volume也是一种可以被进一步研究的方法,但是目前没有很成熟的商业运用。另一种方式是使用光线追踪,如何动态的变更每一帧需要检测的遮挡物一直是一个热门的话题。第四种方式是利用GPU的光栅化硬件,在shadow map的每个纹素中保留所有覆盖该纹素的三角形列表(Sub-Pixel Shadow Mapping、Shadow Silhouette Maps),这个列表可以用过保留形光栅化(conservative rasterization)来获取,对于覆盖过像素的三角形都产生一个片元,但是可能会导致性能上的问题。

为了替代在shadow map中保存遮挡物的数据来判定接受物体的阴影情况的方法是保存接受物体的数据然后跟遮挡物体进行比较来计算阴影,这个方法被称为irregular z-buffer(The Irregular Z-Buffer, IZB)。这个方法不是改变缓存的形状,而是保存的内容是不规则的:每个纹素中可能包含一个或多个接受位置或者没有。

在这里插入图片描述

Other Applications

Screen-space shadows可以提高阴影精确度
使用三维shadow map(voxel-based shadow)

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值