Cocos Creator 自定义 Effect:用 Shader 程序化生成可调条纹背景

在 UI 开发里,常见的背景通常是美术切好的 PNG/JPG。

但对于一些简单的规则纹理(比如条纹、栅格、渐变),完全可以用 Shader 程序化生成,这样:

  • 不占贴图内存;
  • 可以在 Inspector 上实时调参数;
  • 同一套 Shader 可以复用在很多地方(不同颜色、角度、密度)。

这篇文章是一个 Cocos Creator 自定义 Effect 的实战:

用一个简单的片元着色器生成"双色条纹背景",支持:

  • 双色配置;
  • 条纹数量(密度);
  • 整体旋转角度;
  • 边缘平滑度(硬切换 / 柔和过渡);
  • 透明 / 不透明两种渲染模式。

完整文件:BackgroundCreate.effect


一、Effect 结构概览

Effect 文件由三部分组成:

  1. CCEffect:定义 techniques / passes / properties;
  2. CCProgram background-vs:顶点着色器;
  3. CCProgram unlit-fs:片元着色器(真正生成条纹的地方)。

1. Techniques 与 Passes

yaml 复制代码
CCEffect %{
  techniques:
  - name: opaque
    passes:
    - vert: background-vs:vert
      frag: unlit-fs:frag
      properties: &props
        colorA:        { value: [1, 1, 1, 1], editor: { type: color } }
        colorB:        { value: [0.85, 0.85, 0.85, 1], editor: { type: color } }
        stripeCount:   { value: 10 }
        rotateDegrees: { value: 0 }
        smoothness:    { value: 0.0 }

  - name: transparent
    passes:
    - vert: background-vs:vert
      frag: unlit-fs:frag
      depthStencilState:
        depthTest: false
        depthWrite: false
      blendState:
        targets:
        - blend: true
          blendSrc: src_alpha
          blendDst: one_minus_src_alpha
          blendSrcAlpha: src_alpha
          blendDstAlpha: one_minus_src_alpha
      properties: *props
}%

这里定义了两套 technique:

  • opaque:不透明背景;
  • transparent:透明混合背景(启用了 blend: true 和 alpha blend)。

二者共用同一套 props

  • colorA:条纹颜色 A;
  • colorB:条纹颜色 B;
  • stripeCount:条纹数量(密度);
  • rotateDegrees:整体旋转角度(度);
  • smoothness:条纹边缘柔和度。

这些属性会直接在 Creator 编辑器里暴露出来,可调。


二、顶点着色器:传入 UV 和本地坐标

glsl 复制代码
CCProgram background-vs %{
  precision highp float;
  #include <builtin/uniforms/cc-global>
  #if USE_LOCAL
    #include <builtin/uniforms/cc-local>
  #endif

  in vec3 a_position;
  in vec2 a_texCoord;

  out vec2 v_uv;
  out vec3 v_position;
  out vec2 v_local;

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

    #if USE_LOCAL
      pos = cc_matWorld * pos;
    #endif

    // MVP 变换
    pos = cc_matViewProj * pos;

    // 传递 UV 给片元着色器
    v_uv = a_texCoord;
    // 传递本地坐标(如果需要按模型空间做归一化,可以用 v_local)
    v_local = a_position.xy;

    return pos;
  }
}%

要点:

  • 输入:a_positiona_texCoord
  • 输出给片元着色器:
    • v_uv:标准 UV(0~1),用于条纹计算;
    • v_local:模型本地坐标,如果想按几何尺寸做更复杂的效果可以用,这里暂时没用到;
  • 使用 cc_matWorldcc_matViewProj 做常规的世界变换和投影。

对当前这个效果来说,顶点着色器的责任非常简单:
把顶点送到屏幕上,并把 UV 传到片元阶段即可。


三、片元着色器:用正弦波生成条纹

真正有意思的部分在 unlit-fs

glsl 复制代码
CCProgram unlit-fs %{
  precision highp float;
  #include <builtin/uniforms/cc-global>
  #include <legacy/output>
  #include <legacy/fog-fs>

  in vec2 v_uv;
  in vec3 v_position;
  in vec2 v_local;

  uniform Constant {
    vec4 colorA;
    vec4 colorB;
    float stripeCount;
    float rotateDegrees;
    float smoothness;
  };

  vec4 frag () {
    vec2 p = v_uv;

    // 旋转
    float rad = radians(rotateDegrees);
    float c = cos(rad);
    float s = sin(rad);
    vec2 rp = vec2(c * p.x - s * p.y, s * p.x + c * p.y);

    // 使用正弦波的符号切换颜色(每跨越 π 即切换一次)
    float wave = sin(3.14159265359 * stripeCount * rp.x);

    // 边缘处理:smoothness==0 则硬切换,否则平滑过渡
    float t = smoothness > 0.0 ? smoothstep(-smoothness, smoothness, wave) : step(0.0, wave);
    vec4 col = mix(colorA, colorB, t);

    return CCFragOutput(col);
  }
}%

逐行拆一下核心逻辑。

1. 旋转 UV 坐标

glsl 复制代码
vec2 p = v_uv;

float rad = radians(rotateDegrees);
float c = cos(rad);
float s = sin(rad);
vec2 rp = vec2(c * p.x - s * p.y, s * p.x + c * p.y);
  • v_uv 是原始 UV(0~1 范围);
  • 把角度 rotateDegrees 转成弧度,用标准 2D 旋转矩阵对 UV 坐标进行旋转:
    • 这样条纹原本沿 x 方向分布(垂直条纹),旋转后可以得到任意角度的条纹;
  • rp 是旋转后的坐标,用于后续条纹计算。

2. 用正弦波生成条纹模式

glsl 复制代码
float wave = sin(3.14159265359 * stripeCount * rp.x);
  • stripeCount:条纹数量;
  • 沿着 rp.x 方向取正弦波:sin(π * stripeCount * x)
  • 每跨越 π 就完成一个"高→低→高"的周期;
  • 之后只关心 wave 的正负(符号),用来决定当前像素用 colorA 还是 colorB

3. 条纹边缘硬切换 / 平滑过渡

glsl 复制代码
float t = smoothness > 0.0 ? smoothstep(-smoothness, smoothness, wave) : step(0.0, wave);
vec4 col = mix(colorA, colorB, t);
  • smoothness == 0.0
    • 使用 step(0.0, wave)
    • wave >= 0 → t = 1;wave < 0 → t = 0;
    • 条纹边缘是硬切换(类似硬边界)。
  • smoothness > 0.0
    • 使用 smoothstep(-smoothness, smoothness, wave)
    • wave 从负到正的过渡区域里平滑插值;
    • 条纹边缘会带一点模糊渐变,更柔和。

最后用 mix(colorA, colorB, t) 在两种颜色之间插值:

  • t = 0colorA
  • t = 1colorB
  • 中间值 → 两者混合。

四、Inspector 上可调的几个参数

在 Effect 里定义的 properties 决定了编辑器里能看到哪些可调项:

yaml 复制代码
properties: &props
  colorA:        { value: [1, 1, 1, 1], editor: { type: color } }
  colorB:        { value: [0.85, 0.85, 0.85, 1], editor: { type: color } }
  stripeCount:   { value: 10 }
  rotateDegrees: { value: 0 }
  smoothness:    { value: 0.0 }

在 Creator 中挂上这个材质后,可以直接调:

  • colorA / colorB:两种条纹颜色;
  • stripeCount
    • 小值 → 条纹宽、数量少;
    • 大值 → 条纹窄、数量多;
  • rotateDegrees
    • 0 度:竖直条纹;
    • 90 度:水平条纹;
    • 任意角度:斜向条纹;
  • smoothness
    • 0:极硬边界,像 UI 分割纹理;
    • 稍大一些:边缘渐变,类似光滑过渡。

五、透明 / 不透明两种模式

opaquetransparent 两个 technique 的区别在于:

  • opaque:默认深度写入、不混合,适合做普通背景;

  • transparent

    yaml 复制代码
    depthStencilState:
      depthTest: false
      depthWrite: false
    blendState:
      targets:
      - blend: true
        blendSrc: src_alpha
        blendDst: one_minus_src_alpha
        blendSrcAlpha: src_alpha
        blendDstAlpha: one_minus_src_alpha
    • 关闭深度测试和写入;
    • 启用标准透明混合,使条纹背景可以叠加在其他 UI 之上。

你可以为这个 Effect 做两个不同材质:

  • 一个用 opaque,作为底层背景;
  • 一个用 transparent,叠在 UI 上当装饰纹理。

六、可以继续扩展的方向

在这个基础上,你还可以考虑:

  • 替换 sinabs(sin),做对称的条纹;
  • 在 y 方向叠加另一组条纹,形成网格背景;
  • 使用噪声函数(简单 Perlin/Value Noise)混合颜色,做"云雾背景";
  • 根据时间(cc_time)偏移 UV,做动态流动条纹背景。

思路都是类似的:
顶点阶段负责把 UV 坐标送进来,片元阶段用数学函数把 UV 映射成颜色。


总结

这个 BackgroundCreate.effect 做了一件很实用又"性价比极高"的事:

  • 用极少的代码实现了程序化双色条纹背景
  • 保留了 Rich UI 所需的几乎所有参数:
    • 颜色、密度、旋转、边缘柔和度;
    • 透明 / 不透明模式;
  • 完全摆脱了纹理资源,不再需要为简单背景切图。

对于日常 UI 开发,这类小型 Shader 工具非常值得积累起来:

一方面减轻美术资源压力,另一方面也让你对 Cocos Creator 的 Effect / Shader 流程有更深的掌握。

相关推荐
BoBoZz197 小时前
ColorEdges 动态有向图的动态渲染
python·vtk·图形渲染·图形处理
BoBoZz198 小时前
AmbientSpheres 调整材质的环境光系数来控制3D物体的着色效果
python·vtk·图形渲染·图形处理
BoBoZz191 天前
ResetCameraOrientation 保存、修改和恢复摄像机的精确视角参数
python·vtk·图形渲染·图形处理
BoBoZz191 天前
MultipleRenderWindows 创建多个渲染窗口
python·vtk·图形渲染·图形处理
LYFlied1 天前
浏览器渲染图层详解
前端·性能优化·图形渲染·浏览器
ryfdizuo2 天前
HDR渲染管线中的像素格式-v1
图形渲染·渲染·游戏开发·hdr·色彩·高动态
AndrewHZ3 天前
【图像处理基石】什么是光栅化?
图像处理·人工智能·算法·计算机视觉·3d·图形渲染·光栅化
做cv的小昊4 天前
计算机图形学:【Games101】学习笔记05——着色(插值、高级纹理映射)与几何(基本表示方法)
笔记·opencv·学习·计算机视觉·图形渲染·几何学
BoBoZz194 天前
3D 医学扫描同时显示患者的皮肤、骨骼的 3D 模型(通过等值面提取),以及三个正交切片
python·vtk·图形渲染·图形处理