导言
在传统的MMO中,最常用的提升视觉显示效果的就是环境光遮蔽AO,传统的AO实现方法使用了光线追踪,路径追踪等方法,但是实时渲染效率下,所以现在常用的是屏幕空间环境光遮蔽SSAO,SSAO的效果表现出会让被遮挡的物体显示变暗,没有遮挡的地方显示更加明亮一些,使得整个视觉空间更有层次感。
章节
一.SSAO的视觉表现
如图左边为正常渲染的原图,中间加了SSAO的效果,可以看出石头后边的墙壁明显要比墙壁其他地方要稍微暗一点,右边的就是SSAO渲染出的结果。
二.SSAO的两种实现方式和原理
1.基于法线的半球积分
这是最常用也是效果最好的SSAO方法,在传统MMO中大多采用该方法, 对于场景中的任意像素计算它的环境遮蔽。计算方法如下:
2.基于视线方向的球积分
由于法线方向的积分,一半的采样点被浪费,当采样次数不够的时候不能形成很好的效果,为了充分利用每次采样点对AO值贡献的计算,采用如下公式:
如图所以,公式很简单,理解起来就是去掉了max(o,ao)的操作,使得所有的采样点都参与了对AO贡献度的计算,如图可知B点对AV方向的投影(cos值)也就是贡献值为正数,C点对AV方向的投影为负,当采样到足够多的点后进行平均就能的到该点的AO值,至于公式采用了基于视角方向的积分是为了更多情况下采样点都能满足对该点AO贡献度的计算。根据该公式计算cos值得范围通常是-1,1之间,通常我们可以直接映射转换到0-1之间进行参与计算。
两种方式都理解后,下面我们来分别实现两种方式的SSAO算法,以及其效果对比,还有算法中包括选取点灯常规需要注意的地方。
三.SSAO的两种实现方式和效果对比
实现:
1.采样点的选取,如下采样点使用了自定义CROSS[]数组,然后在上下左右方向一定距离处选取采样点,当需要更高质量的时候可用噪声图对采样点方向进行随机采样,得到的效果会更好,当然也可以自定义随机采样点矩阵或者特定的点云模型矩阵参与采样。
2.根据不同的需求给SSAO采样点分级,比如要好点的效果可以采样32次以上,要性能高点可以采样8次就够了.
3.之后计算SSAO值,下面也给了两种计算SSAO的方法,但是得到的Ao贡献值不同方法参与计算要进行不同映射。
4.对于有较高要求的SSAO给结果进行模糊处理 降噪 会使明暗程度更加平滑。
inline half calcAO(half2 tcoord, half2 uvoffset, half3 p, half3 norm,half3 viewdir)
{
half2 t = tcoord + uvoffset;//采样点是UV坐标周围的点 对应的world空间点
float2 XY_Depth = float2(1.0f,0.003921568627451);
float depth = dot(tex2D(_Depth, t).xy, XY_Depth);
float3 diff = float3(t*2-1, 1)*_FarCorner*depth-p;//viewpos offset
half3 v = normalize(diff);
half d = length(diff) *0.11;// _Params1.w/100;//distance
return dot(viewdir, v);
//return max(0.0, dot(norm, v))* (1.0 / (1.0 + d)) ;
//半球积分
}
float4 frag( v2f IN) : COLOR
{
float2 uv= IN.uv;
float2 XY_Depth = float2(1.0f,0.003921568627451);
float2 sampleuv = uv;
float4 depth_normal = tex2D(_Depth,sampleuv);
float view_depth = dot(depth_normal.xy,XY_Depth);//
float3 normal = DecodeNormal(depth_normal.zw);
float3 view_pos = IN.viewpos*view_depth;
float3 viewdir = normalize(view_pos);
const half2 CROSS[4] = { half2(1.0, 0.0), half2(-1.0, 0.0), half2(0.0, 1.0), half2(0.0, -1.0) };
half eyeDepth =view_depth;// LinearEyeDepth(depth);
half3 position =view_pos;// getWSPosition(uv, depth); // World space
#if defined(SAMPLE_NOISE)
half2 random = normalize(tex2D(_Sample2x2, _ScreenParams.xy * uv / _Params1.x).rg * 2.0 - 1.0);
#endif
half radius =max(_Params1.y/100, 0.005);
// if(view_pos.z>30)
// return view_depth;// Skip out of range pixels!!!!!!!!!!!!!!!!!!!!!!!!
half ao = 0;
// Sampling
for (int j = 0; j < 4; j++)
{
half2 coord1;
#if defined(SAMPLE_NOISE)
coord1 = reflect(CROSS[j], random) * radius;//this random important
#else
coord1 = CROSS[j] * radius;
#endif
#if !SAMPLES_VERY_LOW
half2 coord2 = coord1 * 0.707;
coord2 = half2(coord2.x - coord2.y, coord2.x + coord2.y);
#endif
#if SAMPLES_Num==20 // 20
ao += calcAO(uv, coord1 * 0.20, position, normal,viewdir);
ao += calcAO(uv, coord2 * 0.40, position, normal,viewdir);
ao += calcAO(uv, coord1 * 0.60, position, normal,viewdir);
ao += calcAO(uv, coord2 * 0.80, position, normal,viewdir);
ao += calcAO(uv, coord1, position, normal,viewdir);
#elif SAMPLES_Num==16 // 16
ao += calcAO(uv, coord1 * 0.25, position, normal,viewdir);
ao += calcAO(uv, coord2 * 0.50, position, normal,viewdir);
ao += calcAO(uv, coord1 * 0.75, position, normal,viewdir);
ao += calcAO(uv, coord2, position, normal,viewdir);
#elif SAMPLES_Num==12 // 12
ao += calcAO(uv, coord1 * 0.30, position, normal,viewdir);
ao += calcAO(uv, coord2 * 0.60, position, normal,viewdir);
ao += calcAO(uv, coord1 * 0.90, position, normal,viewdir);
#elif SAMPLES_Num==8 // 8
ao += calcAO(uv, coord1 * 0.30, position, normal,viewdir);
ao += calcAO(uv, coord2 * 0.80, position, normal,viewdir);
#elif SAMPLES_Num==4 // 4
ao += calcAO(uv, coord1 * 0.50, position, normal,viewdir);
#endif
}
ao /= SAMPLES_Num;
float3 ret =0.5+ao;
//float3 ret =1-ao;
if (_Debug==1)
return half4(ret.xyz, 1);
float4 diff = tex2D(_Diffuse, sampleuv);
ret*= diff.xyz*_AmbientColor;
float3 WorldN= mul((float3x3)ViewToWorld, normal);
float3 envDiff = texCUBE(_SkyTexture,WorldN);
ret *= envDiff*envDiff;
return half4(ret.xyz, 1);
}
下面来看不同SSAO算法,以及不同采样:
总结:从上面的渲染图可以得到一些结论
1.正常情况下,采样次数越越多效果越好
2.在较低次数采样下基于View方向的球积分效果表现更好,在较高次数采样下基于normal方向的半球积分表现效果更好。总的来说基于normal方向的计算效果更佳逼真。
本章讲了SSAO的基本算法, 对于算法中常用的坐标和方向转换不明白的地方,可以好好看看基础,另外本章也提到了采样将SSAO值模糊处理进行平滑得到更好的效果,那么下一张讲最常用的模糊处理吧,模糊处理用在很多地方,比如高斯模糊,Dof,运动模糊,径向模糊,FXAA等等,虽然功能和算法都不一样,但是模糊的思想是互通的。