内容简介
Unity提供的Image Effect开发接口,是通过MonoBehaviour.OnRenderImage (RenderTexture source, RenderTexture destination)提供的,其中source是引擎传入的没有经过本次处理的原始图像,而我们将把经过处理的结果图像绘制到destination上去。通过实现这个函数,在引擎最后把渲染结果显示之前,Unity给了开发者一个机会来对image进行处理。Unity自带的Bloom效果也是通过这种方式实现的,下面我们来一起剖析Bloom的实现原理。
缩减采样(Downsample)
为了提高后面处理过程的效率并且增强bloom的效果,Bloom的OnRenderImage函数首先对原始图像进行了2次缩减采样。关键代码如下:
Graphics.Blit (source, halfRezColorDown, screenBlend, 2);
RenderTexture rtDown4 = RenderTexture.GetTemporary (rtW4, rtH4, 0, rtFormat);
Graphics.Blit (halfRezColorDown, rtDown4, screenBlend, 2);
Graphics.Blit (rtDown4, quarterRezColor, screenBlend, 6);
RenderTexture.ReleaseTemporary(rtDown4);
第1行将原始图像source渲染到长、宽各缩为一半的RenderTexture halfRezColorDown中去,渲染时使用BlendForBloom这个shader的pass 2。我们来看一下这个shader的pass 2里做了些什么:
v2f_mt vertMultiTap( appdata_img v ) {
v2f_mt o;
o.pos = mul(UNITY_MATRIX_MVP, v.vertex);
o.uv[4] = v.texcoord.xy;
o.uv[0] = v.texcoord.xy + _MainTex_TexelSize.xy * 0.5;
o.uv[1] = v.texcoord.xy - _MainTex_TexelSize.xy * 0.5;
o.uv[2] = v.texcoord.xy - _MainTex_TexelSize.xy * half2(1,-1) * 0.5;
o.uv[3] = v.texcoord.xy + _MainTex_TexelSize.xy * half2(1,-1) * 0.5;
return o;
}
half4 fragMultiTapMax (v2f_mt i) : SV_Target {
half4 outColor = tex2D(_MainTex, i.uv[4].xy);
outColor = max(outColor, tex2D(_MainTex, i.uv[0].xy));
outColor = max(outColor, tex2D(_MainTex, i.uv[1].xy));
outColor = max(outColor, tex2D(_MainTex, i.uv[2].xy));
outColor = max(outColor, tex2D(_MainTex, i.uv[3].xy));
return outColor;
}
vertex shader中,将5个纹理坐标放入了顶点中,以便后继在fragment shader中取得插值来使用。可以看到o.uv[4]里保存的是顶点处的uv坐标,而o.[0]、o.[1]、o.[2]、o.[3]则保存了周围4个u、v各偏移半个像素对应的4个点处的uv值。那么这些uv坐标代表什么含义呢?
如上图所示,假设原始图像是8×8尺寸,图中黄色区域包括了原始图像的4个像素,绿色圆形区域代表像素中心。当我们将它渲染到一半尺寸,即4×4的图像时,每4个原始图像中的像素对应1个目标图像中的像素,如黄色区域所示,图中红色圆形代表目标像素的中心。当fragment shader填充目标图像像素时,(经过插值的)o.uv[4]对应该像素中心处的uv坐标,而o.[0]、o.[1]、o.[2]、o.[3]则正好对应原始图像中距目标像素中心最近的4个原始像素中心处的uv坐标。利用这些uv坐标对原始图形进行采样,就能够通过某种方式决定目标像素。就像
fragment shader中的代码所体现的,它利用经过插值的5个uv坐标对原始图像进行采样,然后取各个通道的最大值,最终输出目标像素。注意这里的downsample并不是常见的取四个相关的原始像素的平均值,而是取包括目标像素中心所在位置颜色的最大值,这样做的目的应该是人为扩大高亮区域。