【分享】远程图片加载,加类shader操作后动态合批

通常来说我们对远程图片的合批是使用动态合图,但是假如远程图片需要一些自定义的shader操作,那么即使动态合图之后也不能跟其它图片合批,因为使用的材质不同。

现在我给大家分享一个方案,含代码。
1.下载图片
2.获取图片的像素数据
3.进行像素数据修改
4.生成SpriteFrame
5.加入节点,加入动态合图

方案里面我写了一个CSpriteFrame 模块
模块里只有一个方法。首先我们使用3张远程图片做测试,图片如下:
企业微信截图_c7d94560-b270-429b-b128-7b01ee7e0761

模块的方法这么写:



上面3处使用了这个模块,分别作了切圆和去掉四分之一的操作,图片情况、DrawCall数量和动态合图结果显示:

我们再把代码注释掉,情况如下:

模块代码:

复制代码在这里:
//加载远程图片并可以自定义修改图片数据的类

import { SpriteFrame } from “cc”;

import { ImageAsset } from “cc”;

let CSpriteFrame =

{

create(url:string, frag:Function, callback:Function)

{

    let img = new Image();

    img.onload = function()

    {

        let canvas = document.createElement('canvas');

        let ctx = canvas.getContext('2d');

   

        // 将图片绘制到画布上

        canvas.width = img.width;

        canvas.height = img.height;

        ctx.drawImage(img, 0, 0);

       

        // 获取像素数据

        let pixels = ctx.getImageData(0, 0, img.width, img.height).data;

        if (frag) frag(pixels, img.width, img.height);

        // 创建一个新的ImageData对象

        let imageData = new ImageData(pixels, img.width, img.height);

        ctx.putImageData(imageData, 0, 0);

        let asset = new ImageAsset(canvas);

        let spriteFrame = SpriteFrame.createWithImage(asset);

       

        return callback(spriteFrame);

    }

    img.src = url;

}

}

export {CSpriteFrame};

如果复制有问题可能是上传转义,谢谢。

2赞

先声明个人没抬杠的想法, 还是感谢分享。 个人觉得这个反而大幅增加了cpu的压力, 如果要更好性能可以考虑worker里用offscreenCanvas, 处理后再回传buffer到主线程, 但也就仅限于h5环境。

你这个方法有没有研究过如果在原生可以怎麽搞?

嗯,你说的没错,确实这样做能提高性能。
目前我们没做原生环境开发,这方面倒没有去研究。

我觉得做到统一的话,还是要使用webgl来做,原纹理渲染到新纹理上再使用。
再研究研究吧。

感谢回复,我在h5方面的知识不太多,通过你才知道有离屏canvas和worker这些东西

offscreenCanvas 有一个缺点, 好像是手机safari支持较弱, 对于类似问题,我是偏向使用自定义顶点合批, 在cc3.x 版里改这个比2.x里改更方便, 主要是利用顶点合批方式把合图的真实uv范围传到shader, 在shader的顶点入参数直接取得, 然后按当前uv值映射到0~1范圈一样可以做到你想要的视觉效果,而不打断dc同时把这个工序交给gpu处理性能更好

【分享】自定义渲染合批之自定义顶点格式(附 Demo 和引擎源码解读)

1赞

你看看这个,这贴主使用了这个传uv的方法失败了。主要问题是uniform变量在整个绘制期间都是不变的,
你看看有没什么好的做法,我查阅了opengl许多资料都没法解决

你没理解那个自定义顶点格式, 意思是在cpu 把图像buffer 提交给gpu时就已经把这些参数带入, 在shader中就像你取a_position 一样可以直接取得, 不需要在material.setProperty 这种依赖uniform变量,你看看每个对像的a_position不一样为何却没有被打断dc, 因为内部传参所以material不会重新编译合批自然不会被打断,你那连接的人用的还是unifrom传uv参数, 这100% material会重新编译呀, 另外在自定顶点格式在处理上是在assembler. updateUV 时传进去的, updateUV时已经把uv值都计算好了

远程图片怎么合图的

你说的这个应该是对的,自定义一组顶点,即用一个attribute vec4 area,将三角形的三个自定义顶点数据都设成相同的值,那么在片元着色器中就能得到uv范围了。这个方法是可行的。

那个贴子我看到过你,那个贴子用【tkhoi01281】的方法可以头像合批,因为开启了动态合图,只是不能和其它图片合批而已,因为shader都不同,要想合在一起就要合并shader了

我是要合图不是合批

远程图片的动态合图

你远程图片最后也是用在spriteframe麻,spriteframe.package = true (只要图片不大于你设置的阀值,都会自动合), 如果超过也想合, 可以调用cc.dynamicAtlasManager.packToDynamicAtlas(sprite,frame)

合并过后怎么取出来用呢,我直接使用花 发觉是没有合批的

动态合图里面保存着spriteFrame,随时可以取用。合批也是要有前提的,cocos默认的是节点树连续且材质相同

dynamicAtlasManager.insertSpriteFrame 过后 如何使用spriteframe 能给一个具体方法的吗

dynamicAtlasManager.insertSpriteFrame(mySpriteFrame) 之后
其实你是可以直接使用mySpriteFrame的,因为我发现在生成spriteFame的时候,texture指向的是自己碎图,过了一帧之后再看变成了指向动态图集的大图,所以你是可以直接使用的,另外还可以从动态图集的属性里找得到,参考我上面的截图企业微信截图_5d7616ee-a322-4179-97d5-60b5a361d42d

resources.load(“skill_1/spriteFrame”,SpriteFrame,(error,frame)=>

    {

        console.log(error);

        let textrue = dynamicAtlasManager.insertSpriteFrame(frame);

        this.sp.spriteFrame = frame;

    });

    resources.load("skill_2/spriteFrame",SpriteFrame,(error,frame)=>

    {

        console.log(error);

        let textrue = dynamicAtlasManager.insertSpriteFrame(frame);

        this.sp2.spriteFrame = frame;

    });

    resources.load("skill_3/spriteFrame",SpriteFrame,(error,frame)=>

    {

        console.log(error);

        let textrue = dynamicAtlasManager.insertSpriteFrame(frame);

        this.sp3.spriteFrame = frame;

    });

这样用是不合批的

1.this.sp, this.sp2, this.sp3是不是连续的节点
2.this.sp, this.sp2, this.sp3有没有使用别的材质
3.加载完成后,查看一下动态图集,那三张图有没有真的加入动态图集

dynamicAtlasManager.insertSpriteFrame 插入不成功是什么原因的

企业微信截图_4d2eca53-fa89-4d1b-badb-3badf4a68b78

图片尺寸有没有超过动态合图的限制