使用Group Shared Memory加速径向模糊
径向模糊 在写了上一篇文章径向分派Compute Shader之后,很自然的就需要用径向模糊来检验我们的算法。 径向模糊的效果在网上可以说是一找一大堆了,百分之九十五的教程,会使用一个很大的循环重复采样纹理,百分之四点九的教程,会使用多pass的方法减少采样的次数(当然这些数字是我瞎编的,我还留了百分之零点一,以防真的有人也用Compute Shader做了径向模糊)。这篇文章就来探讨如何使用Compute Shader中的Group Shared Memory来加速径向模糊的计算。我们的目标是在一个Compute Shader中实现超大模糊距离(甚至能超过128像素)的径向模糊。 缓存颜色信息 照例我们要再看一遍索尼的Bend Studio的Graham Aldridge在Sigraph 2023的索尼创作者大会上,介绍的往日不再(Days Gone)中计算屏幕空间接触阴影的方式,这里可以找到演示文稿和参考代码。演示文稿的第24-25页介绍了如何在Group Shared Memory中缓存深度信息,当然在径向模糊中我们缓存的是颜色信息了。 下图是缓存颜色信息的示意图,绿色方块对应的像素是我们整个Thread Group的起点,红色方块是我们当前计算的像素,黑色和灰色的线代表了该像素到径向模糊中心的方向。我们的Thread Group Size是12,深蓝色(包括绿色和红色)的像素同属于一个Thread Group,我们需要计算这些像素径向模糊后的颜色。深蓝色、浅蓝色、灰色和浅灰色、镂空的灰色和浅灰色,代表了我们需要缓存的颜色,因为我们需要对缓存的颜色进行双线性插值,我们除了需要缓存射线经过的最近的像素(即蓝色区域)外,还需要缓存射线经过的第二近的像素(即灰色区域)和射线经过的第三近的像素(即镂空灰色区域)。也就是说当Thread Group Size为12时,我们需要缓存6 * 12个像素,亦即每个像素六次采样。 仅缓存第二近的像素在大部分情况下能够得到正确的双线性插值,但是注意看红色方块向右的第二次采样,仅对蓝色和实心灰色区域的颜色进行插值是不能得到正确的颜色的,因此我们需要额外缓存镂空灰色区域的颜色,亦即第三近的像素。 Group Shared Memory分配 如上所述,我们的Group Shared Memory的大小是THREAD_COUNT * 6(懒得把THREAD_COUNT统一改成THREAD_GROUP_SIZE了),其中包含了上述的深蓝色、浅蓝色、灰色、浅灰色、镂空灰色、镂空浅灰色区域对应像素的颜色信息。数组的长度是THREAD_COUNT * 6,在我们缓存时,我们会将其视为6行THREAD_COUNT列的表来储存颜色信息,而在我们读取时,我们会将其视为3行THREAD_COUNT*2列的表来读取数据。 groupshared float3 cachedColor[THREAD_COUNT * 6]; void SetCachedColor(float3 color, int2 threadPos) {cachedColor[threadPos.x+threadPos.y*THREAD_COUNT]=color;} float3 GetCachedColor(int2 threadPos) {return cachedColor[threadPos.x+threadPos.y*THREAD_COUNT*2];} 具体的代码 RadialDispatchComputeShader.compute 核心的代码来自于上一篇文章径向分派Compute Shader。循环读取颜色值的方式有很多,可以严格按照格点来读取,这样只需要对y方向做线性插值,也可以按照统一的步长来读取,这样需要对xy方向都做线性插值,我这里使用的是统一的步长。每一组缓存的起始像素是最接近于射线的像素的下面一个像素,读取缓存使用的偏移是用的当前采样点下第一个像素的坐标和当前缓存列最下面像素的坐标相减,这里面比较绕不太好描述。。。 #pragma kernel RadialBlurMain // #pragma warning(disable: 3556) #define THREAD_COUNT 128 Texture2D<float4> _ColorTex; RWTexture2D<float4> _RW_TargetTex; float2 _CenterPosSS; float4 _TextureSize; float _Intensity; struct DispatchParams { int2 offset; int count; int stride; int xMajor; }; StructuredBuffer<DispatchParams> _DispatchData; int GetDispatchType(int index, out int dispatchIndex, out DispatchParams dispatchParams) { for (int i=0; i<8; ++i) { dispatchParams = _DispatchData[i]; dispatchIndex = dispatchParams....