Reference:
1.介绍基本概念和原理 https://zhuanlan.zhihu.com/p/492969251
Screen Space
Screen space / 屏幕空间:3D 场景最终投影到屏幕上之后的 2D 坐标空间”,即最终对应到屏幕像素的位置。例如:这棵树显示在屏幕坐标 (640, 360)
在 Shader 里,screen space 常用于:
- 屏幕空间后处理,比如模糊、泛光、描边
- 屏幕空间反射,Screen Space Reflection,SSR
- 屏幕空间环境光遮蔽,SSAO
- 根据像素在屏幕上的位置做效果
- UI、鼠标点击、深度图采样等
实时渲染中如何处理间接光照
常规实时渲染,shader自己不会完整计算光线反弹,会用以下廉价方案:
- 环境光 Ambient light
- finalColor = directLighting + ambientColor * albedo
- 缺点是不真实,因为不会考虑方向、遮挡、颜色反弹。比如红墙旁边的白球理论上会被染红,普通 ambient light 做不到
- 烘焙光照贴图 LightMap
- 最常见的静态间接光方案
- 场景里的静态物体提前在编辑器里计算 GI,然后存到一张或多张贴图里。运行时 shader 只需要根据 UV 去采样
- indirectDiffuse = SAMPLE_TEXTURE2D(lightmap, sampler, lightmapUV).rgb;
- finalColor = directLighting + indirectDiffuse * albedo;
- 缺点是只适合静态物体,动态变化的灯光和物体不太好处理
- 墙角柔和阴影、地面反弹光、室内漫反射,很多都是 lightmap 烘焙出来的间接光
- Light Probe / Irradiance Probe 光照探针
- 场景中放很多 probe,提前记录这些位置的间接光信息。运行时根据角色位置,在附近几个 probe 之间插值
- indirectDiffuse = SampleLightProbe(worldPosition, normal);
- finalColor = directLighting + indirectDiffuse * albedo;
- 所以角色走进红色房间时身上会带一点红色环境光,这通常就是 probe 的贡献
- 场景中放很多 probe,提前记录这些位置的间接光信息。运行时根据角色位置,在附近几个 probe 之间插值
- Reflection Probe / Cubemap
- 对于高光、金属、光滑表面,间接光不只是漫反射,还包括反射
- 比如金属球上反射天空、墙壁、房间,这通常来自 reflection probe 或环境 cubemap
- indirectSpecular = SampleReflectionProbe(reflectDir, roughness);
- finalColor = directLighting + indirectDiffuse + indirectSpecular;
- 粗糙度越高,采样越模糊;越光滑,反射越清晰
- IBL,Image-Based Lighting
- PBR 里常见的环境光照方案是 IBL
- 它通常分间接漫反射 diffuse IBL和间接镜面反射 specular IBL,漫反射部分一般来自 irradiance map。镜面反射部分一般来自 prefiltered environment map 加 BRDF LUT
- indirectDiffuse = irradiance * albedo;
- indirectSpecular = prefilteredEnv * brdfLUT;
- finalColor = directLighting + indirectDiffuse + indirectSpecular;
- SSAO / GTAO,屏幕空间环境遮蔽
- 没有 AO 的时候,ambient/light probe 可能会让角落亮得很假,用来修正间接光,让缝隙、墙角、接触区域变暗
- indirectLighting *= ao;
如果真正在实时渲染中实现Games101中基于物理的间接光照则有以下技术:
- Screen Space GI / SSR
- 屏幕空间方法估算间接光
- SSR:屏幕空间反射,用当前屏幕颜色做反射。
- SSGI:屏幕空间全局光照,用屏幕里的颜色估算一次反弹光
- 它们比光追便宜,但有明显限制:屏幕外的东西无法参与计算
- 屏幕空间方法估算间接光
- 实时 GI / Ray Tracing / DDGI
- 然后现在有腾讯的基于神经网络的全局光照方案,可以降低开销,也是未来的方向(老师说的)
SSAO 概念

- 首先SSAO是在屏幕空间内的 In Screen space
- 在渲染后完成 Done in post-rendering pass
- 不依赖场景复杂性 doesn‘t depend on scene complexity
- 对全局光照的近似,物理上不准确 An approximation of globol illumination,not physically accurate
- 我们不知道入射的间接光照incident indirect lighting是什么,那就假设它是常量(对所有着色点、来自所有方向都一样),非常像环境光,假设一个常数,模拟间接光照
与环境光不一样的地方,环境光是整体带一个颜色,AO并不是所有环境光照都会被接收到
- 给定一个点从四面八方看,没被遮挡的地方就接受的到间接光照,被遮挡的地方就接受不到间接光照

SSAO原理
我们在寻找环境遮蔽的时候是不能选择无限距离的射线的,假设我们在一个封闭的房间内,如果选择一个无限距离的射线,那么整个房间都是被遮蔽的。所有我们选择一个半球向量去寻找环境遮蔽


- 法向半球并不是以向量射出去采样的(这样采样范围不够精确),而是在周围撒上一些采样点去和深度图进行对比
如图:我们让摄像机从下往上看,下面对着摄像机的那条线表示深度,在线上的点形成一个球撒一些点,然后用这个点与深度进行比较,比深度更深就是被挡住(红色),比深度浅就是没被挡住(绿色)
在第二图中出现了错误的结果,下面的红色点没有在物体的内部,在物体深度值外,但是比较后发现它被计算成物体内部了

流程:
需要获取环境的深度缓冲和法线缓冲(类似G-Buffer):

- 1.深度缓冲:深度值会计算在屏幕空间中的坐标值求出每个像素的位置(计算像素坐标位置)。
- 2.法线缓冲:决定采样半球的朝向,SSAO 在这个半球里采样随机向量,比如 16 个、32 个、64 个采样方向
- 我猜这也解释了为什么将Intensity增大后看到的AO效果不是平滑的向缝隙外延展,在最外侧会出现很多噪点
- 3.从法线半球随机给出一个采样点,计算随机后的坐标信息
- 4.计算采样点后获取一个新的深度值
- 5.与深度图的深度值做对比
- 大于时舍去(在深度外)
- 小于时加权到AO(在深度内)
- 6.多次循环生成AO图
- 7.生成的AO图可能不正确,通过后期优化处理
深度缓冲:
深度缓冲中的深度值用于场景里每一个像素与摄像机的距离,我们从深度缓冲中重建像素点的世界空间位置。得到物体与相机的距离
法线缓存:
用相机空间的法线信息,用来构建每一个屏幕像素法线,切线,副法线,用来计算法线半球中的采样随机向量。随机向量用于判断和描述该像素的AO强度。
由于采样时对每个表面法线方向生成采样核心非常困难
- 世界空间:
- 每个像素的法线方向都不同,采样半球方向也不同。
- 切线空间:
- 每个像素都把自己的法线当成 +Z。所以所有像素都可以复用同一套 +Z 半球采样核心
所以将在切线空间内生成采样核心(就是一组随机采样向量),法向量将指向正 z 方向

法向半球:
使用法向半球,假设黑色为屏幕像素点,蓝色为像素点的法线向量,我们在半圆中随机选取采样点,灰色是被挡采样点(深度大于周围),通过灰色采样点判断AO的强度。
使用法向半球的目的:
- 1.给出随机采样点
- 2.正确的求出边上随机像素值的坐标
- 3.使用法向球型,会导致错误的效果

SSAO问题:
凳子与地板没有接触,但是还是产生了AO,这是我们不想看到的效果

算法实现
- Unity 自带 SSAO 会生成 AO 纹理,但你的自定义 shader 不会自动使用它;如果你自己写 lighting,就要自己采样
_ScreenSpaceOcclusionTexture,并且你的 shader 还要有 DepthNormals / DepthOnly pass,才能正确参与 SSAO - Unity自带的SSAO,它的
Method不是在选“SSAO / GTAO / HBAO”这种大算法,而是控制 SSAO 使用的 noise 类型;Interleaved Gradient 用 interleaved gradient noise,Blue Noise 用一组 blue-noise texture
开源项目
SSAO / GTAO / HBAO
GameTechDev/XeGTAO:[Jimenez 等,2016] 地面真实环境遮蔽的实现,MIT许可
这个项目的移植工作尝试过但是效果不好
AmplifyCreations/AmplifyOcclusion: Full source-code for Amplify Occlusion plugin for Unity
这个如果将来需要可以尝试
老师我有一个疑问,那rendder feature 里的SSAO和模型本身的AO信息不会冲突嘛
我猜实际项目中SSAO负责场景的AO效果,对于人物等精细模型禁用SSAO,读取AO贴图,不知道是不是这样…

