游戏场景碎片过渡效果

早些时候在社区见到一个元宇宙的项目,做了一个前后场景方块碎片过渡的效果,十分有科技感,异名就有了复刻的想法;加上两个场景切换的过渡效果比较实用,社区上已经有了开源的方案,异名复刻的成本很低,但是收获不少614aa5f27cc512b254f6cac967d678e0.gif

实现思路

因为最终效果需要应用到两个场景切换中,所以借助了常驻节点来实现全局共享的能力。常驻节点是一种特殊类型的节点,它们在场景切换时不会被销毁,而是保持在内存中。常驻节点在整个游戏生命周期中都存在,可以用于实现一些需要在多个场景之间共享的功能或数据,它的开启很简单:cc.game.addPersistRootNode(this.node)

在常驻节点中,我们创建一个内部Camera和Sprite来渲染场景的过渡画面

// 同样的方式创建一个内部的渲染Sprite,
let cameraNode = cc.find('TRANSITION_CAMERA', this.node);
if (!cameraNode) {
  cameraNode = new cc.Node('TRANSITION_CAMERA');
  this._camera = cameraNode.addComponent(cc.Camera);
  cameraNode.parent = this.node;
} else {
  this._camera = cameraNode.getComponent(cc.Camera);
}

这样我们就拥有了三个Camera,当场景A切换到场景B的时候,我们把 TRANSITION_CAMERA 激活,用这个相机来渲染过渡的画面,具体细节看核心的过渡逻辑

_loadScene(sceneUrl, fromCameraPath, toCameraPath, onSceneLoaded, onTransitionFinished) {
    this._spriteNode.active = true;
    this._cameraNode.active = true;
    let fromCameraNode = cc.find(fromCameraPath);
    let fromCamera = fromCameraNode && (fromCameraNode.getComponent(cc.Camera) as any);

    let originTargetTexture1 = fromCamera.targetTexture;
    fromCamera.cullingMask &= ~this._camera.cullingMask;
    fromCamera.targetTexture = this._texture1;
    fromCamera.render(cc.director.getScene());
    fromCamera.targetTexture = originTargetTexture1;

    return cc.director.loadScene(sceneUrl, (arg) => {
      onSceneLoaded && onSceneLoaded(...arg);

      let toCameraNode = cc.find(toCameraPath);
      let toCamera = toCameraNode && (toCameraNode.getComponent(cc.Camera) as any);

      toCamera.cullingMask &= ~this._camera.cullingMask;
      let originTargetTexture2 = toCamera.targetTexture;
      toCamera.targetTexture = this._texture2;
      toCamera.render(cc.director.getScene());

      this._camera.depth = toCamera.depth;
      this._camera.clearFlags = toCamera.clearFlags;

      this._onLoadFinished = () => {
        toCamera.targetTexture = originTargetTexture2;

        this._spriteNode.active = false;
        this._cameraNode.active = false;
      };
    });
  }

这里面有一个"Render-To-Texture(RTT)"的操作,它将图形渲染的结果直接绘制到一个纹理(Texture)上,而不是直接渲染到屏幕上,核心代码是这三行

// 设置渲染目标:将渲染目标设置为一个纹理,而不是默认的屏幕缓冲区。这样,所有的渲染操作都会直接渲染到这个纹理上
fromCamera.targetTexture = this._texture1;
// 进行渲染:执行需要渲染的图形绘制操作,渲染结果会直接绘制到纹理上
fromCamera.render(cc.director.getScene());
// 恢复渲染目标:渲染完成后,将渲染目标恢复为默认的屏幕缓冲区,以便后续的渲染操作正常进行
fromCamera.targetTexture = originTargetTexture1;

这里面还有一个我们不常用的位运算 fromCamera.cullingMask &= ~this._camera.cullingMask;,其目的是通过对摄像机的 cullingMask 属性进行位操作,来控制哪些图层的对象会被摄像机忽略。cullingMask 是在图形编程中用于决定摄像机渲染哪些图层的一个掩码(mask),它通常用于指定摄像机在渲染时应该考虑哪些图层中的对象,哪些图层中的对象应该被忽略

其中 ~this._camera.cullingMask 使用了位求反操作符 ~,它会将 this._camera.cullingMask 的每个位都取反,即将 0 变为 1,将 1 变为 0通过按位与运算符;然后通过 &= 将 fromCamera.cullingMask 和 ~this._camera.cullingMask 的结果进行按位与操作,以得到新的掩码。这个新的掩码会在不影响其他位的情况下,将与参考摄像机的 cullingMask 相关的位设置为 0,从而排除这些层级的对象不被当前摄像机渲染

获得了前后场景的画面,接下来就可以基于此实现两张Texture的切换效果;在这个demo中,我们要实现一个渐进的、基于小方块的过渡动效,方块从起始颜色逐渐变化到目标颜色,我们定义三个参数:progress(time) 控制过渡的进度,smoothness 控制过渡边缘的平滑度,squares 控制方块的数量,核心函数如下

vec4 transition(vec2 p) {
      const vec2 center = vec2(0.5, 0.5);
      // 根据进度计算当前方向
      vec2 v = normalize(direction);
      v /= abs(v.x) + abs(v.y);
      float d = v.x * center.x + v.y * center.y;
      float offset = smoothness;

      // 计算当前进度
      float pr = smoothstep(-offset, 0.0, v.x * p.x + v.y * p.y - (d - 0.5 + progress * (1.0 + offset)));

      // 按照 squares 划分成小方块的坐标
      vec2 squarep = fract(p * squares);
      vec2 squaremin = vec2(pr / 2.0);
      vec2 squaremax = vec2(1.0 - pr / 2.0);

      // 通过 step 函数计算当前像素位置是否在当前方块内,并返回一个混合系数 a
      float a = (1.0 - step(progress, 0.0)) * step(squaremin.x, squarep.x) * step(squaremin.y, squarep.y) * step(squarep.x, squaremax.x) * step(squarep.y, squaremax.y);
      return mix(getFromColor(p), getToColor(p), a);
  }

切分小方块代码是 vec2 squarep = fract(p * squares), 这行代码的作用是将输入坐标 p 乘以 squares 后,取其小数部分,这种操作通常用于在纹理坐标或屏幕空间坐标上创建重复的图案或效果

举例如果 p 是 (0.3, 0.7) 且 squares 是 4,则 p * squares 是 (1.2, 2.8),而 fract(1.2, 2.8) 是 (0.2, 0.8),这样,原始坐标 (0.3, 0.7) 被映射到一个在 [0, 1) 范围内的新坐标。通过这种方式,就能实现将图像或屏幕空间分割成多个小方块的效果,然后通过step函数算出每个像素点是否处于方块内,最后进行色彩的插值,从而实现画面的切换,最终效果如下

e1398958dde249cd9765ab16874f5ff6.gif
demo

效果预览

源码获取请点击查看原文,长按二维码查看效果👇

f9ef53e095915f6e2b681bf68c10bc11.png

融球效果(shader)    颜色滤镜    水波扩散效果(shader)

镜面光泽效果    圆形头像(shader)    追光效果

shader 溶解效果    富文本打字机效果    放大镜效果

子弹跟踪效果    移动残影效果    刮刮卡实现

微信小游戏首包超出4M之后      设计稿生成游戏界面

GameJam线下48小时极限游戏开发体验  

马赛克效果(shader)

f62840cba5d49791c62174e3a7fee098.png

  • 8
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值