如何在ThreeJS中加载Texturepacker精灵表?

5
我正在尝试在ThreeJS中从TexturePacker加载精灵表,这包括一张图像和一个JSON文件。该图片里面有许多小的精灵图案打包在一起,而JSON文件则定义了在图片中小精灵的位置和大小。
我已经尝试了三种加载方法:
1.使用ThreeJS加载程序来加载JSON和图片,并为其分配新纹理,具有不同的重复和偏移值。
2.使用WebGLRenderTarget缓冲区将源图像裁剪成目标图像。
3.使用Canvas缓冲区将源图像裁剪成目标图像。
使用不同偏移量的多个纹理实例的方法应该可以正常工作,因为我没有复制源图像,但是当我通过切换材质的纹理运行动画时,它会使用大量的内存,就好像每个动画都将整个源精灵表复制到内存中。如果我改变动画的纹理偏移而不是使用纹理副本,那么它就能正常工作,但偏移更改将应用于使用相同源精灵表的每个对象。
WebGLRenderTarget方法需要相机和场景来裁剪纹理以及添加到场景中的精灵。然而,由于它不能生成原始纹理的1:1裁剪版本,而且加载速度非常慢,所以其输出结果无法使用。在ThreeJS中是否有一种方法可以将纹理渲染为1:1的较小缓冲区?
Canvas方法效果最佳,我为每个精灵创建一个canvas元素,并将精灵表裁剪到每个canvas上。这是1:1和高质量的,但使用精灵表的目的是让GPU只需寻址单个图像,并需要进行HTML加载器处理。理想情况下,我不想将精灵表裁剪到更小的纹理缓冲区。
为什么使用相同的大源图像与多个THREE.Texture对象会占用如此多的内存?我原本以为它只需要在内存中保留单个纹理,并且Texture对象只需显示具有不同偏移量的相同纹理。

这个问题似乎已经在以下页面上被注意到,并提出了一些解决方法:https://github.com/mrdoob/three.js/issues/5821https://dev59.com/FoLba4cB1Zd3GeqPj9ff但是我无法在最新版本的ThreeJS中使这些解决方法起作用。 - adevart
1个回答

4

我找到了一种有效的方法。

首先,我通过使用ThreeJS ImageLoader加载spritesheet图像并创建WebGLTexture来加载纹理,将其存储在_spritesheets [textureID] .texture中。

let texture = this._spritesheets[textureID].texture;
let gl = this._renderer.getContext();
let webGLTexture = gl.createTexture();
gl.bindTexture(gl.TEXTURE_2D, webGLTexture);
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true);
gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, texture);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_S, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_WRAP_T, gl.CLAMP_TO_EDGE);
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.LINEAR);

然后我将纹理对象的webGL纹理参数设置为此值,并将其webglInit值设置为true,以便它不会创建新的缓冲区。

let frames = textureJSON.frames;
for (let frameID of Object.keys(frames)) {
    let frame = frames[frameID];
    let t = new THREE.Texture(texture);

    let data = frame.frame;
    t.repeat.set(data.w / texture.width, data.h / texture.height);
    t.offset.x = data.x / texture.width;
    t.offset.y = 1 - data.h / texture.height - data.y / texture.height;

    let textureProperties = this._renderer.properties.get(t);
    textureProperties.__webglTexture = webGLTexture;
    textureProperties.__webglInit = true;

    this._textures[frameID] = {};
    this._textures[frameID].texture = t;
    this._textures[frameID].settings = { wrapS: 1, wrapT: 1, magFilter: THREE.LinearFilter, minFilter: THREE.NearestFilter };
}

通过ThreeJS FileLoader加载spritesheet JSON文件。然后我按帧ID将精灵存储在一个_textures对象中,并可以将它们赋值给材质的map属性。


1
太好了!谢谢你。当我试图在几个对象上绘制纹理时,遇到了大量纹理加载的问题。我不想让每个对象上的纹理都从同一位置开始,这种方法完美地解决了这个问题。 - aecend
最终,我采用了一种替代方法,我发现它更简单易用。这就是使用函数Object3D.onBeforeRender调用。如果您在绘制每个具有不同纹理偏移的对象时运行此函数,则可以在每个对象渲染之前设置纹理偏移量。您可以设置一个辅助函数来更轻松地分配偏移量,并将偏移量存储在纹理ID中。 - adevart

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接