tangoyzx
论坛版主
论坛版主
  • UID343
  • 粉丝5
  • 关注0
  • 发帖数10
阅读:1020回复:0

(译)SSAO教程

楼主#
更多 发布于:2015-12-02 17:25
SSAO教程

原文地址:http://john-chapman-graphics.blogspot.co.uk/2013/01/ssao-tutorial.html(需翻墙)




背景

        环境遮蔽是表面上一个点被周围几何体遮挡的量的近似值,影响着这个点的入射光到达程度。环境遮蔽技术有效地模拟比较接近的阴影-你在房间角落的阴影或者物品间细小空间中的阴影。环境遮蔽一般都很微妙,但是可以显著提升CG场景的真实性。

图片:image2015-11-25 17-26-11.png


        核心是计算表面上每个点的遮蔽系数并且合并到光照模型中,通常通过调整环境光参数来达到遮蔽多光少以及遮蔽少光多的效果。计算遮蔽系数可能很昂贵;离线渲染一般通过发射大量的射线去碰撞以法线为准的半球空间中,去对一个点附近的遮蔽几何体进行采样。这对实时渲染而言并不实际。
        为了实现可以互动的帧率,计算遮蔽系数需要尽可能地优化。其中一个选项是预计算,但是这限制了场景的动态性(光可以移动,但是几何体不可以。)
        时间回到2007年,Crytek为Crysis实现了一个实时的方案,并迅速成为了游戏图形学的标准方案。这个方法很简单:用逐像素深度信息当做场景几何体的近似,并且在屏幕空间计算遮蔽系数。这意味着整个过程都可以在GPU上进行,100%动态并且与场景复杂度无关。我们来快速看看Crysis的方法是如何工作的,然后对其进行些优化。


Crysis方法

        Crysis并没有用射线在半球中检测,而是在深度缓冲的球体中进行取样。

图片:5.png


        通过下面的方式生效:
l  投影每个采样点到屏幕空间并且获得深度缓冲中的坐标
l  对深度缓冲采样
l  假如采样点的坐标在采样深度的后面,那么这个点就对遮蔽系数有贡献
很明显结果的质量与采样点的数量成正比,所以需要尽量减少采样点来到达合适的性能。但是减少采样点会产生非常丑的“带状”伪影。这个问题可以通过随机旋转每个像素采样核来不就,并且通过模糊来去除高频噪声造成的“带状”伪影。

图片:3.png


        Crysis方法生成的遮蔽系数伴随着一个特定的视觉效果-因为采样核心是个球体,平的墙壁看起来会变灰因为有50%的采样点在周围的几何体里面。凹的角落会比想象中更黑,但是凸出的角落会更亮因为只有很少的采样点落在几何体里面。尽管这些问题视觉上都还可以接受,但是还是生成了一个稍微偏离现实效果的显示效果。


基于法线的半球

        与其对每个像素都进行球体核的取样,我们可以在半球体上取样,基于该像素平面上的法线。这么做通过获取逐像素法线来提升了视觉效果。对于延迟渲染,这是可行的,所以消耗是最小的(特别对结果的提升)。
生成采样核

        第一步就是要生成采样核:
l  在单位半球体中采样
l  (samples position are more densely clustered towards the origin)这有效地根据与核中心的距离计算遮蔽贡献——接近点的采样点比远的采样点贡献更多遮蔽值。
生成半圆很容易的:

图片:7.png


这样生成的采样点在平面上的基于z轴的半球体。选择的方向是任意的——它只会影响我们在shader中重定向核。下一步就是缩放每一个采样点让它们可以分散到半球体中。最简单的做法是:

图片:15.png


这样会生成一个均匀的点的集合。我们真正想要的是随着跟原点距离增加而增加的点,根据下面的曲线:

图片:5.png


我们可以用一个加速插值的函数来达到:

图片:6.png




生成噪声贴图

        下一步我们需要创造一个随机值的集合用于旋转采样核,这样可以有效地增加采样数并且减少前文所说的“带状”伪影。

图片:7.png


        注意z是0,因为我们的核是基于z轴的,我们想要围绕着z轴的随机旋转。
        这些随机值会放在贴图上并且平铺到屏幕上。纹理平铺会令核的方向会重复,并且令到结果会有规律性。通过保持小贴图,我们可以令这种规律高频率地发生,那就可以通过模糊来除去并且保留图像的低频细节。使用4x4的贴图以及模糊核可以以最小的消耗产生一个很好的效果。这就是Crysis用的方法。


SSAO着色器

        所有前置工作做完以后,我们来到了实现的核心:着色器本身。这里有两个通道:计算遮蔽系数,并且对结果进行模糊
        计算遮蔽系数需要获得像素点的view space的坐标和向量。

图片:8.png


我们倒推view space坐标通过像素点的线性深度以及插值的vViewRay。可以看看Matt Pettineo’s blog里面对用深度重建坐标的其他方法的讨论。最重要的东西是origin最后是像素点在view space的坐标。
获取像素点的法线就比较直接:规模/偏差以及归一化步骤是必须的,除非你使用一些高精度的格式保存法线。

图片:9.png


下一步我们需要沿着原点法线构造一个change-of-basis矩阵去重新调整我们的采样核。这里我们可以巧妙地使用随机旋转:

图片:16.png


第一行通过噪声贴图获得一个随机向量rvec。uNoiseScale是个vec2,用来缩放vTexcoord去平铺噪声贴图。所以加入我们渲染目标是1024x768并且我们的噪声贴图是4x4,uNoiseScale会是(1024/4,768/4)。(这可以计算一次,并且在初始化噪声贴图的时候当做常量传入)
下面三行使用了Gram-Schmidt processs来计算一个正交基,结合我们的随机旋转向量rvec。
最后一行通过tangent,bitangent和normal向量构造了一个变换矩阵。Normal向量放在了矩阵的z中因为基本核是沿着那个轴的。
下一步我们循环采样核(传进一个vec3的数组,uSampleKernel),对深度缓冲采样并且计算遮蔽系数。

图片:11.png


获取view space的采样点很简单:我们乘以我们的转向矩阵tbn,然后以uRadius缩放这个取样点(一个很好的美术效果调整的系数,把它作为常量传入),然后加上像素view space坐标点origin。
我们现在需要投影sample(在view space中)到屏幕空间中去获得贴图坐标来对缓冲贴图取样。这个步骤有一个一般的过程——乘以现在的投影矩阵(uProjectionMat),然后除以w并且进行缩放和偏移以获得我们贴图的坐标:offset.xy。
下一步我们从深度缓冲(uTexLinearDepth)读取sampleDepth。假如它在采样点前面,那么这个采样点就是在几何体里面并且贡献到遮蔽值。假如sampleDepth在采样点后面,那么采样点对遮蔽系数就没有共享。引入rangeCheck帮助放置大深度不连续的错误遮蔽。

图片:12.png


如上图可见,rangeCheck会把采样半径范围外的遮蔽值设为0。
最后一步是对遮蔽值归一化并且反转它,为了产生一个可以直接控制光照贡献的值。

图片:17.png




模糊着色器

        模糊着色器就很简单:所有我们想做的就是平均4x4矩形里面的每个像素点的从而去除4x4的噪声。
       

图片:13.png


        这个着色器里面唯一需要注意的就是uTexelSize,允许我们准确地以目标像素为中心进行采样。

图片:14.png

最新喜欢:

归海一啸归海一啸

欢迎分享

游客

返回顶部