流逝的时光
总有一天我们都会离去 email: zzxhang@gmail.com
posts - 21,comments - 111,trackbacks - 0

                                                  

注:翻译只求意思准确,省略了一些不太相关的内容,如有错误烦请指正,原文:http://developer.download.nvidia.com/whitepapers/2007/SDK10/VarianceShadowMapping.pdf

                                     Variance Shadow Mapping

                                           Author:Kevin Myers kmyers@nvidia.com

                                          译:清风 http://www.cppblog.com/zzxhang/

 

Motivation:

     在Dx10级别的硬件下VSM能被完美支持,如果不能对fp32纹理过滤,算法就只能在fp16精度下完成,这是不够的,会让计算在数值上变得不稳定,此外当渲染阴影图时还可以打开multisample anti-aliasing(MSAA)改善生成的阴影图质量

 

How Does It Work?

    Shadow maps首先在光源视角上把场景的深度信息渲染到一张纹理中去。完成后这张纹理就包含了所有最靠近光源的像素片段。之后渲染场景时我们就可以查询这张纹理用于判断当前进行着色的像素片段是不是被阴影图上的像素片段遮挡了。通过Percentage-closer filtering(PCF)进行一定数量的纹理查询可以得到软阴影。

    PFC的问题在于没有一个好的方法能对深度值进行预采样,如果你武断地对深度图进行一些特定的采样方法,如anisotropic filtering或者mip-mapping,那么你得到的深度值就不是一个正确的遮挡值,而是好几个可能的遮挡值的平均值,用这个值来进行判断就会造成图像失真。

 

Summary of the Algorithm:

    你可以在由William Donnelly 和 Andrew Lauritzen完成的最原始的VSM论文http://www.punkuser.net/vsm/中看到细致的算法理论阐述。

    简单地说,VSM的作者在研究中进行PCF得到的是在PCF采样区域中深度值大于我们当前渲染像素深度的像素的百分比的一个边界值,换句话说我们在拿一个单独的深度值和采样区域的整体分布进行比较,我们想知道的是在这个区域中有多少百分比的深度值是大于这个单独的深度值的,我们只关心采样区域深度分布的百分率而不是单独的采样值,通过切比雪夫不等式,我们能用平均值(或者说期望E(x))和分布的方差求出这个百分比的边界值。


    

   平均值是很容易通过深度图得到的,我们可以通过mip-mapping或者separable blur在shader中过滤得到,方差也一样很简单,通过平均值E(x)和平方的平均值E(x2)就能算出来:

就象我们之前说的那样,平均值是很容易得到的,而平方的平均值我们可以只需要把深度图的格式改为双通道纹理并把深度的平均值渲染进第二个通道。在照亮场景前我们仍然可以随意进行一些过滤,之后简单的一次纹理查询就可以计算出平均值和方差。

VSM Shader:

这是在DrawScene.fx的shader代码:

float2 VSM_FILTER( float2 moments, float fragDepth ) 

                  

                          float2 lit = (float2)0.0f; 

                          float E_x2 = moments.y; 

                          float Ex_2 = moments.x * moments.x; 

                          float variance = E_x2 - Ex_2; 

                          float mD = moments.x - fragDepth; 

                          float mD_2 = mD * mD; 

                          float p = variance / (variance + mD_2); 

                          lit.x = max( p, fragDepth <= moments.x ); 

 

                          … 

                          return lit; //lit.x == VSM calculation 

                  }


    首先注意最后的max语句,如果当前片段的深度值小于或者等于平均深度值,即t<=E(x),那么方程1已经不适用了,图2展示了如果我们不做t >= E(x)的限制从切比雪夫方程中得到的结果,注意在场景中模糊区域是怎样从被照亮区域到阴影区域的,如果你对此有困惑,先想想导致方差改变的因素是什么,分布区域包含值域的范围越广,方差的值就越大,深度值范围的剧烈变化(如轮廓区域)会使得方差的值变得比较大,在图3中你能更清楚地看到这一点,轮廓区域是方差迅速增加的地方,回头看我们的方程这些区域会使得我们方程的结果趋向于1,在轮廓的另一边方差会迅速减少,此时影响方程值继续增大的因素就是另一个变量了(t – E(x)),在图4中这个变量的值用绿色来形象化显示,注意跟阴影区域相反的颜色变化是怎么发生的,当靠近轮廓边缘时采样的深度值越来越接近像素片段深度值,(t – E(x))迅速减少。

  
       
         
      

Performance:

   当加上separable blur后VSM的效果真的很炫,这是用1024x1024的VSM在GeForce 8800 GTX上用1024 x 768的分辨率跑例子程序的结果:

   

Integration:

   把已经在用shadow map的程序改成用VSM还是比较简单的,首先深度图必须变为R32G32F格式的纹理用来存深度值和深度值的平方,当写深度进纹理时我们不必使用投影后的深度值,直接把像素点和光源的线性距离作为深度值更好。最后在shader中必须把原先的光照算法改成使用VSM中的数学方法计算阴影。

   当深度复杂性很高的时候VSM存在光照溢出(light bleeding)的错误,最初的VSM论文中证明了当遮挡体和接收体都比较平坦的时候VSM算法能计算得很正确,但如果情况不是那样,剧烈的方差变化可能会导致方程返回一个错误的结果,照亮了不该照亮的地方。

   精度也是一个需要控制的问题,就算使用fp32的精度仍然可能不够因为我们要存贮深度的平方值,在例子程序你可以通过改变光源距离(滑块控制)看到这个问题,当光源距离增加时遮挡体的深度值也越来越大,然后我们深度值的平方很快就遇到了精度问题。通过改变深度值的缩放值能一定程度上减轻这个问题,实际上在大多数场景中都可以通过把光源视锥体的大小限制在一定范围内控制这个问题。

    由于在VSM中处理的是Z值的分布而不是一个简单的深度测试,因此VSM的一个改进是不再需要z-offset来避免Z值过于靠近而产生的Z-fighting问题(译者:但是仍然需要一个varance-offset…)

 



译者: 本人把dx sample中的shadow map改成了使用VSM,1024 X 1024的shadow map,3 x 3 PCF,下面是VSM版和原版的效果图对比(为了突出阴影做了些改动):

dx sample:


vsm:

posted on 2009-10-20 22:52 清風 阅读(4682) 评论(8)  编辑 收藏 引用 所属分类: 图形

FeedBack:
# re: Variance Shadow Mapping(译)
2009-10-21 17:06 | tiny
good job!!  回复  更多评论
  
# re: Variance Shadow Mapping(译)[未登录]
2009-11-23 09:34 | 董波
谢谢楼主,哈哈。  回复  更多评论
  
# re: Variance Shadow Mapping(译)
2010-05-19 13:29 | 丁小饼
冒昧的问一下,最近在钻研D3D;能给我看看你写的vsm 效果文件么?  回复  更多评论
  
# re: Variance Shadow Mapping(译)[未登录]
2010-05-19 20:06 | 清风
@丁小饼
现在已经找不到了,不好意思,你可以从这里http://www.punkuser.net/vsm 下载vsm的demo  回复  更多评论
  
# re: Variance Shadow Mapping(译)
2011-08-15 15:41 | fangsunjian
能解释一下地面上出现的条纹是怎么回事么?
filter改成0或者不进行blur直接输出阴影。
可以看得很清楚。
在一些极端的情况下条纹会变得相当明显。如何解决?  回复  更多评论
  
# re: Variance Shadow Mapping(译)
2014-03-15 10:46 | OOO
非常感謝這些資料,最近一直想改進 Imperfect Shadow Map 這類大量廉價 Shadow Map 的陰影品質,翻一翻剛好找到這篇,直接實做看看,雖然 VSM 品質一樣沒很好,但是對 ISM 這種以數量取勝的作法來說,VSM 效能剛好夠快能搭配

自己弄得 http://youtu.be/Xej6WeWDBBM
Light Bleeding 嚴重到靠杯=.=...  回复  更多评论
  
# re: Variance Shadow Mapping(译)
2014-03-16 18:17 | OOO
原本以為是 Light Bleeding 的現象,
結果是 FLOAT 經度不足...
加到 32BIT 之後就正常了@@  回复  更多评论
  
# re: Variance Shadow Mapping(译)
2015-03-31 10:56 | 向宇波
大神,能指点一下
“首先深度图必须变为R32G32F格式的纹理用来存深度值和深度值的平方”
这句话吗,我写vsm就在这个地方卡住了,求代码示例一下  回复  更多评论
  

只有注册用户登录后才能发表评论。
【推荐】超50万行VC++源码: 大型组态工控、电力仿真CAD与GIS源码库
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理