基于RenderTexture实现多Pass Dual Kawase Blur
实现思路完全参考皮皮大佬的多pass kawase blur,通过rendertexture反复横跳,实现降采样和升采样。为了支持合图时也能使用,修改了顶点数据。
示例代码
// Dual Kawase Blur (双重模糊)
CCEffect %{
techniques:
- name: Down
passes:
- name: Down
vert: vs:Down
frag: fs:Down
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties: &prop
texture: { value: white }
resolution: { value: [1920, 1080] }
offset: { value: 1, editor: { range: [0, 100] }}
alphaThreshold: { value: 0.5 }
- name: Up
passes:
- name: Up
vert: vs:Up
frag: fs:Up
blendState:
targets:
- blend: true
rasterizerState:
cullMode: none
properties: *prop
}%
CCProgram vs %{
precision highp float;
#include <cc-global>
#include <cc-local>
in vec3 a_position;
in vec4 a_color;
out vec4 v_color;
#if USE_TEXTURE
in vec2 a_uv0;
in vec2 a_p;
in vec2 a_q;
out vec2 v_uv0;
out vec4 v_uv1;
out vec4 v_uv2;
out vec4 v_uv3;
out vec4 v_uv4;
out vec2 v_p;
out vec2 v_q;
#endif
uniform Properties {
vec2 resolution;
float offset;
};
vec4 Down () {
vec4 pos = vec4(a_position, 1);
#if CC_USE_MODEL
pos = cc_matViewProj * cc_matWorld * pos;
#else
pos = cc_matViewProj * pos;
#endif
#if USE_TEXTURE
vec2 uv = a_uv0;
vec2 texelSize = 0.5 / resolution;
v_uv0 = uv;
v_uv1.xy = uv - texelSize * vec2(1. + offset); //top right
v_uv1.zw = uv + texelSize * vec2(1. + offset); //bottom left
v_uv2.xy = uv - vec2(texelSize.x, -texelSize.y) * vec2(1. + offset); //top right
v_uv2.zw = uv + vec2(texelSize.x, -texelSize.y) * vec2(1. + offset); //bottom left
#endif
v_p = a_p;
v_q = a_q;
v_color = a_color;
return pos;
}
vec4 Up () {
vec4 pos = vec4(a_position, 1);
#if CC_USE_MODEL
pos = cc_matViewProj * cc_matWorld * pos;
#else
pos = cc_matViewProj * pos;
#endif
#if USE_TEXTURE
vec2 uv = a_uv0;
vec2 texelSize = 0.5 / resolution;
v_uv0 = uv;
v_uv1.xy = uv + vec2(-texelSize.x * 2., 0) * offset;
v_uv1.zw = uv + vec2(-texelSize.x, texelSize.y) * offset;
v_uv2.xy = uv + vec2(0, texelSize.y * 2.) * offset;
v_uv2.zw = uv + texelSize * offset;
v_uv3.xy = uv + vec2(texelSize.x * 2., 0) * offset;
v_uv3.zw = uv + vec2(texelSize.x, -texelSize.y) * offset;
v_uv4.xy = uv + vec2(0, -texelSize.y * 2.) * offset;
v_uv4.zw = uv - texelSize * offset;
#endif
v_p = a_p;
v_q = a_q;
v_color = a_color;
return pos;
}
}%
CCProgram fs %{
precision highp float;
#include <alpha-test>
#include <texture>
#include <output>
in vec4 v_color;
#if USE_TEXTURE
in vec2 v_uv0;
in vec4 v_uv1;
in vec4 v_uv2;
in vec4 v_uv3;
in vec4 v_uv4;
in vec2 v_p;
in vec2 v_q;
uniform sampler2D texture;
#endif
uniform Properties {
vec2 resolution;
float offset;
};
vec2 normalizeUV(vec2 uv) {
// 模糊后图片边缘因采样到黑色像素导致有黑边,暂时不知道其他处理方案
vec2 newUV = clamp(uv * v_p + v_q, 0.002, 0.998);
return (newUV - v_q) / v_p;
}
vec4 Down () {
vec4 sum = vec4(1);
#if USE_TEXTURE
sum = texture2D(texture, normalizeUV(v_uv0)) * 4.;
sum += texture2D(texture, normalizeUV(v_uv1.xy));
sum += texture2D(texture, normalizeUV(v_uv1.zw));
sum += texture2D(texture, normalizeUV(v_uv2.xy));
sum += texture2D(texture, normalizeUV(v_uv2.zw));
sum *= 0.125;
#endif
sum *= v_color;
ALPHA_TEST(sum);
return CCFragOutput(sum);
}
vec4 Up () {
vec4 sum = vec4(1);
#if USE_TEXTURE
CCTexture(texture, normalizeUV(v_uv1.xy), sum);
sum += texture2D(texture, normalizeUV(v_uv1.zw)) * 2.;
sum += texture2D(texture, normalizeUV(v_uv2.xy));
sum += texture2D(texture, normalizeUV(v_uv2.zw)) * 2.;
sum += texture2D(texture, normalizeUV(v_uv3.xy));
sum += texture2D(texture, normalizeUV(v_uv3.zw)) * 2.;
sum += texture2D(texture, normalizeUV(v_uv4.xy));
sum += texture2D(texture, normalizeUV(v_uv4.zw)) * 2.;
sum *= 0.0833;
#endif
sum *= v_color;
ALPHA_TEST(sum);
return CCFragOutput(sum);
}
}%
代码实现
https://github.com/RicardoZhou/CreatorStudy/tree/master/assets/Menu/Shader/DualBlur
遇到的问题
- 合图后,模糊时偏移采样的uv需自行归一化后才可使用,否则会采样到大图上的其他纹理;
- 虽然uv已归一化处理,但最终效果会有黑边(不知道什么原因),归一化时采用 clamp(uv * v_p + v_q, 0.002, 0.998) 处理;
- 某些情况下,rendertexture渲染的纹理,部分像素透明度不是255(遇到的情况:截图时包含spine、label),可在shader中,强制将透明度转为255来解决;
- 降采样过程中会缩小纹理尺寸,需记录每次采样时的尺寸,,且存在迭代次数上限,否则会显示纯色;
参考链接:
高品质后处理:十种图像模糊算法的总结与实现
自定义顶点格式
基于 RenderTexture 实现多 Pass Kawase Blur