[小不溜] 无代码直接让Sprite支持第二套uv

不知道有没有人遇到过一下情况:

  1. 兴高采烈的给sprite写个shader,打包出来各种纹理错乱
  2. 想通过sprite做一些uv相关的计算,总能发现总有纹理合图作怪

以上都是因为纹理合图,无论是静态合图还是动态合图都会出现。这严重打击给cocos写shader的信心。
好啦,解决方法来啦:给sprite保留一套原始uv,也就是第二套uv.

PS: 我完全不知道引擎组怎么想的,为什么不提供二套uv的支持

以下就是核心代码,创建一个ts文件放入项目中即可,只支持小程序以及h5游戏:

import { gfx, RenderData, Sprite, spriteAssembler } from "cc";

declare module "cc" {
    interface Sprite {
        enableUV2: boolean;
    }
}

/**
 * 在原有sprite的基础上,增加了一个uv1的属性,用于存储第二套uv
 */
const vfmtPosUv2Color = [
    new gfx.Attribute(gfx.AttributeName.ATTR_POSITION, gfx.Format.RGB32F), // 3 floats
    new gfx.Attribute(gfx.AttributeName.ATTR_TEX_COORD, gfx.Format.RG32F), // 2 floats
    new gfx.Attribute(gfx.AttributeName.ATTR_COLOR, gfx.Format.RGBA32F),   // 4 floats
    new gfx.Attribute(gfx.AttributeName.ATTR_TEX_COORD1, gfx.Format.RG32F), // 2 floats
];

let simpleAssembler: any = spriteAssembler.getAssembler(new Sprite());
simpleAssembler.updateUVs = function(sprite: Sprite) {
    if (!sprite.spriteFrame) return;
    
    const renderData = sprite.renderData!;
    const stride = renderData.floatStride;
    const vData = renderData.chunk.vb;
    const uv = sprite.spriteFrame.uv;
    vData[3] = uv[0];
    vData[4] = uv[1];
    vData[stride+3] = uv[2];
    vData[stride+4] = uv[3];
    vData[stride*2+3] = uv[4];
    vData[stride*2+4] = uv[5];
    vData[stride*3+3] = uv[6];
    vData[stride*3+4] = uv[7];

    if(sprite.enableUV2) {
        vData[9] = 0;
        vData[10] = 1;
        vData[stride+9] = 1;
        vData[stride+10] = 1;
        vData[stride*2+9] = 0;
        vData[stride*2+10] = 0;
        vData[stride*3+9] = 1;
        vData[stride*3+10] = 0;
    }
};

let requestRenderData = Sprite.prototype.requestRenderData;
Sprite.prototype.requestRenderData = function() {
    if(this.enableUV2) {
        const data = RenderData.add(vfmtPosUv2Color);
        this._renderData = data;
        return data;
    }
    return requestRenderData.call(this);
};

EnableSpriteUV2.ts 文件

import { Component, Sprite, spriteAssembler } from "cc";
import { _decorator } from "cc";

const { ccclass, property, requireComponent, executeInEditMode } = _decorator;

@ccclass('EnableSpriteUV2')
@requireComponent(Sprite)
@executeInEditMode
export class EnableSpriteUV2 extends Component {
    private _sprite: Sprite;
    get sprite() {
        return this._sprite;
    }

    start() {
        this._sprite = this.getComponent(Sprite);
        this._sprite.enableUV2 = true;
        let simpleAssembler: any = spriteAssembler.getAssembler(this._sprite);
        if (this.sprite.renderData && this.sprite.renderData.data) {
            this.sprite.destroyRenderData();
            simpleAssembler.createData(this.sprite);
            simpleAssembler.updateUVs(this.sprite);
        }
    }
}

然后给需要二套uv的sprite添加EnableSpriteUV2组件即可,这样就能开开心心的在sprite的自定义shader中使用第二套uv啦!

我们直接把uv1渲染出来,效果如下:
image

这样,比如做个边界判断如:

if(uv1.x < 0.1) {
      return baseColor;
}

无论合图前后,表现都是一致的啦!!
image

3赞

通过这个方式,使用shader的spriteFrame也可以动态合图?
_(:з」∠)_这么强的吗?
我之前也很好奇为什么shader的使用方式必须要取消合图

既然这样,为什么要合图…… 省文件 IO?

准确的合图切图是用shader 自定义会替换掉内部切图的shader.

很多时候一张图片不只一个地方在用的

支持,有了这个对序列帧动画写shader就方便多了,能不能也支持 android啊

3.6以后需要改c++

1赞