SSAO By Computer Shader(三)

开启一个专题,SSAO By Computer Shader。使用Computer Shader实现SSAO效果。第一篇Computer Shader 入门 。第二篇SSAO理论知识。第三篇SSAO By Computer Shader,使用Computer Shader实现SSAO效果。


文章目录

  • SSAO By Computer Shader(三)
  • 前言
  • 一、准备工作---------代码创建
  • 二、实现流程
    • 1.Cs编写----------CommandBuffer设置渲染顺序
    • 2.Cs编写---------参数准备
    • 3.Computer Shader编写----------AO计算
    • 4.Computer Shader编写----------AO模糊
    • 4.Shader编写----------融合
    • 5.FrameDebugger分析
  • 总结

前言

开启一个专题,SSAO By Computer Shader。使用Computer Shader实现SSAO效果。第三篇SSAO By Computer Shader 。我们已经大致了解Computer Shader 的实际用法以及SSAO效果的实现原理,我们将两者结合一下。


一、准备工作---------代码创建


创建一个控制Computer Shader的脚本,以及两个Computer文件分别用于生成AO图以及模糊处理,最后一个Shader文件用于实际渲染屏幕。

二、实现流程

1.Cs编写----------CommandBuffer设置渲染顺序

首先我们需要修改Computer Shader 执行的顺序,我们希望他在所有对象渲染后再执行,但是我们不希望他受到屏幕后处理的影响,这里我们用到CommandBuffer 类。一个功能及其强大的模块类。命令缓冲列表,可以执行的图形命令。

我们将Compuetr Shader的控制权交给CommandBuffer。首先注册CommandBuffer的渲染顺序。我们在ImageEffect之前激活CommandBuffer,
ImageEffect 就是我们的屏幕效果流程。

代码如下(示例):

  void UnregisterCommandBuffers(){_camera.RemoveCommandBuffer(CameraEvent.BeforeImageEffects, _renderCommand);}void RegisterCommandBuffers(){_camera.AddCommandBuffer(CameraEvent.BeforeImageEffects, _renderCommand);}

2.Cs编写---------参数准备

在LateUpdate()函数中我们触发PushDownsampleCommands(_renderCommand),并将我们的CommandBuffer作为参数传入。
在PushDownsampleCommands函数中,我们主要做这几件事情。

首先第一步参数准备
我们需要生成随机点。我们调用GenSampleKernal()函数获取随机点。

我们将循环n个随机点,随机点的生成范围x,y轴(-1,1),z轴(0-1)
其次我们要对比深度信息,我们需要Computer Shader 中采样到深度图以及法线信息。怎么传进去是一个问题。
为此我们需要申请一张RT存放深度,法线信息,再传入Computer Shader。我们将设置渲染目标为我们申请的RT,_depthCopy。调用DrawProcedural()函数绘制几何体。并用我们bitMat的Pass1来执行渲染。
bitMat中Pass1很简单,单纯渲染法线深度图。

  Pass{CGPROGRAM#pragma vertex vert_procedural#pragma fragment fragsampler2D_float _CameraDepthNormalsTexture;float4 frag(v2f_img i) : SV_Target{return tex2D(_CameraDepthNormalsTexture, i.uv);}ENDCG}

得到这样效果。

这样我们的深度,法线信息就存入到_depthCopy 的RT中。
进入第二步参数传入

我们获取Computer Shader入口索引,CommandBuffer同样有提供Commputer Shader交互的接口。我们将深度法线图_depthCopy 传入,将_linearDepth 作为我们Computer Shader的输入输出,我们将对其写入数据。以及其他常规参数包括随机点集合,半圆半径的等参数。
最后我们激活 Computer Shader ,threadGroupsX,threadGroupsY,threadGroupsZ。我们线程组分配分别是屏幕分辨率/ 8 以及threadGroupsZ 1,简化维度。
以下是CS的完整代码

    void PushDownsampleCommands(CommandBuffer cmd){_sourse.PushAllocationCommand(cmd);cmd.Blit(BuiltinRenderTextureType.CurrentActive, _sourse.id);GenSampleKernal();_depthCopy.PushAllocationCommand(cmd);cmd.SetRenderTarget(_depthCopy.id);cmd.DrawProcedural(Matrix4x4.identity, _blitMaterial, 0, MeshTopology.Triangles, 3);_linearDepth.PushAllocationCommand(cmd);var cs = _upsampleCompute;var kernel = cs.FindKernel("CSMain");cmd.SetComputeTextureParam(cs, kernel, "LinearZ", _linearDepth.id);cmd.SetComputeTextureParam(cs, kernel, "Depth", _depthCopy.id);cmd.SetComputeVectorArrayParam(cs, "SamplePoint", samplePoint.ToArray());cmd.SetComputeFloatParam(cs, "Bias", bias);cmd.SetComputeFloatParam(cs, "Stength", strength);cmd.SetComputeFloatParam(cs, "CameraWidth", _camera.pixelWidth);cmd.SetComputeFloatParam(cs, "CameraHeight", _camera.pixelHeight);cmd.SetComputeFloatParam(cs, "SamplePointCount", samplePointCount);cmd.SetComputeFloatParam(cs, "SampleKernelRadius", sampleKernelRadius);cmd.SetComputeMatrixParam(cs, "InverseProjectionMatrix", _camera.projectionMatrix.inverse);cmd.DispatchCompute(cs, kernel, 166, 96, 1);_ao_result.PushAllocationCommand(cmd);var blur_cs = _blurCompute;var blur_kernel = blur_cs.FindKernel("CSMain");cmd.SetComputeTextureParam(blur_cs, blur_kernel, "AOTEX", _linearDepth.id);cmd.SetComputeTextureParam(blur_cs, blur_kernel, "AO_Result", _ao_result.id);cmd.SetComputeTextureParam(blur_cs, blur_kernel, "Depth", _depthCopy.id);cmd.SetComputeFloatParam(blur_cs, "BilaterFilterFactor", 1f - bilaterFilterStrength);cmd.SetComputeVectorParam(blur_cs, "BlurRadius", new Vector4(blurRadius, 0, 0, 0));cmd.DispatchCompute(blur_cs, blur_kernel, 166, 96, 1);cmd.SetGlobalTexture("_AOTexture", _ao_result.id);cmd.SetGlobalTexture("_MainTex", _sourse.id);cmd.Blit(BuiltinRenderTextureType.CurrentActive, BuiltinRenderTextureType.CameraTarget, _blitMaterial, 1);}

3.Computer Shader编写----------AO计算

我们来看看这个Computer Shader实现了什么逻辑

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain#include "UnityCG.cginc"
#include "noiseSimplex.cginc"
// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> LinearZ;
#define MAX_SAMPLE_POINT_COUNT 64
CBUFFER_START(CBO)float Bias;float Stength;float CameraWidth;float CameraHeight;float SamplePointCount;float SampleKernelRadius;float4x4 InverseProjectionMatrix;
CBUFFER_END
float4 SamplePoint[MAX_SAMPLE_POINT_COUNT];
Texture2D<float4> Depth;

同常规Shader一样,我们需要定义参数,分别是我们在cs那边传入的参数。LineZ 对应cs的_linearDepth,我们将其标记为可读可写,他将作为我们的返回值存储我们的AO值。
来看看我们的核心计算逻辑。

我们先看看一下上部分逻辑。差不多分为三块。
第一线程分配
首先我们要分配线程数numthreads(8,8,1)我们填的是8,8,1基本就是默认值。SV_DispatchThreadID (Int3)当前线程在所有线程组中所有线程里的ID。
第二部获取像素坐标对应视空间上的点。
首先这里的像素坐标就等于我们的id了,我们需要得到当前像素坐标的深度以及法线信息,我们可以直接采样传入_depthCopy得到。我们需要将当前像素坐标转化为视空间下的点,将其转到裁剪空间下再经过透视矩阵的逆矩阵变化到视空间下。乘上深度值得到像素坐标对应视空间上的点。
第三步,创建半圆空间转换矩阵TBN。
我们创建随机向量,减去 随机向量与法线点积乘上法线向量的结果,得到切线向量。通过叉积得到第三个向量组成我们的TBN矩阵。
我们来看下部分逻辑

同样分为三个部分,
第一部分生成采样点。
我们遍历随机点转换到半圆空间中,加上半圆空间原点生成半圆空间向量。再通过裁剪矩阵,归一化转换到UV空间生成我们的深度采样点。
第二步,深度比较
将采样点采样深度图得到采样深度,与原像素点深度做比较,大于返回1,小于返回0。再乘上一个范围权重,距离越近ao效果越明显。经过1-取反后强化深度效果写入我们的RT,LinearZ。
来看看frame debug的效果

我们我们的渲染流程发生在ImageEffect之前,右边分别是我们的Kernael名字,线程组分配,法线深度图,ao图以及其他参数。以上就是Computer Shader的逻辑以及实现效果了。
具体代码如下

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain#include "UnityCG.cginc"
#include "noiseSimplex.cginc"
// Create a RenderTexture with enableRandomWrite flag and set it
// with cs.SetTexture
RWTexture2D<float4> LinearZ;
#define MAX_SAMPLE_POINT_COUNT 64
CBUFFER_START(CBO)float Bias;float Stength;float CameraWidth;float CameraHeight;float SamplePointCount;float SampleKernelRadius;float4x4 InverseProjectionMatrix;
CBUFFER_END
float4 SamplePoint[MAX_SAMPLE_POINT_COUNT];
Texture2D<float4> Depth;[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{float3 col;// TODO: insert actual code here!//深度值float pPointDepth;//法线值float3 viewNormal;float4 depth = Depth[id.xy] ;DecodeDepthNormal(depth, pPointDepth, viewNormal);float2 uv_pos = float2(id.x / CameraWidth, id.y / CameraHeight);float4 clips = float4(uv_pos * 2.0  - 1.0, 1, 1);float4 viewRay = mul(InverseProjectionMatrix, clips);viewRay = viewRay / viewRay.w;float3 viewPos = pPointDepth * viewRay;float noise = perlin(uv_pos.x, uv_pos.y);float noise2 = perlin(uv_pos.x + 0.1, uv_pos.y + 0.1);float noise3 = perlin(uv_pos.x - 0.1, uv_pos.y - 0.1);float3 randvec = float3(noise, noise2, noise3);float3 tangent = normalize(randvec - viewNormal * dot(randvec, viewNormal));float3 bitangent = cross(viewNormal, tangent);float3x3 TBN = float3x3(tangent, viewNormal, bitangent);int sampleCount = SamplePointCount;float oc = 0.0;for (int i = 0; i < sampleCount; ++i){float3 randomVector = mul(TBN, SamplePoint[i].xyz);float3 randomPos = viewPos + randomVector * SampleKernelRadius;float3 rclipPos = mul((float3x3)unity_CameraProjection, randomPos);float2 rscreenPos = (rclipPos.xy / rclipPos.z) * 0.5 + 0.5;float samplePointDepth;float3 randomNormal;rscreenPos = float2(rscreenPos.x * CameraWidth, rscreenPos.y * CameraHeight);float4 rcdn = Depth[rscreenPos.xy] ;DecodeDepthNormal(rcdn, samplePointDepth, randomNormal);float rangeCheck = smoothstep(0.0, 1.0, SampleKernelRadius / abs(viewPos.z - samplePointDepth));oc += (pPointDepth >= samplePointDepth + Bias ? 1.0 : 0.0) * rangeCheck;}oc = 1.0 - oc / sampleCount;col = pow(oc, Stength);LinearZ[id.xy] = float4(col, 1);
}

4.Computer Shader编写----------AO模糊

进入下一个环节模糊处理

跟前面一样我们同样获取模糊的computer Shader的入口索引,以及参数传入。最后设置全局贴图,将AO图传入shader

模糊中,我们直接偏移多次UV坐标,多次采样然后取均值返回。
最后就是我们的屏幕渲染了。


以下是Blur Computer Shader的具体代码

// Each #kernel tells which function to compile; you can have many kernels
#pragma kernel CSMain#include "UnityCG.cginc"RWTexture2D<float4> AO_Result;
#define MAX_SAMPLE_POINT_COUNT 64
CBUFFER_START(CBO)
Texture2D<float4> AOTEX;
Texture2D<float4> Depth;
float BilaterFilterFactor;
float4 BlurRadius;
CBUFFER_ENDfloat3 GetNormal(float2 uv){//获得法线贴图float4 cdn = Depth[uv] ;return DecodeViewNormalStereo(cdn);}half CompareNormal(float3 normal1, float3 normal2){return smoothstep(BilaterFilterFactor, 1.0, dot(normal1, normal2));}[numthreads(8,8,1)]
void CSMain (uint3 id : SV_DispatchThreadID)
{float2 delta = BlurRadius.xy;float2 uv = id.xy;float2 uv0a = id.xy - delta;float2 uv0b = id.xy + delta;float2 uv1a = id.xy - 2.0 * delta;float2 uv1b = id.xy + 2.0 * delta;float2 uv2a = id.xy - 3.0 * delta;float2 uv2b = id.xy + 3.0 * delta;float3 normal = GetNormal(uv);float3 normal0a = GetNormal(uv0a);float3 normal0b = GetNormal(uv0b);float3 normal1a = GetNormal(uv1a);float3 normal1b = GetNormal(uv1b);float3 normal2a = GetNormal(uv2a);float3 normal2b = GetNormal(uv2b);fixed4 col = AOTEX[uv];fixed4 col0a = AOTEX[uv0a];fixed4 col0b = AOTEX[uv0b];fixed4 col1a = AOTEX[uv1a];fixed4 col1b = AOTEX[uv1b];fixed4 col2a = AOTEX[uv2a];fixed4 col2b = AOTEX[uv2b];half w = 0.37004405286;half w0a = CompareNormal(normal, normal0a) * 0.31718061674;half w0b = CompareNormal(normal, normal0b) * 0.31718061674;half w1a = CompareNormal(normal, normal1a) * 0.19823788546;half w1b = CompareNormal(normal, normal1b) * 0.19823788546;half w2a = CompareNormal(normal, normal2a) * 0.11453744493;half w2b = CompareNormal(normal, normal2b) * 0.11453744493;half3 result;result = w * col.rgb;result += w0a * col0a.rgb;result += w0b * col0b.rgb;result += w1a * col1a.rgb;result += w1b * col1b.rgb;result += w2a * col2a.rgb;result += w2b * col2b.rgb;result /= w + w0a + w0b + w1a + w1b + w2a + w2b;AO_Result[id.xy] = float4(result, 1);
}

4.Shader编写----------融合

在我们的渲染Shader中,我们已经知道ao图以及原图数据了,直接采样融合即可

 Pass{CGPROGRAM#pragma vertex vert_img2#pragma fragment fragsampler2D _AOTexture;sampler2D _MainTex;float4 frag(v2f_img i) : SV_Target{fixed ao = tex2D(_AOTexture, i.uv).r;fixed4 final_col = tex2D(_MainTex, i.uv);return final_col * ao;}ENDCG}

5.FrameDebugger分析

以下就是我们最终效果图

在帧事件中分别在这四个事件中完成

1.渲染深度图(shader完成)
2.计算ao值(computer shader完成)
3.模糊ao效果(computer shader完成)
4.融合(shader完成)


总结

以上就是SSAO By Computer 的全部内容,若有错误,欢迎纠正。

SSAO By Computer Shader(三)相关推荐

  1. Computer Shader

    ComputerShader是一种特殊的shader,得利于GPU强大的并行计算力,computershader可以轻松完成一些重复且量大的计算任务. ComputerShader用法 Compute ...

  2. 【Unity3D Shader编程】之十一 深入理解Unity5中的Standard Shader(三)屏幕像素化特效的实现

    本系列文章由@浅墨_毛星云 出品,转载请注明出处.    文章链接:  http://blog.csdn.net/poem_qianmo/article/details/50095705 作者:毛星云 ...

  3. Unity Shader着色器优化

    对游戏开发者而言,着色器长久以来就是游戏开发中的重要部分,在Unity中编写并实现着色器的过程直观且高效,优秀的着色器还可以创造非常精美的游戏画面,同时保证极高的性能.今天将由Unity的技术工程师张 ...

  4. 通过一个小Trick实现shader的像素识别/统计操作

    2018/12/14日补充:后来发现compute shader里用AppendStructuredBuffer可以解决这类问题,请看这里:https://www.cnblogs.com/hont/p ...

  5. 【西川善司的3D图形技术连载】GPU和Shader技术的基础知识(1~8回)

    本连载的主要目的,是介绍最新的PC和GAME所使用的最新3D图形技术的发展趋势. 暂时的方针是,首先是考虑介绍比较新的PC Game和PS3,XBOX360等新时代游戏机的游戏所采用的技术. 那么首先 ...

  6. 【西川善司】GPU和Shader技术的基础知识(全8回)

    本文取自西川善司的3D图形技术连载,全99回 本贴为1~8回,争取每1~2天更新一回吧.半年更新完. 也希望大家能支持. 翻译 Trace 校对&注解 千里马肝 http://www.open ...

  7. Shader|GPU流水线_该用户还没想到昵称_新浪博客

    Shader运行于GPU上. Shader的类型主要有:顶点着色器(Vertex Shader).细分曲面着色器(Tessellation Shader).几何着色器(Geometry Shader) ...

  8. UE4 Shader 框架(转载)

    Trace的空间 关注跨平台次世代游戏开发 博客园 首页 新随笔 联系 订阅 管理 随笔-88  文章-0  评论-69  关于Shader的跨平台方案的考虑 Apple 推出 metal后,除了新的 ...

  9. Shader处理角色皮肤特效接缝

    游戏角色皮肤要做流动特效,如果使用顶点uv对效果贴图进行采样,存在以下几个问题: 因为在展UV的时候,各个结构之间基本都是拆开的,流动效果会有明显的接缝问题,破坏了效果的完整性 同样因为展UV的原因, ...

最新文章

  1. 题目1254:N皇后问题(DFS)
  2. 【Mca架构】java互联网高级架构师
  3. 本地第一次对接已经存在的github上的repository
  4. 韩文博 php,No releases available for package pear.php.net/HTTP_Upload
  5. 截取AVI格式的视频C语言代码
  6. mysql调用短信接口_短信平台接口调用方法参考
  7. CAD(计算机辅助设计)
  8. 龙芯OpenJDK更新策略:没必要跟进小版本,最后大版本更新
  9. element ui 1.4 升级到 2.0.11
  10. java smtp pop3_Java基于smtp与pop3实现收发邮件的功能
  11. 整理12种电脑有趣屏保
  12. php5 安装imagick,为php安装imagick拓展与image magick图像处理库
  13. aardio - 【库】plusList——二次封装plus自绘组件库
  14. microsoft 365 E5申请过程收不到验证码的解决方案
  15. C语言求字母的全部组合
  16. (附源码)计算机毕业设计SSM游泳馆管理系统
  17. VMware workstation搭建华为FusionCompute实验环境(二)保姆级安装教程,可运行虚拟机
  18. 同位素标记其他的研究方法和技术
  19. ecshop smarty php,ecshop的smarty基础普及知识
  20. Altium designer别再傻傻的一个个给元件添加封装了

热门文章

  1. php 双向链表,Go实现双向链表
  2. 2019该怎么学unity3D游戏开发?
  3. vue-cli自动化测试karma + mocha + chai
  4. 什么是SQL注入攻击以及如何防止SQL注入攻击
  5. HTML、CSS、JS(JAVA Sprict)
  6. ubuntu系统下THETA S 全景相机 通过ROS导出图像
  7. 我的世界服务器怎么自己修改op权限,我的世界op权限指令
  8. c语言(http://c.biancheng.net/view/1714.html)
  9. Lucene IKAnalyzer(V2012)
  10. Java集合(超详细-含源码)