首页 > 其他分享 >CocosCreator3.x 应用在UI(Sprite) 上的 shader(.effect) 的合批,通过自定义顶点参数(二)、具体操作步骤

CocosCreator3.x 应用在UI(Sprite) 上的 shader(.effect) 的合批,通过自定义顶点参数(二)、具体操作步骤

时间:2023-10-26 14:13:47浏览次数:55  
标签:合批 return sprite 自定义 rotateCenter float vec4 vec2 操作步骤

具体操作步骤

接下来以一个制造旋转效果的 shader 为例子,提供了这些参数的设置:

  1. 旋转速度 float
  2. 旋转中心位置 vec2
  3. 逆时针/顺时针 bool
  4. 扭曲度 float

并在使用的贴图一致的前提下并且参数不同的值都能够合批。

最终项目可以从 GITHUB 获取。
CCC版本:3.8.0

深入了解可以阅读后续的 参考资料 及 源码阅读。

第一步、shader(.effect)

1. 将 builtin-sprite.effect 复制一份出来,重命名为 rotate-sprite.effect.

  • builtin-sprite.effect 是 Sprite 组件默认使用的 shader(.effect)。

在 Assets面板 中搜索 builtin-sprite 即可找到 builtin-sprite.effect。

复制一份到项目的 assets 中。

重命名为 rotate-sprite.effect。

2. 打开 rotate-sprite.effect,在 顶点着色器 sprite-vs 上定义顶点参数,并传递给 片元着色器 sprite-fs。

在编辑前 sprite-vs 如下,其中如 in vec3 a_position 这类以 a_ 就是使用的顶点参数。

CCProgram sprite-vs %{
  ...
  in vec3 a_position;
  in vec2 a_texCoord;
  in vec4 a_color;

  out vec4 color;
  out vec2 uv0;

  vec4 vert () {
    ...
    uv0 = a_texCoord;
    #if SAMPLE_FROM_RT
      CC_HANDLE_RT_SAMPLE_FLIP(uv0);
    #endif
    color = a_color;

    return pos;
  }
}%

将我们要用到的顶点参数添加后。

CCProgram sprite-vs %{
  ...
  in vec3 a_position;
  in vec2 a_texCoord;
  in vec4 a_color;
  // 旋转速度
  in float a_rotateSpeed;
  // 旋转中心
  in vec2 a_rotateCenter;
  // 是否顺时针旋转
  in float a_clockwise;
  // 扭曲度
  in float a_distort;
  ...
}%

因为旋转效果要在 片元着色器 sprite-fs 中实现,因此我们把这些 顶点参数的值 传递给 片元着色器 sprite-fs。

在 顶点着色器 sprite-vs 中定义对应的 out 输出变量。

CCProgram sprite-vs %{
  ...
  out vec4 color;
  out vec2 uv0;
  // 旋转速度
  out float rotateSpeed;
  // 旋转中心
  out vec2 rotateCenter;
  // 是否顺时针旋转
  out float clockwise;
  // 扭曲度
  out float distort;
  ...
}%

在 顶点着色器 sprite-vs 的函数中完成对 out 输出变量 的赋值。

CCProgram sprite-vs %{
  ...
  vec4 vert () {
    ...
    uv0 = a_texCoord;
    #if SAMPLE_FROM_RT
      CC_HANDLE_RT_SAMPLE_FLIP(uv0);
    #endif
    color = a_color;
    
    rotateSpeed = a_rotateSpeed;
    rotateCenter = a_rotateCenter;
    clockwise = a_clockwise;
    distort = a_distort;

    return pos;
  }
}%

最终 shader(.effect) 的 顶点着色器 sprite-vs 会是这样。

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;
  // 旋转速度
  in float a_rotateSpeed;
  // 旋转中心
  in vec2 a_rotateCenter;
  // 是否顺时针旋转
  in float a_clockwise;
  // 扭曲度
  in float a_distort;

  out vec4 color;
  out vec2 uv0;
  // 旋转速度
  out float rotateSpeed;
  // 旋转中心
  out vec2 rotateCenter;
  // 是否顺时针旋转
  out float clockwise;
  // 扭曲度
  out float distort;

  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

    uv0 = a_texCoord;
    #if SAMPLE_FROM_RT
      CC_HANDLE_RT_SAMPLE_FLIP(uv0);
    #endif
    color = a_color;
    
    rotateSpeed = a_rotateSpeed;
    rotateCenter = a_rotateCenter;
    clockwise = a_clockwise;
    distort = a_distort;

    return pos;
  }
}%

3. 在 片元着色器 sprite-fs 上接收从 顶点着色器 sprite-vs 传递过来的顶点参数。

编辑前,片元着色器 sprite-fs 如下。其中 in vec4 color 这样的 in 输入变量,从 顶点着色器 sprite-vs 中接收了对应变量。

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

  in vec4 color;

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

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

    #if USE_TEXTURE
      o *= CCSampleWithAlphaSeparated(cc_spriteTexture, 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 *= color;
    ALPHA_TEST(o);
    return o;
  }
}%

增加对应我们新增顶点参数的 in 输入变量。

CCProgram sprite-fs %{
  ...
  in vec4 color;
  // 旋转速度
  in float rotateSpeed;
  // 旋转中心
  in vec2 rotateCenter;
  // 是否顺时针旋转
  in float clockwise;
  // 扭曲度
  in float distort;

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

4. 使用参数并实现效果。

如何实现不是本文关注点,这里直接给出完成后的 片元着色器 sprite-fs 的代码。

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

  in vec4 color;
  // 旋转速度
  in float rotateSpeed;
  // 旋转中心
  in vec2 rotateCenter;
  // 是否顺时针旋转
  in float clockwise;
  // 扭曲度
  in float distort;

  #define PI 3.1415926535897932384626433832795

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

  float yOflineOnX(float k, float b, float x) {
    return k * x + b;
  }

  float xOflineOnY(float k, float b, float y) {
    return (y - b) / k;
  }

  bool isBetween(float value, float min, float max) {
    return value >= min && value <= max;
  }

  vec2 findFarthestFittingPoint(vec2 dir, vec2 rotateCenter) {
    vec2 farFitPoint = vec2(0.0);
    float len4fit = 0.0;
    float xSign = sign(dir.x);
    float slope = dir.y / (xSign * max(abs(dir.x), 0.00000001));
    slope = clamp(slope, -9999999999.9, 9999999999.9);
    float yIntercept = rotateCenter.y - slope * rotateCenter.x;
    yIntercept = clamp(yIntercept, -9999999999.9, 9999999999.9);
    
    vec2 checkVal = vec2(0.0, yOflineOnX(slope, yIntercept, 0.0));
    vec2 check2center = checkVal - rotateCenter;
    if (isBetween(checkVal.y, 0.0, 1.0) && dot(dir, check2center) > 0.0) {
      farFitPoint = checkVal;
      len4fit = length(check2center);
    }
    checkVal = vec2(1.0, yOflineOnX(slope, yIntercept, 1.0));
    check2center = checkVal - rotateCenter;
    float len4check = length(check2center);
    if (isBetween(checkVal.y, 0.0, 1.0) && dot(dir, check2center) > 0.0 && len4check > len4fit) {
      farFitPoint = checkVal;
      len4fit = len4check;
    }
    checkVal = vec2(xOflineOnY(slope, yIntercept, 0.0), 0.0);
    check2center = checkVal - rotateCenter;
    len4check = length(check2center);
    if (isBetween(checkVal.x, 0.0, 1.0) && dot(dir, check2center) > 0.0 && len4check > len4fit) {
      farFitPoint = checkVal;
      len4fit = len4check;
    }
    checkVal = vec2(xOflineOnY(slope, yIntercept, 1.0), 1.0);
    check2center = checkVal - rotateCenter;
    len4check = length(check2center);
    if (isBetween(checkVal.x, 0.0, 1.0) && dot(dir, check2center) > 0.0 && len4check > len4fit) {
      farFitPoint = checkVal;
      len4fit = len4check;
    }
    return farFitPoint;
  }

  vec2 rotateVector(vec2 vec, float angle) {
    return vec2(
      vec.x * cos(angle) - vec.y * sin(angle),
      vec.x * sin(angle) + vec.y * cos(angle)
    );
  }

  float easeOutBounce(float x){
    float n1 = 7.5625 * distort;
    float d1 = 2.75;

    if (x < 1.0 / d1) {
        return n1 * x * x;
    } else if (x < 2.0 / d1) {
        return n1 * (x -= 1.5 / d1) * x + 0.75;
    } else if (x < 2.5 / d1) {
        return n1 * (x -= 2.25 / d1) * x + 0.9375;
    } else {
        return n1 * (x -= 2.625 / d1) * x + 0.984375;
    }
  }

  float easeInCirc(float x) {
    return 1.0 - sqrt(1.0 - pow(x, 2.0 * distort));
  }

  vec4 frag () {
    vec4 o = vec4(1.0);

    #if USE_TEXTURE
      float rotateRad = sign(clockwise) * cc_time.x * PI * rotateSpeed;

      // 通过 uv转换 来实现旋转
      vec2 dir = uv0 - rotateCenter;
      vec2 farFitPoint = findFarthestFittingPoint(dir, rotateCenter);
      float percent = length(dir) / length(farFitPoint - rotateCenter);
      vec2 dirRotated = rotateVector(dir, rotateRad);
      farFitPoint = findFarthestFittingPoint(dirRotated, rotateCenter);
      vec2 uvRotated = rotateCenter + (farFitPoint - rotateCenter) * easeInCirc(percent);

      o *= CCSampleWithAlphaSeparated(cc_spriteTexture, uvRotated);
      #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 *= color;
    ALPHA_TEST(o);
    return o;
  }
}%

5. 创建 rotate-sprite.mat,并使用 rotate-sprite.effect。

创建新的 material。

重命名为 rotate-sprite.mat。

修改材质所使用的 shader(.effect),选中 rotate-sprite.effect。

并激活 "USE TEXTURE",然后保存设置即可。

第二步、编写 RotateSprite.ts (一)

1. 新建一个 ts 脚本,命名为 RotateSprite.ts。
image

2. 删掉 start 和 update,并其继承 Sprite
image

3. 编写顶点参数相关逻辑

回顾 shader(.effect) 定义的顶点参数。
image

对应列出如下表,其中 gfx.Format 的值可以 查表 得。

字段 glsl类型 gfx.Format
in vec3 a_position vec3 RGB32F
in vec2 a_texCoord vec2 RG32F
in vec4 a_color vec4 RGBA32F
in float a_rotateSpeed float R32F
in vec2 a_rotateCenter vec2 RG32F
in float a_clockwise float R32F
in float a_distort float R32F

标签:合批,return,sprite,自定义,rotateCenter,float,vec4,vec2,操作步骤
From: https://www.cnblogs.com/bakabird/p/17789258.html

相关文章

  • CocosCreator3.x 应用在UI(Sprite) 上的 shader(.effect) 的合批,通过自定义顶点参数(四
    源码阅读部分顶点数量、布局相关设置针对UI所使用的Mesh的顶点设置:如simple模式使用1个矩形(2x2个顶点),sliced模式使用9个矩形(4x4个顶点)dataLength相当于顶点数量。vertexRow和vertexCol描述了网格形状。SetIndexBuffer则描述网格中所有“三角形”分别由哪3......
  • CocosCreator3.x 应用在UI(Sprite) 上的 shader(.effect) 的合批,通过自定义顶点参数(三
    参考资料资料1来源:https://forum.cocos.org/t/topic/148747/28用户:homym(tkhoi01281)3.x版自定参数我是利用createMesh方法去生成ui,因为createMesh就有自定义顶点参数的方法这个改动其实是可以弄一个新sprite来继承老spirte,然后把引擎里的simple.ts,splice.ts等assemb......
  • Spark编程案例:创建自定义分区
    需求:根据jsp文件的名字,将各自的访问日志放入到不同的分区文件中,如下:生成的分区文件例如:part-00000文件中的内容:只包含了java.jsp的访问日志日志内容:192.168.88.1--[30/Jul/2017:12:53:43+0800]"GET/MyDemoWeb/HTTP/1.1"200259192.168.88.1--[30/Jul/2017:12:53:4......
  • 甘特图组件DHTMLX Gantt用例 - 如何自定义任务、月标记和网格新外观
    dhtmlxGantt是用于跨浏览器和跨平台应用程序的功能齐全的Gantt图表。可满足项目管理应用程序的所有需求,是最完善的甘特图图表库。本文将为大家揭示DHTMLXGantt自定义的典型用例,包括自定义任务、网格的新外观等,来展示其功能的强大性!用例-新建项目外观、当前月份标记和可折叠网格D......
  • 【示波器的原理,使用方法和基本操作步骤】
    简介:示波器入门非常简单,使用AutoScale(自动定标)功能,能轻易的捕捉波形。入门级的AutoScale所采用的“边沿触发”,通过查找波形上的指定沿(上升沿或下降沿等)和电压电平来识别触发。TriggerLevel(触发电平)示波器作用:用来观察和分析电信号的各种特性(包括频率、幅度、相位、波形......
  • 钉钉OA自定义审批流的创建和使用
    前言大家好!我是sum墨,一个一线的底层码农,平时喜欢研究和思考一些技术相关的问题并整理成文,限于本人水平,如果文章和代码有表述不当之处,还请不吝赐教。钉钉作为一款办公软件,审批功能是它的核心功能之一,最常见的审批场景就是请假和报销了。虽然钉钉也内置了一些审批流,但是审批场景层......
  • MapReduce自定义GroupingComparator
    需求:有如下订单明细数据0000001 01 222.80000002 06 722.40000001 05 25.80000003 01 222.80000003 01 33.80000002 03 522.80000002 04 122.4第一列是订单编号,第二列是商品id,第三列是商品金额,列与列之间用制表符分隔。现在需要求出每一个订单中最贵的商品。思路:将订单id和商......
  • 基于RuoYi-Flowable-Plus的若依ruoyi-nbcio支持自定义业务表单流程的集成方法与步骤(二
    更多ruoyi-nbcio功能请看演示系统gitee源代码地址演示地址:RuoYi-Nbcio后台管理系统前面讲了集成的后端部分内容,下面简单介绍一下前端的内容 1、前端生成的页面需要进行修改,增加流程状态启动等相关信息,如demo的index修改如下<template><divclass="app-container"><el-form......
  • 基于RuoYi-Flowable-Plus的若依ruoyi-nbcio支持自定义业务表单流程的集成方法与步骤(一
    更多ruoyi-nbcio功能请看演示系统gitee源代码地址演示地址:RuoYi-Nbcio后台管理系统由于大家最自定义业务表单的整个集成方法还不熟悉,下面大概介绍一下这个流程与方法。1、首先需要建立数据库表,根据自己业务进行数据表的建立,目前系统需要在另外sql进行数据库表的建立,以后可以考虑系......
  • 基于RuoYi-Flowable-Plus的若依ruoyi-nbcio支持自定义业务表单流程(五)
    更多ruoyi-nbcio功能请看演示系统gitee源代码地址演示地址:RuoYi-Nbcio后台管理系统今天讲一下wf_demo表单的一些修改1、demo的实现类修改如下:主要是增加一个服务名称,后面要用到,同时继承于WfCallBackServiceI,以便进行调用。@Service("wfDemoService")publicclassWfDemoServiceImp......