Gaussian Blur Cover

使用Group Shared Memory加速高斯模糊

2023年4月5日更新 再议高斯模糊,更实用的高斯模糊。 为什么要用Compute Shader来做高斯模糊 在之前的博客中我说Compute Shader的优势就是快,因为GPU的并行运算比CPU强大很多。但是相比于同样运行在GPU上的Fragment Shader之类,Compute Shader是不是就毫无优势了呢?答案是否定的,在DX11的文档中我们可以看到Thread Group Shared Memory这么一个概念,Compute Shader的Thread Group中的每一个Thread,都可以极快速的访问到对应的Group Shared Memory中的数据,这个效率比采样一张贴图来的高。因此在进行高斯模糊这样的需要大量贴图采样的计算时,先将贴图数据缓存到Group Shared Memory中,再多次访问Group Shared Memory,这样运行的效率会高得多。 相关的一些参考可以在英伟达的PPT里找到。 Compute Shader进行高斯模糊的具体操作 这里暂时不使用半分辨率的优化方法,目的是把SrcIden对应的RenderTexture经过高斯模糊储存到DestIden中,这时我们需要一张临时的相同大小的RenderTextureBlurIden。 确定需要的最大的高斯模糊的像素宽度MAX_RADIUS,越大的高斯模糊宽度,需要越大的Group Shared Memory,而Group Shared Memory的大小是有上限的(cs_5.0是32768 bytes)。当然也没有必要在全分辨率的情况下做特别大的高斯模糊就是了。这里我设置最大的高斯模糊像素数是32,即高斯模糊当前像素点和最远采样点之间的距离不能超过32(双线性采样的话还要缩小一个像素)。 高斯模糊往往使用水平和竖直两个高斯核心进行模糊,对应的需要两个Compute Shader Kernel(也可以写成一个,不过思考起来有点绕),我们这里设置两个Kernel,对应水平和竖直两个pass。水平Kernel使用[numthreads(64, 1, 1)],最高可以是1024,竖直Kernel则使用[numthreads(1, 64, 1)]。 由于高斯模糊中会采样像素点的左右(上下)两侧的像素,Group Shared Memory需要在GroupThreads的基础上向两侧扩大最大高斯模糊像素数MAX_RADIUS,用于保存额外的像素数据。这时我们需要的Group Shared Memory的大小是numthreads + 2 * MAX_RADIUS个,在本文章中是64 + 2 * 32个float3的数据。 GaussianBlur.cs 这里略去Unity SRP的设置pass的操作,仅展示高斯模糊相关的操作。当blurRadius过大时,就能看到明显的多重采样的痕迹了。 private void DoGaussianBlurHorizontal(CommandBuffer cmd, RenderTargetIdentifier srcid, RenderTargetIdentifier dstid, ComputeShader computeShader, float blurRadius) { int gaussianBlurKernel = computeShader.FindKernel("GaussianBlurHorizontalMain"); computeShader.GetKernelThreadGroupSizes(gaussianBlurKernel, out uint x, out uint y, out uint z); cmd....

August 19, 2021 · zznewclear13
zznewclear13 技术美术 图形学 个人博客 technical art computer graphics