学习教程来自:【技术美术百人计划】图形 4.2 SSAO算法 屏幕空间环境光遮蔽

笔记

0. 前言

SSAO的使用一般在IPhone10及骁龙845之后的机型中使用

1. SSAO介绍

AO:环境光遮蔽 Ambient Occlusion
SSAO:屏幕空间环境光遮蔽 Screen Space Ambient Occlusion 通过深度缓冲、法线缓冲计算AO

2. SSAO原理

2.1 样本缓冲

  1. 深度缓冲:每一个像素距离相机的深度值
  2. 法线缓冲:相机空间下的法线信息
  3. Position

2.2 法线半球

其中:深度(深度值)+位置(相机空间下向量)—>世界空间下相机到像素的向量
相机空间下向量:

v2f vert_Ao(appdata v){v2f o;UNITY_INITIALIZE_OUTPUT(v2f,o);o.vertex = UnityObjectToClipPos(v.vertex);//顶点位置:转换到裁剪空间o.uv = v.uv;float4 screenPos = ComputeScreenPos(o.vertex);//顶点位置:转换到屏幕空间float4 ndcPos = (screenPos / screenPos.w) * 2 - 1;//归一化float3 clipVec = float3(ndcPos.x, ndcPos.y, 1.0)* _ProjectionParams.z;//像素方向:倒推回裁剪空间o.viewVec = mul(unity_CameraInvProjection, clipVec.xyzz).xyz;//像素方向:倒推回相机空间return o;
}

评论区大佬 dmpwmf 指正ComputeScreenPos返回的值是齐次坐标系下的屏幕坐标值,其范围为[0, w]。
参考链接:Unity Shader中的ComputeScreenPos函数

3. SSAO算法实现

3.1 获取深度和法线缓冲

private void Start()
{cam = this.GetComponent<Camera>();cam.depthTextureMode = cam.depthTextureMode | DepthTextureMode.DepthNormals;//与运算,增加深度和法线的渲染纹理
}

3.2 重建相机空间坐标

参考:Unity从深度缓冲重建世界空间位置
其中:深度(深度值)+位置(相机空间下向量)—>相机空间下相机到像素的向量
相机空间下向量:

//第一步:获得相机空间下的像素方向
v2f vert_Ao(appdata v){v2f o;UNITY_INITIALIZE_OUTPUT(v2f,o);o.vertex = UnityObjectToClipPos(v.vertex);//顶点位置:转换到裁剪空间o.uv = v.uv;float4 screenPos = ComputeScreenPos(o.vertex);//顶点位置:转换到屏幕空间float4 ndcPos = (screenPos / screenPos.w) * 2 - 1;//归一化float3 clipVec = float3(ndcPos.x, ndcPos.y, 1.0)* _ProjectionParams.z;//像素方向:倒推回裁剪空间o.viewVec = mul(unity_CameraInvProjection, clipVec.xyzz).xyz;//像素方向:倒推回相机空间return o;
}//第二步:获得深度信息,相乘得到向量
fixed4 frag_Ao(v2f i) : SV_Target{fixed4 col tex2D(_MainTex, i.uv);//屏幕纹理float3 viewNormal;//相机空间下法线方向float linear01Depth;//深度值0-1float4 depthnormal = tex2D(_CameraDepthNormalsTexture, i.uv);//获得纹理信息并解码DecodeDepthNormal(depthnormal, linear01Depthm viewNormal);float3 viewPos = linear01Depth * i.viewVec;//相机空间下像素向量}

3.3 构建法向量正交基(TBN)

tangent bitangent viewNormal

fixed4 frag_Ao(v2f i) : SV_Target{//重建向量正交基viewNormal = normalize(viewNormal) * float3(1, 1, -1);//Nfloat2 noiseScale = _ScreenParams.xy / 4.0;//噪声纹理的缩放float2 noiseUV = i.uv * noiseScale;float3 randvec = tex2D(_NoiseTex, noiseUV).xyz;//采样得到随机向量float3 tangent = normalize(randvec - viewNormal * dot(randvec, viewNormal));//Tfloat3 bitangent = cross(viewNormal, tangent);//Bfloat3x3 TBN = float3x3(tangent, bitangent, viewNormal);
}

3.4 AO采样核心

//第一步:在C#部分生成采样核心
private void GenerateAOSampleKernel()
{if(sampleKernelCount == sampleKernelList.Count){return;//list为满则返回}sampleKernelList.Clear();for(int i = 0; i < sampleKernelCount; i++){var vec = new Vector4(Random.Range(-1.0f, 1.0f), Random.Range(-1.0f, 1.0f), Random.Range(0, 1.0f), 1.0f);vec.Normalize();//初始化随机向量var scale = (float)i / sampleKernelCount;scale = Mathf.Lerp(0.01f, 1.0f, scale * scale);//i从0-63对应0-1的二次方程曲线vec *= scale;sampleKernelList.Add(vec);}
}
//第二步:从每一个采样位置的深度变化程度累计ao
fixed4 frag_Ao(v2f i) : SV_Target{//采样并累加float ao = 0;//ao值int sampleCount = _SampleKernelCount;for(int i = 0; i < sampleCount; i++){float3 randomVec = mul(_SampleKernelArray[i].xyz, TBN);//采样方向float weight = smoothstep(0, 0.2, length(randomVec.xy));//针对不同的采样方向分配权重//采样位置float3 randomPos = viewPos + randomVec * _SampleKernelRadius;//相机空间下的采样位置float3 rclipPos = mul((float3x3)unity_CameraProjection, randomPos);//相机空间到裁剪空间(投影空间)下float2 rscreenPos = (rclipPos.xy / rclipPos.z) * 0.5 + 0.5;//裁剪空间到屏幕空间float randomDepth;//采样位置的深度float3 randomNormal;//采样位置的法线方向//从纹理中读取上述信息float4 rcdn = tex2D(_CameraDepthNormalsTexture, rscreenPos);DecodeDepthNormal(rcdn, randomDepth, randomNormal);//对比计算aofloat range = abs(randomDepth - linear01Depth) > _RangeStrength ? 0.0 : 1.0;//深度变化float selfCheck = randomDepth + _DepthBiasValue < linear01Depth ? 1.0 : 0.0;//深度变化过大的归零ao += range * selfCheck * weight;}ao = ao / sampleCount;ao = max(0.0, 1 - ao * _AOStrength);return float4(ao, ao, ao, 1);
}

4. AO效果改进

从上面代码中截取出来的

4.1 采样Noise获得随机向量

float2 noiseScale = _ScreenParams.xy / 4.0;//噪声纹理的缩放
float2 noiseUV = i.uv * noiseScale;
float3 randvec = tex2D(_NoiseTex, noiseUV).xyz;//采样得到随机向量

4.2 裁剪掉异常值

  1. 差距巨大的深度值
float selfCheck = randomDepth + _DepthBiasValue < linear01Depth ? 1.0 : 0.0;//深度变化过大的归零
  1. 同一平面深度值由于精度问题造成的深度变化
float range = abs(randomDepth - linear01Depth) > _RangeStrength ? 0.0 : 1.0;//深度变化
  1. 根据距离Smooth权重
float weight = smoothstep(0, 0.2, length(randomVec.xy));//针对不同的采样方向分配权重
  1. 双边滤波模糊(C#部分)
RenderTexture blurRT = RenderTexture.GetTemporary(rtW, rtH, 0);//获取模糊渲染纹理
ssaoMaterial.SetFloat("_BilaterFilterFactor", 1.0f - bilaterFilterStrength);
ssaoMaterial.SetVector("_BlurRadius", new Vector4(BlurRadius, 0, 0, 0));//x方向
Graphics.Blit(aoRT, blurRT, ssaoMaterial, (int)SSAOPassName.BilateralFilter);
ssaoMaterial.SetVector("_BlurRadius", new Vector4(0, BlurRadius, 0, 0));//y方向
Graphics.Blit(blurRT, aoRT, ssaoMaterial, (int)SSAOPassName.BilateralFilter);

5. 对比模型烘焙AO

5.1 烘焙方式

  1. 建模软件烘焙到纹理:可控性强(操作繁琐,需要UV,资源占用大),自身细节性强(缺少场景细节),不受静动态影响。
  2. 游戏引擎烘焙,如Unity3D Lighting:较简单,整体细节好,动态物体无法烘培
  3. SSAO:复杂度基于像素多少、实时性强、灵活可控;性能消耗较前两种最大,最终效果比1差(理论上)

6. SSAO性能消耗

消耗的点:

  1. 随机采样:IF FOR循环打破了GPU的并行性,过高的采样次数大大提高了复杂度
  2. 双边滤波的模糊处理:增加了屏幕采样的次数

作业

1. 实现SSAO效果

跟着敲了一遍,内容见上述。

SSAO

2. 使用其他AO算法实现进行对比

比如HBAO
参考知乎:Ambient Occlusion环境遮罩1提到了以下AO算法
SSAO-Screen space ambient occlusion
SSDO-Screen space directional occlusion
HDAO-High Definition Ambient Occlusion
HBAO±Horizon Based Ambient Occlusion+
AAO-Alchemy Ambient Occlusion
ABAO-Angle Based Ambient Occlusion
PBAOVXAO-Voxel Accelerated Ambient Occlusion

git上找了个GTAO-Ground Truth Ambient Occlusion的算法
原理参考:UE4 Mobile GTAO 实现(HBAO续)
代码来源:Unity3D Ground Truth Ambient Occlusion

整体有点偏黑:

GTAO

技术美术知识学习4200:SSAO算法相关推荐

  1. 技术美术知识学习3600:纹理压缩

    学习教程来自:[技术美术百人计划]图形 3.6 纹理压缩--包体瘦身术 笔记总结 什么是纹理压缩 纹理压缩是为了解决内存.带宽问题,专门在计算机图形渲染系统中存储纹理而使用的图像压缩技术 为什么要纹理 ...

  2. 技术美术知识学习5200:光追相关概念介绍

    学习教程来自:[技术美术百人计划]图形 5.2 光线追踪.路径追踪.光线投射.光线步进介绍 笔记 1. 光线追踪 Ray Tracing 光栅化渲染:并行的按照物体->三角面->像素拆解每 ...

  3. 技术美术知识学习_06:关于法线贴图详解

    一.什么是法线贴图 法线贴图说明: 法线贴图就是在原物体的凹凸表面的每个点上均作法线,通过RGB颜色通道来标记法线的方向,你可以把它理解成与原凹凸表面平行的另一个不同的表面,但实际上它又只是一个光滑的 ...

  4. 技术美术知识学习_02:MatCap实现

    1. 原理: 1.1 Part 1:实现思路:MatCap实际上就是我们生活中洗衣服出现的肥皂泡现象,我们把它统称为薄膜干涉现象! 2. 实现过程: 1.1 Part 1:首先我们需要求出视线空间下的 ...

  5. 计算机等级考 之 三级网络技术 理论知识 学习

    前面look一下就好,后面才是码了好久的知识!!! 题型 及 分值比例 单选题    40T    40分    ,    综合题    4T    40分    ,    应用题    20分 上机 ...

  6. 庄懂技术美术shader学习 Lesson 07

    1.单色环境光 1colambient 2.3ColAmbient 3.3ColAmbient 代码 4.shader forge 投影 5.unity投影调用 6.学习内容整合OldSchool P ...

  7. 【技术美术图形部分】2.2 模型与材质基础

    记录之前膜拜一下这节课的大佬,才大三,我一个研二菜狗留下不学无术的泪水! May佬提到,这次课程安排的目的是给美术同学一个缓冲的空间,我的话在写这篇学习笔记就尽量加入一些自己的理解. 友情提示!才发现 ...

  8. 【技术美术图形部分】图形渲染管线2.0-GPU管线概述几何阶段

    图形渲染管线1.0 [技术美术知识储备]图形渲染管线1.0-基本概念&CPU负责的应用阶段 在上一篇中,从渲染分类开始介绍了什么是渲染流水线.为什么要有流水线以及流水线如何进行的,还介绍了CP ...

  9. 【技术美术图形部分】图形渲染管线3.0-光栅化和像素处理阶段

    写在前面 开始学习<入门精要>第八章透明效果后,接触到了渲染顺序.透明度测试这些概念,才发现之前的渲染管线总结忘掉了光栅化和像素处理这两个阶段的学习记录,怪不得没什么印象...赶紧写一篇补 ...

  10. 【技术美术图形部分】PBR全局光照:理论知识补充

    写在前面 最近做东西的流程是这样的,想实现一个风格化森林小场景,场景体现的主体是风格化树和交互草 --> 于是用了两天时间学SpeedTree做树模型 --> 用了两天时间SD做了树干贴图 ...

最新文章

  1. SQLServer查看存储过程的方法
  2. POJ1144——网络(求割点)
  3. 【配置文件】log4j是什么log4j
  4. 转!mysql 查询 distinct多个字段 注意!!
  5. mdkstc系列器件支持包下载_Find X2系列 Android 11 Beta1 测试版发布
  6. fedora16设置root登录
  7. 瑞幸:现在卖24元一杯已经很便宜了 以后还要降价
  8. visio 科学图形包_如何科学地做笔记
  9. js 中的 number 为何很怪异 1
  10. 跳跃回溯____寻找最长平台
  11. 计算机网络 校园网规划与设计方案,校园网设计方案—计算机网络课作业
  12. 数据结构实验报告——线性表
  13. matlab数字调制蒙特卡洛仿真,AWGN信道下数字通信系统的蒙特卡洛仿真(基于matlab)...
  14. OpenCV C++案例实战十九《制作电子相册查看器》
  15. 教师招聘计算机学科试题,2016年教师招聘考试《信息技术》练习试题二
  16. 构建之法之开篇之作——阅读与思考
  17. 计算字符串的长度(一个汉字算两个字符)
  18. STM32 USB无法连接电脑
  19. java程序会发生内存泄露吗及内存泄漏场景
  20. HEX文件校验和算法

热门文章

  1. C语言——逻辑运算符
  2. 计算机word虚线在哪里,在word中画虚线的五种方法
  3. 用C语言读把SGY地震数据读成txt
  4. 坐标转换-大地转高斯平面平面坐标转换
  5. thinkpad指点杆(trackpoint)在WPS的word文档中失效的解决办法
  6. 符合规则的c语言常量,c语言常量定义规则知识点总结
  7. Rabbitmq Plugin configuration unchanged. 问题完全解决方案
  8. 港股分时交易数据 API 接口
  9. 黑马程序员—选择黑马,是我前进的方向
  10. apkg格式怎么打开_pdf转图片怎么转?分享最简单的PDF转图片方法