在反应中加载three.js 纹理作为base64 字符串

问题描述

我正在使用服务器生成图表的 base64 字符串表示形式。然后通过使用 POST 请求获取数据来加载它以响应组件。数据加载到反应状态,但场景在加载纹理之前渲染。我读过 Is there a way to wait for THREE.TextureLoader.load() to finish? ,我还应该使用 promise 吗?我想使用反应状态进行通信,因为在下面的示例中,最终的通信将更加复杂。

const Comp= () => {
    const [state,setState] = useState(null)

    // basic three.js scene setting
    var scene = new THREE.Scene();
    var camera = new THREE.PerspectiveCamera(28,1,1000);
    camera.position.set(75,25,50);

    camera.lookAt(scene.position);
    scene.add(camera);
    camera.add(new THREE.PointLight(0xffffff,Infinity));

    var cubeGeo = new THREE.BoxBufferGeometry(3,3,0.0001);

    // fetching data
    const getFile = (event) => {
        fetch('http://127.0.0.1:5000/getFile',{
                headers: {
                    'Accept': 'application/json','Content-Type': 'application/json'
                },method: 'POST'
            })
            .then(response => response.json())
            .then(response => setState(response.state))
    }
    
    // fetch data when loading
    useEffect(
        () => getFile(),[]
    )

    // use effect when change of state happens
    // (it depends on [state])
    useEffect(
        () => {
            // state contains image data as string representation of base64 
            var texture = new THREE.TextureLoader().load("data:image/jpeg;base64," + state);
            var mat = new THREE.MeshBasicMaterial({ map: texture });

            var cubes = []
            const n = 100;
            const V = 111;
    
            // rest is irrelevant
            for (var i = 0; i < n; i++) {
                var mesh = new THREE.Mesh(cubeGeo,mat);

                cubes[i] = mesh
                var x = V * (Math.random() - .5);
                var y = V * (Math.random() - .5);
                var z = V * (Math.random() - .5);
                var r = Math.sqrt(x ** 2 + y ** 2 + z ** 2) / 20
                x /= r
                y /= r
                z /= r
                // console.log(r,x,y,z)
                cubes[i].position.x = x;
                cubes[i].position.y = y;
                cubes[i].position.z = z;
                scene.add(cubes[i]);
            }
        },[state])

        return (
            <p ref={() => animate()} />
        )
}

export default Comp;

解决方法

TextureLoader.loadonLoadonErroronProgress 使用回调。

因此,如果您想让场景等到纹理加载完毕,请在调用 onLoadonError 之前不要开始渲染。

示例:

let url = "...";
let loader = new THREE.TextureLoader();

function onLoad(tex){
  // start rendering now
}

function onError(){
  // start rendering now?
}

function onProgress(xhr){
  //...
}

loader.load(url,onLoad,onError,onProgress);

在three.js 的最新版本中,还有一个loadAsync 函数,它返回一个Promise。有了这个,您可以构建您的应用程序以利用 async/await 等待纹理完成加载。

伪代码:

// set up your scene,etc.,then...

async function run(){
  let url = "...";
  let loader = new THREE.TextureLoader();

  function onProgress(xhr){
    //...
  }

  await loader.loadAsync(url,onProgress); // or use .then/.catch

  // start rendering now
}

run();

三.js r127

,

我知道这不是正确的答案,但您应该看看 R3F React-three-fiber

https://github.com/pmndrs/react-three-fiber

并且它的 api 上有 useLoader:

https://github.com/pmndrs/react-three-fiber/blob/master/markdown/api.md#useloader

您可以在哪里:

useLoader(loader: THREE.Loader,url: string,extensions?,xhr?)