使用纹理实现扫光效果

欢迎关注公众号
qrcode_for_gh_5f59886669d1_258

01

效果预览

02

实现原理

图片

uv 坐标:

纹理的百分比坐标

在 creator 中,uv 坐标的原点在左上角,u轴向右,v轴向下,范围是 0-1

使用纹理坐标获取纹理颜色叫做采样(Sampling)

根据时间控制流光纹理的 uv 坐标,获取其 rgba,然后与目标纹理 uv 坐标的 rgba 叠加,实现流动的效果

03

实现步骤

1 创建材质

在资源管理器面板合适的目录右键:

图片

新建 Effect:


//扫光-纹理

CCEffect %{
  techniques:
  - passes:
    - vert: vs
      frag: fs
      blendState:
        targets:
        - blend: true
      rasterizerState:
        cullMode: none
      properties:
        texture: { value: white }
        u_fluxayTexture: { value: white }
        u_time: { value: 0 }
}%

// Vertex Shader(顶点着色器) 
// 将顶点从模型空间坐标系统转化到屏幕空间坐标系统
// 顶点着色器分为输入和输出两部分
// 负责的功能是把输入的数据进行矩阵变换位置,计算光照公式生成逐顶点颜⾊,⽣成/变换纹理坐标
// 并且把位置和纹理坐标这样的参数发送到片段着色器
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
    // 输入的纹理坐标
    // UV坐标:原点在左上角,u轴是向右,v轴是向下,范围是0-1
    in vec2 a_uv0;
    // 输出的纹理坐标
    out vec2 v_uv0;
  #endif

  void main () {
    mat4 mvp;
    
    #if CC_USE_MODEL
      mvp = cc_matViewProj * cc_matWorld;
    #else
      mvp = cc_matViewProj;
    #endif

    v_uv0 = a_uv0;

    v_color = a_color;

    gl_Position = mvp * vec4(a_position, 1);
  }
}%

// Fragment Shader(片段着色器)
// 片元着色器的作用是处理由光栅化阶段生成的每个片元,最终计算出每个像素的最终颜色(RGBA)
CCProgram fs %{
  precision highp float;

  #include <alpha-test>
  #include <texture>
  
  in vec4 v_color;

  in vec2 v_uv0;

  uniform sampler2D texture;

  uniform sampler2D u_fluxayTexture;  //流光纹理

  // 自定义属性
  // 所有非 sampler 的 uniform 都必须以 UBO 形式声明
  // UBO 成员声明类型和顺序有严格的校验机制,以排除 GL 标准下隐式布局对齐带来的内存消耗
  uniform ARGS {
    float u_time;
  };

  void main () {
    vec4 o = vec4(1, 1, 1, 1);

    #if USE_TEXTURE
      // texture.inc 核心函数
      // o = texture2D(texture, v_uv0);
      // texture: 纹理,v_uv0: 纹理坐标,通过 GLSL 的内建函数 texture2D 来获取纹理上对应UV坐标的颜色(RGBA)
      o = texture2D(texture, v_uv0);
    #endif

    // 纹理颜色和顶点颜色(节点颜色)叠加得到最终颜色
    o *= v_color;

    // alpha-test.inc 核心函数
    // if (color.a < alphaThreshold) discard;
    // discard:退出片段着色器,不执行后面的片段着色操作,片段也不会写入帧缓冲区
    ALPHA_TEST(o);

    // 在底图不透明的地方叠加流光纹理的颜色
    if(o.a >= 1.0) {    
      // 根据时间控制流光纹理的UV
      vec2 fluxayUV = vec2(v_uv0.x, v_uv0.y);
      fluxayUV.x -= u_time - 1.0;
      // 获取流光纹理上UV的颜色
      vec4 fluxay = texture2D(u_fluxayTexture, fluxayUV);
      // 叠加颜色
      gl_FragColor = o + fluxay;
    } else {
      gl_FragColor = o;
    }
  }
}%

新建 Material,在其 属性检查器 的 Effect 下拉框中选择刚刚创建 Effect 选项,然后在纹理的位置拖入流光纹理,点击应用
图片

然后将该材质拖入到 sprite 组件的材质位置

图片

2 编写脚本组件

接下来需要编写脚本组件,将时间传递给 effect


const { ccclass, property } = cc._decorator;

@ccclass
export default class FluxayTexture extends cc.Component {

    private _speed: number = 1;
    private _time: number = 0;
    private _material: cc.MaterialVariant;

    onLoad() {
        // 获取材质
        this._material = this.node.getComponent(cc.Sprite).getMaterial(0);
    }

    update(dt) {
        if (this._time > 2) {
            this._time = 0;
        }

        this._material.setProperty("u_time", this._time);
        this._time += dt * this._speed;
    }
}

获取源码请在公众号回复:

扫光

13赞

比公众号推送慢了5个小时,鸦哥开始搞差异化了,此处应该艾特大表姐

外边跑了一天····

:joy:

:sob:

大佬牛逼!!!!

光的贴图会被拉伸变形啊,能保持扫光贴图形状不变嘛?

根据uv取的,想不被拉伸的话,保持同比?

可以,正常情况下使用贴图了肯定不想拉伸变形。

大佬,怎么从下到上扫???

Mark!!!

可以不用脚本设时间,直接在effect中用cc_time更好

图片边缘会有黑边

鸭神:我个人不敢苟同这个说法,cc_time是游戏启动后的时间,这个时间,不可控。看具体需求定。
2.4以前的版本有bug,片元着色中不能用,cc_time。

哥,不带你这么玩的啊
你自己回复就好了啊 :rofl:

加入三角函数算一下就可控了,这样可以节省一个脚本来设置的麻烦