动态合图与shader

实际上,那个时候,我已经看到动态合图里面有这些图片了,所以应该是正确的uv了

因为之前没有做过这类的处理,所以不知道你说的这些具体用到那些api,所以,请赐教[抱拳]

合图成功了 就用assember负责切图的

assember这个api没有找到

搜索这个 Assembler

好的,我晚点研究一下,谢谢

用顶点裁切。

自己做个组件,自己实现Assemble。参考mask组件的圆形裁切。组装顶点数据。

我查了些资料,感觉你这个传uv的方式做不了了。
因为cocos 里面给shader传参是uniform,这意味着你10个头像一次提交绘制时,这10个头像访问到的值都是相同的,也就是你出现的情况,只有最后一个正确绘制。你可以让这10个头像断批试试,应该10个就能正确绘制,但也就断批了。

第二个方法也许需要改引擎,原理其实也简单,你调出动态合图的情况来看,可以看到10个头像在一张大图里,假如这10个在大图里的头像都是早就切好的圆形呢,那就能按照普通情况合批了对吧。
这个就是要自己在下载上传到gpu之后切一下。

只有一个显示并裁剪 可以看看几何坐标是否都正确,别搞得都算的是同一个几何坐标了 都重叠在一起了

动态图集合图的时候,把微信头像搞成圆的 :upside_down_face:

哈哈. 我倒觉得这样最好.
源头数据预先处理,就没后面什么事情了.
就如同cdn的资源链接后缀加上类似_round标签一样(cdn云端处理).

不过要考虑图片的处理数量是否会导致游戏卡顿.

可以详细说说怎么弄吗

提供个思路,没有尝试过:区域截图( 官方区域截图方案)的方式,渲染圆形图片,把截图加到动态图集中

【CocosCreator 3.x 技术方案分享】第一期 - Creator 3.x - Cocos中文社区 这里面有官方区域截图方案

又问下了 ChatGpt,也没验证,不过我觉得这种方式不如截图

user:
现在有一个 Uint8Array 的数据, 存储的是图片的像素数据,假设图片是正方形的, 现在想要将图片变成圆形,也就是圆形半径外的像素透明,直接修改 Uint8Array,代码怎么实现?


assistant:
要将一个正方形的图像转换为圆形,需要对每个像素进行计算,判断该像素是否在圆形内部。如果在圆形内部,则保留该像素的值,否则将该像素的值设置为透明。下面是一个可能的实现:

function squareToCircle(pixels, width, height, radius) {
  const centerX = width / 2;
  const centerY = height / 2;
  const radiusSquared = radius * radius;
  const bytesPerPixel = 4; // assuming RGBA format

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const offset = (y * width + x) * bytesPerPixel;
      const dx = x - centerX;
      const dy = y - centerY;
      const distanceSquared = dx * dx + dy * dy;

      // Check if the pixel is outside the circle
      if (distanceSquared > radiusSquared) {
        // Set the pixel to transparent
        pixels[offset + 3] = 0;
      }
    }
  }
}

这个函数接受一个 Uint8Array,宽度和高度,以及圆的半径。它假设每个像素使用 RGBA 格式,即每个像素由四个字节组成,依次是红、绿、蓝和透明度。该函数遍历所有像素,计算该像素到图像中心的距离,如果该距离大于圆半径,就将该像素的透明度设置为 0,即将其变为透明。


user:
假设,现在对象结构是这样的 SpriteFrame = {“texture”: { “image”: {“data”: []}}} 其中 data 是 Uint8Array, 重新编写代码


assistant:
好的,那么我们可以将之前的代码稍作修改,让其适用于新的对象结构:

function squareToCircle(spriteFrame, radius) {
  const image = spriteFrame.texture.image;
  const data = image.data;
  const width = image.width;
  const height = image.height;
  const centerX = width / 2;
  const centerY = height / 2;
  const radiusSquared = radius * radius;
  const bytesPerPixel = 4; // assuming RGBA format

  for (let y = 0; y < height; y++) {
    for (let x = 0; x < width; x++) {
      const offset = (y * width + x) * bytesPerPixel;
      const dx = x - centerX;
      const dy = y - centerY;
      const distanceSquared = dx * dx + dy * dy;

      // Check if the pixel is outside the circle
      if (distanceSquared > radiusSquared) {
        // Set the pixel to transparent
        data[offset + 3] = 0;
      }
    }
  }
}

这个函数接受一个 SpriteFrame 对象和圆的半径。它首先获取到图像数据 data、宽度和高度,并计算出中心点和半径的平方。接下来,它遍历所有像素,计算该像素到图像中心的距离,如果该距离大于圆半径,就将该像素的透明度设置为 0,即将其变为透明。注意,这里直接修改了 data 数组,原始的 Uint8Array 对象也会被修改。

哈哈哈,可以可以,我抽空验证一下

假定你组装好了顶点的数据和三角形的数据结构如下

function getDefaultVerticesData(): IVerticesData {

    return {

        x: [],

        y: [],

        nu: [],

        nv: [],

        triangles: [],

    }

}

然后你自己的assemble去提交渲染数据:

initData() {
        this._renderData.createFlexData(0, 4, 6, this.getVfmt());
}

updateRenderData(maskSprite: MaskSprite) {
        if (!maskSprite._vertsDirty) return;
        let frame = maskSprite._spriteFrame;
        let renderData = this._renderData;
        if (!renderData || !frame) return;
        let vertices = maskSprite.vertices;
        if (!vertices) {
            vertices = maskSprite.caculateVertices();
        }
        if (!vertices) return;
        if (this.verticesCount != vertices.x.length) {
            this.verticesCount = vertices.x.length;
            this.indicesCount = vertices.triangles.length;
            this._renderData.createFlexData(0, this.verticesCount, this.indicesCount, this.getVfmt());
        }

        this.updateIndices(vertices.triangles);

        this.updateColor(maskSprite);
        this.updateUVs(maskSprite);
        this.updateVerts(maskSprite);
        this.updateWorldVerts(maskSprite);

        maskSprite._vertsDirty = false;

    }

updateIndices(triangles: number[]) {
        this._renderData.iDatas[0].set(triangles);
    }

    updateUVs(maskSprite: MaskSprite) {
        let vertices = maskSprite.vertices,
            u = vertices.nu,
            v = vertices.nv;

        let verts = this._renderData.vDatas[0];

        for (let i = 0; i < u.length; i++) {
            let dstOffset = i * RenderDataIndex.Count;
            verts[dstOffset + RenderDataIndex.u] = u[i];
            verts[dstOffset + RenderDataIndex.v] = v[i];
        }
    }

    updateVerts(maskSprite: MaskSprite) {
        let node = maskSprite.node,
            contentWidth = Math.abs(node.width),
            contentHeight = Math.abs(node.height),
            appx = node.anchorX * contentWidth,
            appy = node.anchorY * contentHeight;

        let vertices = maskSprite.vertices,
            x = vertices.x,
            y = vertices.y;

        let scaleX = node.scaleX,
            scaleY = node.scaleY;

        let local = this._local = [];
        for (let i = 0, l = x.length; i < l; i++) {
            local[i * 2] = (x[i] + node.width * node.anchorX - appx) * scaleX;
            local[i * 2 + 1] = (- y[i] + node.height * (1 - node.anchorY) - appy) * scaleY;
        }
        this.updateWorldVerts(maskSprite);
    }

    updateWorldVerts(maskSprite: MaskSprite) {
        let node = maskSprite.node;
        let matrix = node._worldMatrix;
        let matrixm = matrix.m;
        let a = matrixm[0], b = matrixm[1], c = matrixm[4], d = matrixm[5],
            tx = matrixm[12], ty = matrixm[13];
        let local = this._local;
        let world = this._renderData.vDatas[0];
        let floatsPerVert = this.floatsPerVert;
        for (let i = 0, l = this.verticesCount; i < l; i++) {
            let lx = local[i * 2];
            let ly = local[i * 2 + 1];
            world[floatsPerVert * i] = lx * a + ly * c + tx;
            world[floatsPerVert * i + 1] = lx * b + ly * d + ty;
        }
    }

我做了个东西,可以满足你的需求

我也碰到这个问题了,大佬最后怎么解决的?