(图片)高斯模糊 Shader

效果图:

高斯模糊前:
image
高斯模糊后:
image

高斯模糊

  • 什么是高斯模糊?
    高斯模糊,也叫高斯平滑,在CocosCreator中属于渲染后期图像处理效果。
    因为看起来像加了一层毛玻璃滤镜,也叫“毛玻璃效果”。
  • 实现原理
    高斯模糊的处理过程就是图像与自己的 正态分布卷积
    • 正态分布:正态分布是一种概率分布,有均匀分布、集中、对称的特性,在纹理中计算当前像素一定范围内的像素的权重,越靠近当前像素权重越大,形成一个符合正态分布的权重矩阵。
    • 卷积:卷积会将当前像素的颜色与周围像素的颜色按比例融合,得到一个比较均匀的颜色。卷积计算有专门的模版公式(卷积核)。
    • 通俗一点讲:遍历纹理像素,把当前像素一定范围内的颜色按比例混合,越靠近当前像素的颜色比例越高(正态分布),得到一个均匀模糊的效果。在计算中采集的颜色的范围越大(像素越多),图像越模糊。

代码实现

copy一个butin-sprite的内置shader进行编写:

  • YAML 流程控制清单
    其他的不用改动,配置一个节点尺寸的属性,用于在片元着色中计算顶点位置
    properties:
        alphaThreshold: { value: 0.5 }
        size: { value: [500.0, 500.0], editor: { tooltip: '节点尺寸' } }
    
  • vs顶点着色器
    顶点部分不做处理,把顶点坐标和颜色信息传递给下一个着色器。
  • fs片元着色器
    拿到从顶点着色器传过来的顶点和颜色,以及纹理和节点尺寸的信息。
    定义一个常量RADIUS作为像素采样范围的半径
    定义一个函数 getBlurColor 来计算 模糊后的颜色并返回
    // 获取模糊颜色
    vec4 getBlurColor (vec2 pos) {
      vec4 color = vec4(0); // 初始颜色
      float sum = 0.0; // 总权重
      // 卷积
      for (float r = -RADIUS; r <= RADIUS; r++) { // 水平方向
    	for (float c = -RADIUS; c <= RADIUS; c++) { // 垂直方向
    	  vec2 target = pos + vec2(r / size.x, c / size.y); // 目标像素位置
    	  float weight = (RADIUS - abs(r)) * (RADIUS - abs(c)); // 卷积计算权重公式
    	  color += texture2D(texture, target) * weight; // 累加颜色
    	  sum += weight; // 累加权重
    	}
      }
      color /= sum; // 平均值
      return color;
    }
    
  • 完整shader
// Copyright (c) 2017-2020 Xiamen Yaji Software Co., Ltd.
CCEffect %{
  techniques:
  - passes:
    - vert: sprite-vs:vert
      frag: sprite-fs:frag
      depthStencilState:
        depthTest: false
        depthWrite: false
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one_minus_src_alpha
          blendDstAlpha: one_minus_src_alpha
      rasterizerState:
        cullMode: none
      properties:
        alphaThreshold: { value: 0.5 }
        size: { value: [500.0, 500.0], editor: { tooltip: '节点尺寸' } }
}%

CCProgram sprite-vs %{
  precision highp float;
  #include <builtin/uniforms/cc-global>
  #if USE_LOCAL
    #include <builtin/uniforms/cc-local>
  #endif
  #if SAMPLE_FROM_RT
    #include <common/common-define>
  #endif
  in vec3 a_position;
  in vec2 a_texCoord;
  in vec4 a_color;

  out vec4 v_color;
  out vec2 v_uv0;

  vec4 vert () {
    vec4 pos = vec4(a_position, 1);

    #if USE_LOCAL
      pos = cc_matWorld * pos;
    #endif

    #if USE_PIXEL_ALIGNMENT
      pos = cc_matView * pos;
      pos.xyz = floor(pos.xyz);
      pos = cc_matProj * pos;
    #else
      pos = cc_matViewProj * pos;
    #endif

    v_uv0 = a_texCoord;
    #if SAMPLE_FROM_RT
      CC_HANDLE_RT_SAMPLE_FLIP(v_uv0);
    #endif
    v_color = a_color;

    return pos;
  }
}%

CCProgram sprite-fs %{
  precision highp float;
  #include <builtin/internal/embedded-alpha>
  #include <builtin/internal/alpha-test>

  in vec4 v_color;
  uniform Properties {
    vec2 size;
  };
    // 模糊半径
  // for 循环的次数必须为常量
  const float RADIUS = 20.0;

  #if USE_TEXTURE
    in vec2 v_uv0;
    #pragma builtin(local)
    layout(set = 2, binding = 12) uniform sampler2D cc_spriteTexture;
  #endif

  // 获取模糊颜色
  vec4 getBlurColor (vec2 pos) {
    vec4 color = vec4(0); // 初始颜色
    float sum = 0.0; // 总权重
    // 卷积过程
    for (float r = -RADIUS; r <= RADIUS; r++) { // 水平方向
      for (float c = -RADIUS; c <= RADIUS; c++) { // 垂直方向
        vec2 target = pos + vec2(r / size.x, c / size.y); // 目标像素位置
        float weight = (RADIUS - abs(r)) * (RADIUS - abs(c)); // 卷积计算权重公式
        color += texture2D(cc_spriteTexture, target) * weight; // 累加颜色
        sum += weight; // 累加权重
      }
    }
    color /= sum; // 求出平均值
    return color;
  }

  vec4 frag () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      o *= CCSampleWithAlphaSeparated(cc_spriteTexture, v_uv0);
      #if IS_GRAY
        float gray  = 0.2126 * o.r + 0.7152 * o.g + 0.0722 * o.b;
        o.r = o.g = o.b = gray;
      #endif
    #endif
    o = getBlurColor(v_uv0);
    o.a = v_color.a;//还原透明度
    //o *= v_color;
    ALPHA_TEST(o);
    return o;
  }
}%

UI实现

  1. 新建一个材质,shader选择刚创建的shader,勾选USE_TEXTURE(不然代码中获取不到texture)
  2. 创建一个要模糊的精灵,挂载自定义的材质。

备注

该案例基于3.7.4版本,需要在功能裁剪中取消勾选WebGL2.0,否则代码编译不通过(不清楚原因)

posted @ 2024-11-26 11:26  EricShx  阅读(506)  评论(0)    收藏  举报