« 软阴影的实现尝试Ⅰ显示本站所有图片 »

软阴影的实现尝试Ⅱ

软阴影与反锯齿有着深刻的关系,PCF在Graphics上原本就是一种反锯齿技术,而它在阴影算法上往往作为简捷的软阴影达成途径。VSM(Variance Shadow Map)同样旨在解决这一问题,但它并非像以往一样对阴影图的后处理,而是重新定义阴影图。——ZwqXin.com

本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/try-soft-shadow-2.html

1. 锯齿与反锯齿

锯齿感是在Casting的时候产生的。我们确实是相当于把一张阴影图(深度图)贴在场景上了,但具体的操作依然是一条深度比较指令。譬如,传统的SM中,根据场景物体像素在光源视觉下的深度得到的阴影图,在Casting阶段对应相机视觉中的像素判断是否属于阴影图中的像素——这本来就是一个TRUE OR FALSE的判断,若TRUE证明非阴影,像素颜色值不变;若FALSE则涂黑,以表明该处阴影。

NewValue = OldValue * (isShadow? 0 : 1)

即使把0替换成shadowMapValue或是运用距离值判断,也改变不了这里的根源:把视场景分割成阴影区和非阴影区。双方强烈的撕扯总会造成霹雳的裂痕。我们一般使用的纹理在贴在奇形怪状的表面上时产生的扭曲同样会导致锯齿,可为什么却不那么难接受呢?原因是filtering。初学纹理贴图的时候,一定会接触到Texture Filtering,尤其在发生欠采样和过采样的时候,纹理映射的策略。在OpenGL 1.x中,GL_LINEAR和GL_NEAREST应该是最为熟悉的,尤其是线性插值GL_LINEAR,让我们贴纹理的时候能得到比较好的性效比:

  1. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
  2. glTexParameteri(GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

贴纹理过程的采样能通过简单的线性插值完成,再把插出来的值映射到恰当的位置去——想想对于深度图这会是什么情况:低分辨率的阴影图上一个4X4格子,对应场景中9X9大小区域,恰好区域覆盖阴影边界,深度插值后某像素获得了表示“半影”的灰度数值。但是要“贴”到场景时,需要经过一个TRUE OR FALSE过滤器,这个“半影”像素依然要选择:自己究竟属于影子,还是非影子。一旦整个边界的像素选择完成,由于阴影判断式对所有像素等效,它们会发现自己“整齐”地划出了一条边界线,锯齿边界线。

那么,拯救这条边界线最直接的方法也可看出:让这些“半影”像素随机地选择自己的所属。这样就会产生不整齐、不和谐——这正是半影所需要的。在GPU GEMS 2里就介绍了这样一种做法,效果应该会很好关键是效率,3D纹理的使用就不是那么让人愿意接近了(不过以后我或许要尝试一下哈)。那么,还有别的拯救方法吗?

2. Variance Shadow Map

VSM(Variance Shadow Map)的提出正基于此。其实它本质也是一种基于概率的方法,把以上的TRUE OR FALSE过滤器过程改作一个可见性估计过程……嘛,你也知道概率学上的东西有很多微妙的地方……让texture filtering作用于每个像素的方差(Variance)而非其本身的值,于此建立的切比雪夫不等式作为这个可见性估计式。ZwqXin本身概率论学得不深,为了避免误人子弟,这许概率数学原理我就PASS了。

每个像素经过这样折腾一下,取切比雪夫不等式的上限(UpperBound,即上式pmax(t))为结果,它大概揭示了当前像素属于“非阴影区”的程度(当这个UpperBound越接近0时代表此像素越可能“属于阴影”)。UpperBound计算式的三个参数,t是像素的实际深度(根据GPU GEMS3,这里取的是像素距离光源的距离distToLight),μ是当前像素深度的期望E(x)(取的是直接在阴影图上采样得到的[插值后的]深度,好吧,囧),σ2是方差(σ2 = E(x2)- [E(x)]2)。

首先查查什么是“切比雪夫不等式”( Chebyshev's inequality )。是的,上过概率课,所以至少听过这个名词(听说,那是因为期末考后潜意识对自身的遗忘魔法):in any data sample or probability distribution, "nearly all" the values are "close to" the mean value — the precise statement being that no more than 1/k2 of the distribution's values can be more than k standard deviationsaway from the mean. [WIKI]。

上面式子应该是另一种表达形式吧(吧?)。在我看来,切比雪夫不等式的上限是关于 t的函数式,描述了一个具体的量t在随机集合X上的位置的概率范围。Casting过程中,一个像素往深度图Shadow Map上采样,姑且认为这是一个随机选择的过程;像素若是根据其纹理坐标(光源投影矩阵合理所得)进行的采样,纹理上应该只有唯一的一个点与之对应——当然,即使取隔壁的点也不是不可,不过它作为准确值的概率没有那个“唯一的点”大——把可能取的值作为集合X,那X就是一个以“唯一的点”为中心的圆,使该点作为集合的期望值μ。(期望的计算式子E(X) = x1 * p(x1) + x2 * p(x2) + ……,稍微考虑一下就明白)。问题是t,它作为实际值,在我们的纹理采样过程中是不可能出现的,甚至我觉得它根本无法得出,为什么取“像素距离光源的距离distToLight”呢?它的计算不存在于纹理采样过程中,但也不可能就说它就是“真实值”啊——哪怕表义如此。对此,可看看GPU GEMS3 8.1中作者的解释,不过我是没有太理解而已(尽管没有更好的)。

于是,既然摆脱的TRUE OR FALSE的抑制,我们完全可以动用texture filtering去取那个“唯一的点”——期望值E(x)]了。这不,为了计算方差,我们还需要一个E(x2)——类似地,我们只需要在ShadowMap Generating阶段把像素深度值的平方也保存起来就可以了——这就是Variance Shadow Map,需要一张纹理的两个通道,分别存储深度和深度平方。CASTING阶段把各值传入切比雪夫不等式计算上限值——这不是确切的“属于非阴影区的概率”,但它是唯一明确能得到的值,这个不等式误差是VSM致命弱点的根源。这个上限值概率作为“阴影因子”乘在场景像素上,造出了肉眼可见的“半影”区,同时大大地增强了阴影的边缘的轮廓感(难看的锯齿减少)。

http://www.zwqxin.com  Soft Shadow
(CSM一般效果,没加PCF模糊,注意看的是阴影外形)
http://www.zwqxin.com  Soft Shadow
(对比。CSM+VSM 可见没有了突兀的轮廓,filter的效果显著)


http://www.zwqxin.com  Soft Shadowhttp://www.zwqxin.com  Soft Shadow
(对比。没有VSM效果时,远观时(splitNum = 4)轮廓已经很糟糕了,但VSM下能显示完好轮廓)

 本文来源于 ZwqXin (http://www.zwqxin.com/), 转载请注明
      原文地址:http://www.zwqxin.com/archives/opengl/try-soft-shadow-2.html

  • quote 1.康夫
  • 你绝对是个人才,佩服!以后会经常拜读你的文章!
  • 2010-4-8 0:10:06 回复该留言
  • quote 3.blw
  • 请问作者有源代码可以参考下么?我现在遇到好多问题
  • 2012-8-9 9:36:40 回复该留言

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

IE下本页面显示有问题?

→点击地址栏右侧【兼容视图】←

日历

Search

网站分类

最新评论及回复

最近发表

Powered By Z-Blog 1.8 Walle Build 100427

Copyright 2008-2013 ZwqXin. All Rights Reserved. Theme edited from ipati.