基于RenderTexture实现多Pass Dual Kawase Blur

基于RenderTexture实现多Pass Dual Kawase Blur

DualKawaseBlur
实现思路完全参考皮皮大佬的多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

9赞

点赞~效果看起来不错呀

膜拜大佬~

:cow::beer: :cow::beer: :cow::beer:

楼主你好,我修改了一下你的方法,增加了一个节点,两个节点取的同一个图集但是并不能合批,是我哪里操作有问题吗?


我的理解是处理后的rt是单独的,已经和原来的图集无关了,所以不能合批,只是支持模糊合图里的纹理。 :joy:

原来是这样,感谢回答 :joy:
我在学习你代码里的那个glitch效果,感觉挺炫酷的,就是不知道怎么入手

+1,只能对着代码一点一点抄了,主要不会unity,好多代码看不懂。

:joy:

交个作业,抄了一个水平的rgbsplit,但是感觉我哪里的参数没调对,总觉得绿了吧唧的 :joy:
newdadsa

改了个noise的方式,感觉好些了,就是他扰动的rb不能像演示里面飞出屏就很尬,是因为2d和3d的区别么?还是需要用摄像机截图再处理再渲染的方式?

大佬行动力惊人 :heart_eyes:可能是2d3d的区别吧,这个不需要截图,截图主要是为了解决多pass,其他细节我就不懂了 :joy:

大佬。。。双pass 模糊是用两个rendertexture来搞的。。那如果我图片本身是有透明挖孔的。。第二次模糊岂不是就变成纯色了么,这种怎么处理

:joy:你是指原rt就带有透明像素,希望模糊后保留透明度是吗

本身有挖孔指的是图片本身有透明区域,还是额外做了挖孔效果?

试了下,透明像素没毛病

我来提交一个3.4.1可以用的,用大佬的effect改的,大佬可以帮我看看有没有问题嘛,暂时还没用上大佬的代码处理图片,这个可以直接作用在材质上,挂载到图片立马就有效果,但是效果和大佬的好像有点差别
@1203790244 @ifaswind
kawase.zip (1.4 KB)

1赞

effect应该没啥问题,3.x我还没用过,2.x不支持多pass,所以升降的shader我分成了2个techniques,你直接挂在图片上的话,应该只是实现了升或降的单次模糊(单看效果,直接用1个好像也没啥问题,不过原算法是这样的,我也不好瞎改),3.x好像支持多pass,所以实现的方式可以改一改。还有一个,自定义顶点我后来去掉了,因为现在处理前会先截图得到原始的rt,此时纹理已经与原来没有关系了,所以没用上,如果其他使用情景下有需要的话可以留着。

好的好的,我自己试试

:+1:
给大佬点赞

3.6.3 android原生平台无效,不知道有人遇到了吗?