APP下载

软阴影算法及实现

2010-07-07曾晓一何援军

图学学报 2010年4期
关键词:走样像素点阴影

曾晓一, 何援军

(上海交通大学计算机科学与工程系,上海 200240)

在现实世界中,阴影无处不在。阴影提供了光源对物体的照射信息,它增加了人们理解三维场景的深度[1]。对于阴影的生成,目前人们已经做许多研究,现有的阴影算法分为两大类:硬阴影算法和软阴影算法[2]。所谓硬阴影算法是指场景中的点只有在阴影中或在阴影之外两种状态,这种阴影是假设在只有理论上的“点光源”照射下的。但在现实场景中光源是有一定尺寸的,这使得场景中点有可能接受到部分的光源。在软阴影算法中,定义一个点位于本影中,如果光源射向它的光线被全部遮挡了;否则,如果只有部分被遮挡,则这个点位于半影中。软阴影比硬阴影有更多的真实感,但也更难以实现。如何有效简便地生成具有真实感的实时软阴影[3]是目前研究的焦点,也是本文要解决的问题。

1 前人工作

目前两种流行的阴影生成算法是阴影体(Shadow Volumes)[4-5]和阴影映射算法(Shadow Mapping)[6-7]。

1.1 阴影体算法

Crow(1977)[4]提出的阴影体算法从光源位置将遮挡物的轮廓线沿着光源方向延伸,形成一个筒形的半无穷区域,称这个区域为阴影体,处在阴影体内的点绘制为阴影。使用模板缓冲能够很方便地实现该算法。该算法生成的是精确的硬阴影,不会产生锯齿现象,但是对场景中几何对象有限制,非常依赖几何形体,没有很好的鲁棒性。

1.2 阴影映射算法

相对于阴影体算法,Lance Williams(1978)[6]提出的阴影映射算法是一种基于图像采样技术的阴影生成算法,对场景中的几何物体没有特别的要求,但却存在比较严重的走样现象[8]。

普通的阴影映射算法需要两步绘制过程:

(1)以光源位置为视点计算画面 将视图上各像素点的深度值保存在深度缓冲纹理中,即“阴影映射图”中。

(2)回到观察者视点位置计算画面 使用上一步得到的深度纹理来进行阴影测试,通过比较给定点在光源方向视图下的深度值与该点在“阴影映射图”中的深度值关系来判断该点是否位于阴影中。如果给定点和光源之间没有任何遮挡物体,则该给定点在光源方向视图下的深度值与该点在“阴影映射图”中的深度值应该相等,则判断该点不在阴影中;反之则判断该点在阴影中。

这样的两步计算过程会由于“阴影映射图”的精度和采样问题使生成的阴影出现严重的走样现象。

2 软阴影算法

2.1 算法步骤

为了解决普通阴影映射算法的走样问题,本文提出的阴影算法并不是直接通过比较给定点在光源方向视图下的深度值与该点在“阴影映射图”中的深度来直接决定一个点是否在阴影中,而是结合给定点周围点的信息来决定此点是否在阴影中。因此,在普通算法第二步前,并不直接利用阴影映射图将场景绘制到屏幕,而是使用阴影映射图先将场景绘制到一个并不立即显示的屏幕缓冲纹理中,然后对此纹理进行一些处理,最后再利用此处理后屏幕缓冲纹理的将场景绘制到屏幕。

本文提出的软阴影算法包含下列步骤:

(1)以光源位置为视点,将场景的深度信息渲染到浮点格式的纹理中,生成阴影映射图。

(2)回到观察点,关闭所有光源,利用阴影映射图将场景的阴影信息渲染到屏幕缓冲纹理中。

(3)对上一步中得到的屏幕缓冲纹理进行反走样处理和模糊处理。

(4)打开所有光源,利用处理后的屏幕缓冲纹理将场景渲染到屏幕。

2.2 生成阴影映射图

算法第一步和普通的阴影映射算法一样,首先创建一个浮点格式的纹理作为render target,然后以光源位置为视点,将场景的深度信息渲染到这个纹理中,生成阴影映射图(Shadow map)。由于深度信息值的范围很大,因此创建一个 R32F浮点格式类型的纹理来保存阴影映射图。

2.3 将场景的阴影信息渲染到屏幕纹理缓冲中

算法第二步和普通的阴影映射算法的基本原理是一样的,首先通过深度的比较来判断每个像素点是否在阴影中,得到该点的阴影系数。不同的是并没有直接利用这阴影系数将场景渲染到屏幕,而是将场景中每个像素的阴影系数信息渲染到一个并不立即显示的纹理缓冲中。屏幕上的像素点如果在阴影中,则将这点的阴影系数设置成0.1,如果不在则设置成1.0。这个带有阴影信息的纹理将在下一步进行处理。

2.4 对带阴影信息的屏幕缓冲纹理进行反走样和模糊处理

为了得到比较真实的软阴影效果,本算法第三步将对上一步得到的纹理进行反走样和模糊处理。处理反走样,本算法采用percentage closer filtering (PCF)来平滑锯齿边缘,即简单的对每个点周围8个纹理点进行采样,并取得它们阴影系数的平均值作为该点的阴影系数。对于模糊处理,本算法使用Gaussian filter高斯滤波来模糊屏幕缓冲纹理,即对每个像素点周围的点进行采样,并用这些采样点的高斯加权均值来设置该点的值。

2.5 渲染带阴影的场景

利用处理后的屏幕缓冲纹理,将场景渲染到屏幕,由于这时的屏幕缓冲纹理已经经过了前面一步的反走样和模糊处理,最后将得到比较柔和的阴影效果。

3 实现中的几个关键问题

3.1 阴影效果的绘制

在最后场景的绘制中,本算法使用了如下局部光照模型

Id为漫反射光强,Ia为环境光光强,Is为镜面发射光强,fShadowTerm为阴影系数,如果像素点在阴影中则fShadowTerm为0,不在则为1。但在实验中发现,按照上面的公式进行绘制将导致那些处于阴影中的像素全黑,这与真实世界并不一致。在真实世界中,由于环境光和周围物体的散射,处于阴影中的像素并没有完全被遮蔽,因此为了得到比较真实的阴影效果,本算法将光照模型做了如下调整:

并对阴影系数fShadowTerm的设置做了些调整,如果像素点在阴影中则fShadowTerm为0.1,不在则为1.0。

3.2 精度问题

因为阴影映射图中存储的深度值是精度有限的浮点数,所以存储在阴影映射图中的各像素点的深度值可能和该点的实际深度值有误差。这个误差会影响到算法第二步判断该点是否是阴影点。如图1所示,对于茶壶上和基面上的一些本不该处在阴影中的点,这些点和光源之间没有任何遮挡物体,所以这些点在光源方向视图下的深度值与它们在“阴影映射图”中的深度值应该相等,则在算法第二步进行深度比较的时候应该判断这些点不在阴影中。但由于存储在阴影映射图中的深度值只是一个有限精度的浮点数,造成将实际上相等的两个深度值判断成不等,从而将这些点错误地判断为阴影点,即形成了如图1所示的波纹。

图1 精度问题

为解决这个问题,本算法在第二步比较深度值的时候,减去了一个很小的浮点数SHADOW_EPSILON,实验表明将SHADOW_EPSILON设置成 0.001f是比较合适的值。

3.3 阴影边缘反走样

阴影映射算法还和所有纹理相关技术一样,面临着共同问题:走样。走样问题将造成阴影边界出现锯齿的现象[9]。具体分析原因,阴影图是在光源视图中生成的,它生成过程实际上是一个从光源对场景逐像素采样的过程,它把场景中的每个采样点的深度信息作为一个像素保存在图中,当视平面中的像素转换到阴影表中进行比较时,肯定会出现转换过去的像素比阴影图的像素要大些或者小些的现象。如果大了,可以平均它所对应的阴影图的像素值来进行比较,确定一个适当的值;如果小些,那么几个在视平面的像素会对应同一个在阴影图中的像素,那就造成多个像素获得同样的比较结果,这样就造成了阴影图的采样精度低于视平面的精度而出现的走样问题。图3显示了使用128*128,256*256,512*512不同精确度阴影映射图的阴影效果,可以看出走样问题会随着阴影映射图精确度的提高而减小。但是这个代价是非常高的,尤其是在实时动态场景中,内存空间是非常有限的。

除了提高阴影映射图的分辨率,本算法还在第三步对阴影映射图做了 percentage closer filtering (PCF)处理,来平滑锯齿边缘。

3.4 模糊处理

为了得到比较柔和的软阴影,算法第四步使用Gaussian filter高斯滤波来模糊屏幕缓冲纹理,即用像素点周围点的高斯加权均值来设置该点的值。实验中使用了两种方法来处理:第一种是对每个像素点左右的各7个像素点,共15个点进行采样,并依照一维高斯分布来设置采样点的权值,即对阴影图横向模糊处理一遍,然后同样对每个像素点上下的各7个像素点,共15个点进行采样,即纵向模糊处理一遍;第二种是对每个像素点左右上下的7*7的方格中共49个点进行采样,并依照二维高斯分布来设置采样点的权值。实验显示,这两种策略的效果差不多,但第二种方法的计算量相对较大,而且也不利于shader程序的编写,所以本算法使用第一种方法,即横纵两遍模糊处理。

4 实验结果与分析

本算法的实现平台为DirectX9.0 VS2005,实验机器配置为:CPU AMD Athlon 64 Dual Core 2.21 GHz ,内存 1.00 GB,显卡 NVIDIA GeForce7500 LE,操作系统Windows XP。

实验场景包含两个物体和一个光源,两物体为一个正四边型的基面和一个茶壶,光源是方向为(-1.0f,-0.8f,0.0f )的方向光源。

首先以光源位置为视点,将场景的深度信息渲染到浮点格式的纹理中,生成的如图2所示的阴影映射图。灰度越小(颜色越深),表示该像素点的深度越小;灰度越大(颜色越浅),该像素点深度的越大。

图2 场景的阴影映射图

图3 采用不同分辨率的阴影效果图

图4 三种阴影效果图比较

使用图2的512*512分辨率的阴影映射图,绘制得到如图4所示的三幅阴影图。第一幅为普通的硬阴影图,第二幅为经过 percentage closer filtering (PCF) 边缘锯齿平滑处理后的阴影图,最后一幅为经过高斯模糊处理后的软阴影图。

从第一幅图中可以看到,虽然已经使用了高分辨率的阴影映射图,但阴影边缘还是有锯齿现象,可见仅靠提高分辨率是无法完全消除锯齿的。第二幅图做了PCF处理,锯齿现象明显有所改善。最后一幅图通过模糊化处理,阴影已经比较柔和了。

以上三种阴影的FPS(帧/每秒),见表1所示。

表1 三种阴影的FPS(帧/每秒)

可见本算法实现的软阴影有较好的实时性。

5 结 论

基于阴影映射图算法,提出了一个有效的实时软阴影算法。本算法采用了 percentage closer filtering (PCF)来平滑锯齿边缘,有效的消除了阴影边缘的走样现象,并使用高斯模糊处理得到了较好的软阴影效果。实验结果表明,本算法不仅简单实用,利于编程,并且也有较好的实时性,可适应实时动态变化场景的需求。

本算法在处理走样问题上仍有可改进的地方,在生成阴影映射图的时候,只需要在可能产生锯齿的地方调高采样率,比如阴影边缘提高采样率,这样可以减少阴影映射图的内存开销。

[1]何援军. 计算机图形学[M]. 北京: 机械工业出版社,2006. 197-198.

[2]Andrew Woo, Pierre Poulin, and Alain Fournier A. A survey of shadow algorithms [J]. IEEE CG&A, 1990,10(6): 13-31.

[3]Jean-Marc Hasenfratz, Marc Lapierre, Nicolas Holzschuch. A survey of real-time soft shadows algorithms [J]. Computer Graphics, 2003, 22(4):753-774.

[4]Crow F. Shadow algorithms for computer graphics [J].Computer Graphics (Proc. SIGGRAPH), 1977, 11(3):2-7.

[5]Bergeron P. A general version of Crow’s shadow volumes [J]. IEEE Computer Graphics& Applications,1986, 6(9): 17-28.

[6]Lance Williams. Casting curved shadows on curved surfaces [C]//Proceedings of SIGGRAPH’78, 1978:270-274.

[7]Lokovic T, Veach E. Deep shadow maps [M]. Pixar.Acm Siggraph, Addison-Wesley, 2000. 2-8.

[8]HOURCADE J C, NICOLAS A. Algorithms for antialiased cast shadows [J]. Computer Graphics,1985, 9(3): 259-265.

[9]REEVES W T, SALESIN D H, COOK R L.Rendering antialiased shadows with depth maps [C]//Computer Graphics (Proceedings of ACM SIGGRAPH 87), 1987: 283-291.

猜你喜欢

走样像素点阴影
“双减”,如何确保落地实施不走样
基于局部相似性的特征匹配筛选算法
你来了,草就没有了阴影
基于G-Buffer的深度学习反走样算法
唐氏综合征是因为“拷贝”走样了
基于5×5邻域像素点相关性的划痕修复算法
基于canvas的前端数据加密
让光“驱走”阴影
基于逐像素点深度卷积网络分割模型的上皮和间质组织分割