欢迎光临散文网 会员登陆 & 注册

URP | 后处理-SSAO效果

2023-07-17 00:15 作者:那个人真狗  | 我要投稿

内容偏多

使用版本 Unity 2021.3.15
Unity 2022.1.0

目的

  • 学习AO相关的基础知识,AO计算都有那些?

  • 实现的方法和原理,都需要注意什么?

  • URP管线下获取Depth Normals方法?

原理

AO

Ambient Occlusion(以下简称"AO")是一种基于全局照明中的环境光(Ambient Light)参数和环境几何信息来计算场景中任何一点的光照强度系数的算法. AO描述了表面上的任何一点所接受到的环境光被周围几何体所遮蔽的百分比, 因此使得渲染的结果更加富有层次感, 对比度更高.

遮蔽(Ambient Occlusion,下面简称AO) 是计算机图形学中的一种着色和渲染技术,用来计算场景中每一点是如何接受环境光的。例如,一个管道的内部显然比外表面更隐蔽(因此也更暗),越深入管道光线就越暗。环境光遮蔽可以被看作是光线能到达表面上每一点的能力的数值。[1]在拥有开放天空的场景中,这是通过估算每个点的可看见天空的大小来完成的;而在室内环境中,只考虑一定范围内的物体,并假设墙壁是环境光源。处理结果是一个漫反射、非定向的着色效果,并不会形成明确的阴影,只是能让靠近物体及被遮蔽的区域更暗,并影响渲染图像的整体色调。环境光遮蔽常被用作后期处理。 与局部方法如Phong着色法不同,环境光遮蔽是一种全局方法,意味着每个点的照明是场景中其他几何体的共同作用。然而,这只是一个非常粗略的近似全局光照。仅通过环境光遮蔽得到的物体外观与阴天下的物体相似。“

AO可以理解为一张贴图,是来描绘物体和物体相交或靠近的时候遮挡周围漫反射光线的效果,可以解决或改善漏光、飘和阴影不实等问题,解决或改善场景中缝隙、褶皱与墙角、角线以及细小物体等的表现不清晰问题,综合改善细节尤其是暗部阴影,增强空间的层次感、真实感,同时加强和改善画面明暗对比,增强画面的艺术性。

单独的物体也是有AO贴图,处理一个物体之间的阴影物体。

计算得出的AO Texture,越黑的地方代表环境光越不可能照到该点,该点是环境光的影响就越小。

  • 没有SSAO

但是这样的解决方法,可以解决单物体自己,不能解决两个物体之间。

为什么了解决这个问题,开发出屏幕空间环境光屏蔽(Screen Space Ambient Occlusion,SSAO)

AO计算公式

  • 处理

SSAO计算方法

SSAO背后的原理很简单:屏幕上的每一个像素,我们都会根据周边深度值计算一个遮蔽因子(Occlusion Factor)。这个遮蔽因子之后会被用来减少或者抵消片段的环境光照分量。遮蔽因子是通过采集片段周围球型核心(Kernel)的多个深度样本,并和当前片段深度值对比而得到的。高于片段深度值样本的个数就是我们想要的遮蔽因子。

  • 计算方法

如图,中心黑点为采样位置,蓝色箭头为平面的法向量,空心点为未受遮蔽的采样点(Sample Point),实心白点为受遮蔽的采样点,左边采样点的AO值计算方法为:未受遮蔽的采样点 / 采样点总数 = 0 / 11 = 0,故该点未受遮蔽,AO值为0;
  • 基于Ray-Tracing的AO计算模型. 红色的射线表示V = 1, 绿色的射线表示V = 0.

这样我们需要对屏幕上的每一个像素进行采样,所以SSAO很费。并且出现一些其他问题。

  • 出现摩尔纹

所以需要进行模糊,这里使用双边滤波(Bilateral filter)来解决

双边滤波是一种非线性滤波器,它可以达到保持边缘、降噪平滑的效果。和其他滤波原理一样,双边滤波也是采用加权平均的方法,用周边像素亮度值的加权平均代表某个像素的强度,所用的加权平均基于高斯分布[1]。最重要的是,双边滤波的权重不仅考虑了像素的欧氏距离(如普通的高斯低通滤波,只考虑了位置对中心像素的影响),还考虑了像素范围域中的辐射差异(例如卷积核中像素与中心像素之间相似程度、颜色强度,深度距离等),在计算中心像素的时候同时考虑这两个权重。

  • 获取到AO贴图

  • 得到AO贴图后,事情就轻松很多了,只需要一一对应游戏画面与AO贴图,改变游戏画面上每一个像素的GI值,就可以实现SSAO效果

不同的AO效果

  • SSAO

  • HBAO

  • HDAO

本次只是实现SSAO效果


SSAO理论知识

实现原理

  1. 先利用深度图和屏幕UV坐标反算出当前顶点世界坐标的位置,

  2. 再使用沿着法线正方向半球内的随机点进行采样, 获得新的坐标点.

  3. 两个点进行比较, 判断是否被遮挡, 然后加权处理获得AO.

  4. AO这时候充满噪点, 需要高斯模糊一下.

  5. 最后和场景颜色RT进行混合叠加.

SSAO原理流程图

获取深度我们是可以获取到的,法线深度URP是没有的我们需要自己获取。

Depth + Normals 实现

unity URP 管线官方不支持深度法线,我们需要自己实现深度法线,

URP | 后处理-描边 - 哔哩哔哩 (bilibili.com)


上面我们制作描边的时候,也是使用SSAO的深度法线,这次我们单独实现,深度法线。

  • Unity URP 不支持深度法线


使用自定义渲染器功能来实现

  • Scriptable Renderer Feature   自定义渲染器功能

    创建一个脚本 DepthNormalsFeature  继承 ScriptableRendererFeature

这里有两种方法,

第一种方法

第二种方法

  • URP默认SSAO的法线效果  和我们第二种方法一样。

扩展 他们两种方法有什么不同吗?有什么区别?

  • DepthNormalsPass  是深度和法线一起获取的,在一起需要进行解码使用。

  • DepthNormals 只是获取像素深度的法线信息。

SSAO | AO实现

设置渲染管线

我们当前准备好渲染管线 ,

这里是使用基础的颜色来测试管线是否正确

Shader部分

Volume

  • 效果

获取世界空间位置,法线,切线数据

  1. 获取世界空间中的顶点的位置

    我们定义一个库用来放放一些我们自定义的函数

这个函数是输入屏幕UV坐标,从深度中转换为齐次剪裁空间坐标(ndc),使用矩阵转换成世界空间。

那这里我们需要获取两个数据,

  • 深度  _CameraDepthTexture

  • 转换世界空间的矩阵    sampler_CameraDepthTexture

深度我们URP管线自带,勾选就可以调用, 转换世界空间的矩阵我们要在管线中获取,传到Shader中。

Shader中 定义一个矩阵变量,等等我们在后处理管线中传入。

  • SSAO.cs中

    我们需要获取摄像机,在由摄像机来生成矩阵,传到我们的Shader

    在SSAOPass 中我们前定义一个摄像机的变量

  • Execute 中进行初始化

  • 定义一个函数 ,这个函数用来后面传递控制变量。

  • 在渲染里调用这个函数,

当前完成

回去到Shader中我们调用 函数求处顶点位置。

效果

2. 获取世界空间中法线

我们需要需要深度法线来计算出像素在世界空间下的位置信息。

深度Unity管线为我们提供了。

这里我们使用上面的第二种方法。

在管线中增加

  1. 在库中输入我们的 贴图

在定义一个函数

Shader中我们调用函数

  • 效果

3. 获取世界空间中切线

切线就是我们需要生成的随机向量部分,

在库里定义两个函数


shader中调用


注意:这些都是在世界空间下计算的,后面需要转换成View空间

效果

这就是我们生成的一些随机噪点

都获取到了,那计算我们需要的副切线,构造一个矩阵。

矩阵准备好了.

注意:wTan = cross(wBin, wNor);  还需要cross在计算一次。       


 

计算AO效果

定义两个变量,一个变量是我们的ao 效果,一个是我们控制噪点多少。

定义一个循环遍历所有采样点。

使用随机向量生成一个半球向量 offDir 通过 scale控制范围

在HLSL库中

增加GetRandomVecHalf 函数

这里增加控制半球控制大小, 世界空间转换裁剪空间,这里需要 wPos 世界空间顶点位置和矩阵。

这里我们需要两个 矩阵,

  • v_Matrix          视图矩阵

    是世界空间到观察者空间的变换矩阵。它描述了相机的位置、朝向和上方向等信息,可以用于将场景从世界坐标系转换到相机坐标系。

  • p_Matrix           投影矩阵

    是将相机坐标系中的场景投影到屏幕坐标系的变换矩阵。它描述了相机的视锥体、投影方式和屏幕尺寸等信息,可以用于将场景从相机坐标系转换到屏幕坐标系。

SetMatData函数 使用同样的方法

采样深度

SampleSceneDepth函数获取屏幕坐标对应的深度值,然后通过LinearEyeDepth函数将其转换为线性深度。该函数使用了_ZBufferParams参数,它包含了一些与深度缓冲相关的信息,如深度缓冲的近、远裁剪面等。


采样AO

这里我们需要获取默认的深度值

首先定义一个sampleZ变量,表示采样点的深度值。然后通过计算sampleDepth和sampleZ之间的距离,并将其与_Radius参数相除,得到一个范围检查的值rangeCheck。这个值用于控制采样点的范围,使得只有距离当前像素一定范围内的采样点才会对AO值产生影响。

接下来的代码中,通过判断sampleDepth是否小于depth - 0.08来进行自遮蔽检查。如果采样点比当前像素更靠近观察者,则返回1,否则返回0。这个值用于控制自遮蔽的影响。

最后,将rangeCheck、selfCheck、_AOInt和weight相乘,得到一个权重值,并将其乘以1(即对AO值产生1的影响),最终得到AO值。



采样深度

这里在库定义一个获取深度的函数

在Sahder中定义

输出AO

我们在Shader部分输出ao效果

现在的效果是白色的,啥都没有。

我们的SSAOVolume还没有关联属性

SSAOVolume

在这里我们关联属性

RendererFeature

关联属性,我们把属性都放到 SetMatData()函数中

增加完代码

效果

AO效果就计算完成,

我们增加一个判断,控制开关AO效果,方便对比。

  • 效果

AO部分就算完成。

SSAO | 模糊算法

SSAO我们需要多个Pass

  • AO

  • 水平模糊

  • 垂直模糊

  • AO + 原图

整理Shader

我们需要4个pass这里不适合放到一起,为了方便,我们 重新创建 hlsl文件,放我们的不同的执行过程。

  • Shader 只执行我们渲染设置,

  • Hlsl 文件执行计算,

这里重新创建两个hlsl文件

  • AOpass

  • BlurPass

Shader

SSAOFunLib.hlsl

AOPass

BlurPass

这个部分查看

URP | 后处理-模糊算法总结 - 哔哩哔哩 (bilibili.com)

在 Volume这里增加上控制模糊的变量

Volume

主要是在管线里处理。

RendererFeature

这里我们来处理模糊

需要定义两个临时RT用来处理模糊

获取临时RT

注意:这里RT1 和R2是储存是不一样的,RT2是一半是我们中间过程,RT是输出结果所以使用高精度。

释放临时RT



在else里面增加逻辑


这里在执行一遍AO效果,装修完储存到 bufferid1中,

cmd.SetGlobalTexture("_AOTex", bufferid1)  把AO效果传递到Shader中进行计算。

执行两次  水平一次,垂直一次,输出查看效果

AO原图混合

我们把处理的结果传入到Shader

这样就可以实现完成了。

  • 可以改变阴影的颜色

这个效果其实还可以在模糊一次,

SSAO.cs

Volune

总结

  1. 在实现AO效果的时候比较麻烦,平常是有两种方法,第一种方法都是在 View视角下计算,这里使用第二种方法,在世界空间下计算完成,在转换成View视角,

    • Vertex position vectors in view space

view space中的顶点位置向量


    • Vertex normal vectors in view space

view space中的顶点法线向量
    • Sample vectors in tangent space

tangent space中的样本向量
    • Noise vectors in tangent space

tangent space中的噪声向量

2. 计算AO具体流程,使用UV计算出需要的 位置,法线,切线构建矩阵, 遍历所有的采样点,使用随机向量计算出半求,扩展半球范围大小。

    计算完成转换到裁剪空间,根据深度判断是否在阴影范围。

3. 在管线里使用摄像机获取矩阵传递给Shader

    • _VPMatrix_invers      视图投影矩阵的逆矩阵

    • _VMatrix         视图矩阵是将场景从世界坐标系转换到相机坐标系的变换矩阵

    • _PMatrix        投影矩阵则是将相机坐标系中的场景投影到屏幕坐标系的变换矩阵


URP | 后处理-SSAO效果的评论 (共 条)

分享到微博请遵守国家法律