TSSAO Temporal Screen-Space Ambient Occlusion (Unity3d 5 示例实现)

前提

环境光(ambient occlusion)是一种GI,其简化形式SSAO可以用“微量高效”来形容,消耗得很少,得到的效果很好。

境光遮蔽(ambient
occlusion)的本质是计算在一个点的半球面范围内有多少方向被阻塞(如下图1.2.1),然后根据它调整表面颜色。如果实时渲染使用的话非常消
耗,所以在游戏中一般都使用(screen-space ambient occlusion)SSAO。SSAO使用depth
buffer来近似一个离散的场景(如下图1.2.2),从而获得固定的开销,而且实现起来比较简单。但是由于SSAO采样数目小,所以产生的效果也不理
想,很容易产生noise。在物体移动时noise会更明显。而且SSAO是low-frequency低分辨率的。所以我们使用时间相干性
(temporal coherence)的SSAO来解决这个问题。


向二次投影技术(reverse
reprojection)让我们重用上一帧的像素点并且随时间优化精制它们。这可以保持每帧采样点的数量较少,并在短时间有效的累加到上百个采样点。所
以时间相干性很适用于AO。并且与光源和视点无关,AO只与局部邻域(local
neighborhood)的几何结构相关,SSAO与像素邻域的场景结构有关。本文主要讲解在延迟渲染戏下使用二次投影技术(reverse
reprojection)提升SSAO的质量。

AO(Ambient Occlusion)

从物理的角度来看,AO时sky light 产生的漫反射。计算公式如下:
 
w
是半球上的所有方向,V是二进制可见度函数(binary visibility
function),如果可见V(p,w)=1,如果被阻塞的话就为0。D是一个在0-1之间的单调递减函数,随着p到ξ的距离增大而减小,ξ为射线到最
近的表面的交点。简化一下,D是一个阶梯函数。尽管光滑的衰减已经提供了不错的结果,但是采样半径仍是最大的问题

SSAO(Screen Space Ambient Occlusion)

SSAO是一种近似AO的方法,在屏幕空间完成。
我们可以用两种方法判断阻塞:
1.    采样点与当前的depth距离
2.    采样点和当前点的距离

可以用如下公式表示:
 
si为采样点,p为当前点,C为contribution
为了近似公式1.1使用蒙特卡罗积分法,contribution函数如下:
 
AO
的方向射线被替换为p周围的采样点,所以这里的V(p,si)二进制可见度函数(binary visibility
function),在p点是否可见采样点si,如果可见则为0,不可见则为1。V是相对于当前z-buffer的。有些SSAO方法省略了depth
test,它们的si的contribution是基于p到si的距离和入射角度。D是p到si距离的衰减。

反向二次投影(Reverse Reprojection)


向二次投影将当前帧像素与上一帧相同世界空间位置的像素结合起来,可以在当前帧重用上一帧的像素。这个方法可以用在很多技术上,比如shadow
mapping, antialiasing, motion blur等等。我们把上一帧的AO信息存在render
target中。在静态几何结构中,二次投影在整个帧是一个常量。使用上一帧(f-1)和当前帧(f)的视点(V)和投影矩阵(P),t为像素的
post-perspective位置
 

们使用延迟渲染存储的这一帧和上一帧eye-linear depth来求出世界空间坐标,也可以直接存储世界坐标到一张render
target中(博主觉得后者比较方便,也免去重复计算)。我们只需要转换当前世界坐标Pf与上一帧的view-projection matrix 
Pf-1Vf-1来得到tf-1
我们根据tf-1来计算查找纹理坐标texf-1来查找上一帧的AO buffer。应用透视除法并且缩放结果在0-1范围内。
 

动态物体


果在动态场景中方程1.4是无效的,因为二次投影是基于动态物体的转换。解决方案是进行两次顶点转换,一次是使用当前帧转换参数(modeling
matrix, skinning.),另一次是使用前一帧的参数。我们无法储存转换参数,所以我们把Pf-1-Pf存到一张render
target中,

此处可以使用比depth精度低的render target来存储这个偏移,可以用16bit。

简单讲解一下unity render target format


ARGB32


色彩渲染贴图格式,每通道8 bits


Depth


一种depth贴图


ARGBHalf


色彩渲染贴图格式,每通道16 bit


Shadowmap


一种本地系统的 shadowmap渲染贴图格式


RGB565


色彩渲染贴图格式


ARGB4444


色彩渲染贴图格式,每通道4 bit


ARGB1555


色彩渲染贴图格式, rgb通道5 bit,Alpha通道1 bit


Default


默认色彩渲染贴图格式, Frame Buffer也是这个格式


ARGB2101010


色彩渲染贴图格式。颜色10 bits,alpha 2 bits


DefaultHDR


默认HDR色彩渲染贴图格式,HDR的 Frame Buffer也是这个格式


ARGBFloat


色彩渲染贴图格式,每通道32 bit浮点值


RGFloat


两种颜色 (RG)渲染贴图格式, 每通道32 bit浮点值


RGHalf


两种颜色 (RG)渲染贴图格式,每通道16 bit浮点值


RFloat


标量(R)渲染贴图格式,32 bit浮点值


RHalf


标量(R)渲染贴图格式,16 bit浮点值


R8


标量(R)渲染贴图格式, 8 bit fixed point.


ARGBInt


四通道(ARGB)渲染贴图格式, 每通道32 bit带符号整数


RGInt


二通道(RG)渲染贴图格式, 每通道32 bit带符号整数


RInt


标量(R)渲染贴图格式,32 bit带符号整数

随时间精制SSAO

在当前帧f,我们计算新的contribution Cf,k为有效的(可视V>0)采样点个数
 
jf(p)是所有已用的采样点的个数。
结合前一帧的信息我们得到AO的求法
 
权重wf(p)是累积的所有采样点的个数。有一个最大值作为阈值。    

论上来讲这种方法可以任意的采样,但是在实践中,是不可以的,因为二次投影并不是准确的,而且重建需要双边过滤,随时间每一次二次投影都使这个误差加剧,
这种误差带来显而易见的模糊,而且会随时间越来越模糊。而且,新的采样点逐渐趋于零,先前的采样永远不会消除。所以我们用先前定义的wmax这个阈值来
clamp wf,使旧的contributions随时间逐渐衰减。此处wmax趋于正无穷。
 
博主把每帧的w值存在了y通道中,另外把最终的AO值存在了x通道中,把depth存在了z通道中。

起始索引jf存在通道a中
从像素中心获取索引值,像素中心根据上面求出的纹理坐标来求得texf-1,res x,y是当前帧buffer的分辨率。
 

检测和处理无效的像素

当我们重投影一个fragment,我们需要判断前一帧是否对应当前像素,也就是说我们储存的上一帧的AO是否有效。我们通过下面三种条件来判断是否有效:1.当前fragment无阻塞2.fragment的采样邻域发生变化3.fragment之前在帧的外面。

判断无阻塞

我们用上一帧和当前帧的depth来判断是否无阻塞,当前帧depth值为df,上一帧为df-1。
 
上式可以在一个大场景中产生一个稳定的结果,有很宽的depth范围,对近面不敏感,对远距离十分敏感。这种情况下,我们discard上一帧,把wf-1设为0,然后计算新的AO。

判断采样邻域的变化


断无阻塞这一步骤已经避免了大部分时间相干性的缺陷,但也只局限于固定不动的场景。但是SSAO是从邻域像素收集信息的,通过使用空间sampling
kernel。所以,在动态场景我们需要考虑当前像素点邻域的动态移动的物体会影响到当前像素点,即使在无阻塞的像素也是这样。
我们在AO中使用采样做两件事一件是计算当前contribution Cf(p)另一件是检测有效性。检测原理如下图
有效的采样点si和像素p通过采样和像素的相关位置的改变被估算:
 
sif-1通过之前存储的偏移向量(offset vectors)和si来算出(上面提到过的的方法)。只使用tangent 面前面的采样。

平滑失效处

上面的方法中我们用一个二进制阈值来检测无阻塞,我们通过 检查所有采样来discard上一帧的值。但是,在变形缓慢的表面,AO改变也是很缓慢,此时我们没有必要全部discard掉上一帧的值,用一种新的方法求出改变的程度,如下公式:
计算了一个在0-1范围内的confidence来表示之前的SSAO的有效程度。
 
S控制无效处的光滑程度在15-30范围内,分布如下图示。如果相关距离没变的话δ(x)=0,随着δ(si)增大confidence趋于零。
 
先前的AO中的所有confidence:
 
把它和前面的权重weight(公式1.6)相乘,为了防止闪烁, convergence小于一定阈值下时我们不重用相同的采样,比如conv(p)<0.5(公式1.7)。
过高的S会移除时间相干性的缺陷,但是会产生很多noise。

处理frame-buffer边界

在帧边界处的信息是不正确的,所以我们检测上一帧中的边界处的采样,在边界不使用上面的平滑失效,直接discard掉,在当前帧计算confidence时也不在超出边界处采样。

自适应Convergence-Aware空间滤波。

SSAO通常使用一个空间滤波来减缓由采样率不足引起的noise。TSSAO也使用一种空间滤波,通过的采样点与当前点在世界空间的距离,这种自适应空间滤波能自动考虑到depth差距,并检测到depth 差距很大的不连续的地方:
 

x是采样点,K(p)是标准化因子,g是空间滤波kernel(比如高斯滤波)。

给出滤波处代码

<span style="font-size:14px;">			for (int i = 0; i < 3; i++)
			{
				for (int j = 0; j < 3; j++)
				{
					uv_sam = uv + float2(-1 + i, -1 + j) / _Size;
					buffer = tex2D(_AoTex, uv_sam);
					conv = buffer.y;
					c = buffer.x;

					pos = GetPosition(uv,0);
					pos_sam = GetPosition(uv_sam,0);
					g = G(distance(pos,pos_sam));

					Kp += g*conv;
					Ao += Kp*c;
				}

			}</span>

采样方式

采样距离不要过近,判断的阻塞很少,在半球的半径或半径的一半这样的距离之间最好。
可以参考以前写过的这篇文章超级采样 Supersampling 方式汇总,此处推荐Halton sequence方法

2D Halton sequence

3D Halton sequence
 
x,z分量计算随机方向的单位球,y分量设置采样半径r。
在Game Programming Gems8使用半随机方法semi-random 3D vectors
在法线周围随机方向向量和距离
这里给出代码:

float3 reflect( float3 vSample, float3 vNormal )
{
return normalize( vSample – 2.0f * dot( vSample, vNormal ) * vNormal );
}
float3x3 MakeRotation( float fAngle, float3 vAxis )
{
float fS;
float fC;
sincos( fAngle, fS, fC );
float fXX       = vAxis.x * vAxis.x;
float fYY       = vAxis.y * vAxis.y;
float fZZ       = vAxis.z * vAxis.z;
float fXY       = vAxis.x * vAxis.y;
float fYZ       = vAxis.y * vAxis.z;
float fZX       = vAxis.z * vAxis.x;
float fXS       = vAxis.x * fS;
float fYS       = vAxis.y * fS;
float fZS       = vAxis.z * fS;
float fOneC      = 1.0f - fC;
float3x3 result = float3x3(
fOneC * fXX +  fC, fOneC * fXY + fZS, fOneC * fZX - fYS,
fOneC * fXY - fZS, fOneC * fYY +  fC, fOneC * fYZ + fXS,
fOneC * fZX + fYS, fOneC * fYZ - fXS, fOneC * fZZ +  fC
);
return result;
}
float4 PostProcessSSAO( float3 i_VPOS )
{
...
const float c_scalingConstant = 256.0f;
float3 vRandomNormal = ( normalize( tex2D( p_sSSAONoise, vScreenUV *
p_vSrcImageSize / c_scalingConstant ).xyz * 2.0f
– 1.0f ) );
float3x3 rotMatrix = MakeRotation( 1.0f, vNormal );
half fAccumBlock = 0.0f;
for ( int i = 0; i < iSampleCount; i++ ) {
float3 vSamplePointDelta = reflect( p_vSSAOSamplePoints[i],
vRandomNormal );
float fBlock = TestOcclusion(
vViewPos,
vSamplePointDelta,
p_fOcclusionRadius,
p_fFullOcclusionThreshold,
p_fNoOcclusionThreshold,
p_fOcclusionPower ) ) {
fAccumBlock += fBlock;
}
...
}

结果

博主实现总共分三步:

1.SSAO

2.TSSAO

3.Filter

给出三步的实现结果

上图为没有滤波的SSAO

上图为没有滤波的TSSAO

上图为有滤波的TSSAO

上图为Diffuse Color 无光照和阴影

上图为Diffuse Color  和有滤波的TSSAO

上图为Diffuse Color  和有滤波的TSSAO和光照颜色

原图

原图和有滤波的TSSAO

总结


于SSAO本身有一个问题就是对过近或过远的物体处理的不好,因为在固定的采样范围下,过远的物体采样范围偏大忽略了远处细节的阻塞,在近处又由于采样范
围偏小,导致判断几乎在同一个位置上、几乎没有阻塞,解决这个问题的方法就是根据远近动态调节采样范围,算是SSAO的一种优化处理。
SSAO本身是一种低分辨率的处理,因为性能的原因采样点过少,采样率也就过少,但是TSSAO有效的解决了这个问题,使得采样点随时间累加,且能很好的处理动态场景,效果比SSAO好很多,而且更加正确,Noise也很少。

本篇到此结束,

顺便求实习工作,哈哈

近期成果:最近弄的一些渲染

email:  [email protected]

            ------  by wolf96

时间: 2024-10-06 13:34:51

TSSAO Temporal Screen-Space Ambient Occlusion (Unity3d 5 示例实现)的相关文章

screen space directional occlusion(SSDO) in Unity5

也许是哪里出了问题..效果一般 16采样点 Gird . Random 博主近期渲染:最近用unity5弄的一些渲染 ---- by wolf96  http://blog.csdn.net/wolf96

Ambient Occlusion

一般在光照模型中,ambient light的计算方法为:A = l * m,其中l表示表面接收到的来自光源的ambient light的总量,而m表示表面接收到ambient light后,反射和吸收的量.出于性能考虑,在计算光照时,我们是不考虑那些从场景中其他物体反弹过来的光的,因为通常我们认为这些光在场景中被发散和弹射许许多多次以至于最后从各个方向照射到物体上的量是相同的.所以ambient light所做的就是提亮物体,它没有任何真实的物理光照计算,所以它最终的渲染效果就是一个常量颜色:

GLSL实现Ambient Occlusion 【转】

http://blog.csdn.net/a3070173/archive/2008/11/04/3221181.aspx 相信使用OpenGl或DirectX3D的朋友都知道到固定功能管线在光照处理主要由环境光,散射光和镜面光构成,这样一个光照处理模型在被光 的地方将以统一的环境光进行着色,导致一种不自然的不真实效果.本文介绍的Ambient Occlusion方法将使用不当预计算的方式离线的生成模型的AO图,并在片元着色器中将对此AO图进行采样的结果与环境光和散射光效果进行相乘以适当 削弱环

Cesium源码剖析---Ambient Occlusion(环境光遮蔽)

Ambient Occlusion简称AO,中文没有太确定的叫法,一般译作环境光遮蔽.百度百科上对AO的解释是这样的:AO是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光.飘和阴影不实等问题,解决或改善场景中缝隙.褶皱与墙角.角线以及细小物体等的表现不清晰问题,综合改善细节尤其是暗部阴影,增强空间的层次感.真实感,同时加强和改善画面明暗对比,增强画面的艺术性.AO简单来说就是根据周围物体对光线的遮挡程度,改变明暗效果.AO具体理论原理在网上都可以找到,感兴趣的可以去

soft alpha test/screen space reflection

http://www.crytek.com/cryengine/presentations/secrets-of-cryengine-3-graphics-technology 很多宝贝里面 不止题目那俩 前者不用msaa的话用后处理 后者 ray tracing ******************************************* screen space reflection /real time local reflection 1.为每个像素计算反射vector(def

静态渲染之Ambient Occlusion

Ambient Occlusion Wikipedia 问题描述: 对于3D游戏,AO作为全局光照的一部分已经成为很多3D引擎的标配,不管是静态还是SSAO.本文描述为在游戏开发后期为了提高整体效果而弥补的静态渲染的AO. 制作流程: 按文档划分UV(正常应该按场景的区域还划分?) 渲染生成AO贴图 运行期使用AO贴图 一些要点: 分UV,合并单个文档的Mesh为一个Mesh,然后转换为DX的标准Mesh,然后利用DX自带的UVAtlas来进行UV划分(UVAtlas分的UV并不是很好),UVA

统计性屏幕空间反射: Stochastic Screen Space Reflection (SSSR)

SSSR进一步调优,对标寒霜级技术水平,实现方式为Direct3D 11+自主实现实时渲染引擎,方法为对比测试.实现已经有段时间了,还是简要更新下吧.以下画面中的SSSR效果全部采用1:4 resolves.如下4个图,全1080p,分别是:"全屏tracing+全屏resolve"(2个图) v.s. "1/4全屏trace+1/4全屏resolve" (2个图).为便于对比测试,特地没对反射效果加双边滤波.你觉得哪个效果棒?如果不看对比的话,你会接受1/4分辨率

Unity3D技术之本地客户端开发入门

欢迎来到unity学习.unity培训.unity企业培训教育专区,这里有很多U3D资源.U3D培训视频.U3D教程.U3D常见问题.U3D项目源码,我们致力于打造业内unity3d培训.学习第一品牌. 本地客户端开发入门 本地客户端 (NaCl) 是 Google 提供的新技术,其允许您在 Web 页面嵌入本地可执行代码,以便您在无需安装插件的情况下部署性能非常强的 web 应用程序.目前,NaCl 仅支持在 Windows.Mac OS X 和 Linux(含可用的 Chrome 操作系统支

Unity3D组件参考手册

Refer to the information on these pages for details on working in-depth with various aspects of Unity. 这些页面的参考信息,是有关Unity深入工作的各个方面的详细信息. The Unity Manual Guide contains sections that apply only to certain platforms. Please select which platforms you