103 THREE.JS 实现局部纹理刷新

前言

由于公司的项目需要增加用户体验,而且图片尺寸比较大,动不动就会大于2k,4k,甚至更高。如果直接加载这样尺寸的图片,会需要很长时间,对于用户体验十分的不友好。
所以,需要首先加载一张小图,然后将整张大图分块进行加载,这样可以使用户第一时间查看的到图片,并且在一定的时间内,也将分块图片加载进来,更新纹理,提高清晰度。
最初我是通过canvas画布实现,创建一块完整图片大小的画布,首先将小图加载进去,然后让材质更新,显示出来当前的图片,然后每一块小块图加载完成,绘制到画布上面,设置material.map.needsUpdate为true来更新GPU的纹理。制作完成后,发现通过这种方式实现后,性能会有卡顿,每更新一次整张图片务必会影响性能。
最后,发现了Three.js的纹理局部更新的方法renderer.copyTextureToTexture可以实现,只更新局部数据,提高性能。接下来我们看一下这个方法如何使用。

方法使用

copyTextureToTexture ( position : Vector2, srcTexture : Texture, dstTexture : Texture, level : Number )

  • position — 当前更新纹理的起点位置,注意,这个位置是基于纹理的左下角开始的。
  • srcTexture —更新的块图,必须是一个Texture对象。
  • dstTexture — 将需要更新的纹理,前一个纹理将会从设置的起点开始绘制自身的高度和宽度的一块区域。
  • level — 指定纹理的细致程度。级别0是基本图像级别,级别n是第n个mipmap缩减级别。默认值为0,不写也可以 。

实际案例

有了这个方法以后,我就知道能够实现当前的局部更新功能。

首先是准备工作,我们需要的东西是整图的宽高和每一张的图片的大小和它在整张图上面所处于的位置。

然后,我们通过整张的尺寸创建一个纹理,并赋值给一个材质:

        material = new THREE.MeshBasicMaterial({map:new THREE.Texture()}); //创建材质
        let canvas = document.createElement("canvas");
        let ctx = canvas.getContext("2d");

        canvas.width = sceneBlockModel.sceneWidth;
        canvas.height = sceneBlockModel.sceneHeight;

        ctx.drawImage(img, 0, 0, sceneBlockModel.sceneWidth, sceneBlockModel.sceneHeight);

        material.map.image = canvas;
        material.map.minFilter = THREE.LinearFilter;
        material.map.generateMipmaps = false;
        material.map.needsUpdate = true;

这些工作在img图片加载完成后才可以制作。

然后就是把所有的分块图片加载到浏览器内,在加载完成时,触发回调,在相应的位置调用copyTextureToTexture更新纹理即可:

 //加载分块图片
 let xLen = Math.ceil(sceneBlockModel.sceneWidth / sceneBlockModel.fileBlockWidth);
 let yLen = Math.ceil(sceneBlockModel.sceneHeight / sceneBlockModel.fileBlockHeight);

 //延迟三秒开始加载分块图片
 setTimeout(function () {
     for (let x = 0; x < xLen; x++) {
         for (let y = 0; y < yLen; y++) {
             let img = new Image();
             img.src = sceneBlockModel.dir + x + "_" + y + ".jpg";
             img.onload = function () {

                 let texture = new THREE.Texture(img);

                 //获取渲染的起始位置
                 let position = new THREE.Vector2();

                 position.x = y * sceneBlockModel.fileBlockWidth;

                 if (x === yLen - 1) {
                     position.y = 0;
                 }
                 else {
                     position.y = (yLen - 2 - x) * sceneBlockModel.fileBlockHeight + (sceneBlockModel.sceneHeight % sceneBlockModel.fileBlockHeight);
                 }

                 renderer.copyTextureToTexture(position, texture, material.map);
             }
         }
     }
 }, 3000);

案例里面我是设置延迟加载分块图片,由于图片是按照一定规则切块的,所以,我直接两个循环,通过计算位置,来实现的等位置渲染。

案例查看地址

https://www.wjceo.com/blog/threejs2/2018-11-02/179.html

你可能感兴趣的:(Three.js笔记)