Unity PostProcessing : Boxblur
写在前面:
本文章为个人学习笔记,方便以后自己复习,也希望能帮助到他人。
由于本人水平有限难免出现错误,还请评论区指出,多多指教。
部分图元和素材来源于网络,如有侵权请联系本人删除。
参考资料与链接会在文章末尾贴出。
=======================================================================
先上效果:
Boxblur
最简单的模糊形式是方框模糊,它只取正方形区域的平均值并显示它。要访问源纹理上的许多不同点,我们只需使用 for 循环遍历它们。在读取不同位置的颜色后,我们将其添加到颜色变量中。然后在添加所有纹理颜色后,我们除以我们添加的样本量以获得平均值。
我们可以将着色器与我们为上一教程制作的后处理脚本一起使用。
Boxblur算是最简单的一种blur方法,简单来说就是计算并显示规定大小正方形区域内颜色的平均值。要访问源纹理上的许多不同点,我们只需使用 for 循环遍历它们。在读取不同位置的颜色后,我们将其添加到颜色变量中。然后在添加所有规定位置的纹理颜色后,把结果除以我们刚才添加的样本量以获得平均值。
比如就像这样:
1D Blur
上述代码因为我们只是在同一点读取纹理 10 次,然后除10,实际上我们的着色器并没有任何效果,所以下一步是从屏幕上的不同位置读取。为此,我们添加了一个名为模糊大小的属性量,依次来控制shader模糊图像的程度。该变量将会相对于当前屏幕的大小来改变我们获取颜色的矩形的大小,之所以通过采用相对于屏幕的大小而不是以像素为单位设置,是为了确保blur在不同分辨率下看起来有一致的效果。
这里我们将计算每个样本的自定义 uv 位置。之所以我们要减去 0.5,是因为我们希望采样某一点周围的点(可以想象一下原点的负半轴和正半轴),因此它是从 -0.5 到 +0.5。然后我们还将该结果与上面模糊大小变量相乘以进一步控制模糊的程度:
C#脚本:
这时我们得到了沿 y 轴模糊的效果,我们还希望沿 x 轴模糊。我们自然而然想到用 for 循环嵌套在另一个 for 循环中并遍历正方形中的所有点,但在这里还有更好的方法。我们可以获取刚刚执行的 blit 的结果,然后沿 x 轴执行第二个 blit。也就是说,先得到沿 y 轴模糊的图像,再处理 x 轴。
2D Blur
对于第二次说 blit,我们编写一个新的shader pass。
可以看到计算与上一个pass大体相同,但这里我们所做的另一个改变是我们将偏移乘以屏幕宽高比(ratio)的倒数,这样样本之间的偏移的距离在 x 轴和 y 轴中是相同的!
要使用这两个pass,我们还需要修改我们的 C# 脚本。因为我们在 y 轴之后和 x 轴之前有一个“临时结果”,所以我们必须使用新的渲染纹理。为此,我们使用 RenderTexture.GetTemporary 这个函数,请求一张渲染纹理,Unity 将在后台管理池化。然后我们为第一个 blit 函数添加第四个参数 0。第四个参数是shader的pass,在我们的shader中,第一个pass是我们的 y 轴pass。 blit 必须从源纹理中读取并写入我们的临时纹理。然后在第一次 blit 之后,我们执行另一个 blit,它将从临时的(y轴模糊)的纹理中读取并写入目标纹理,并使用索引 1 (即使用第二个pass)x轴模糊。搞定模糊纹理后,我们别忘记释放临时纹理(有借有还好习惯)。
Customize Sample Amount
上面代码,通过一个10 * 10 的Boxblur我们获得了一个简单的模糊效果。
接下来我们尝试继续拓展一下,我们用多重编译(multi compile)预设几个模糊质量的版本。
通过这种更改,可以很容易地调整代码中的示例,但我们可以更进一步,让它们在inspector中也可以更改。首先,我们使用 KeywordEnum 这个propertydrawer添加一个属性,该属性显示不同的可能性并在着色器中设置相应的关键字。
注意multi_compile的关键字跟KeywordEnum属性的名字是对应的。
然后在cgprogram中我们可以用multi_compile来声明shader,而其会被编译成多种“可能的效果”。多重编译的声明可以参考下面所示:
KeywordEnum(Low,Medium,High)]_Sample ("Sample amount",float) = 0 ... #pragma multi_compile _SAMPLE_LOW _SAMPLE_MEDIUM _SAMPLE_HIGH ... float _Sample; #if _SAMPLE_LOW #define SAMPLES 10 #elif _SAMPLE_MEDIUM #define SAMPLES 50 #else #define SAMPLES 100 #endif ... float4 frag (v2f i) : SV_Target { _Sample =SAMPLES; ... }
通过此设置,我们可以根据关键字设置会发生什么。比如现在,我们根据关键字更改 samples 变量。这里要注意的是添加多重编译声明和不同的样本数量到两个shader pass!
我们现在成功地实现了一个Boxblur shader,且可以改变它的质量。