three.js 中的同步与异步纹理加载

synch vs. asynch texture loading in three.js

我在午餐时间一直在玩 Three.js,实现旧的 NEHE 演示(到目前为止最多 #30)。令人讨厌的一方面是新的异步纹理加载器。我有一个着色器 material 演示,其中 material 是这样创建的:

var uniforms = {
    tOne: { type: "t", value: THREE.ImageUtils.loadTexture( "images/cover.png" ) },
    tSec: { type: "t", value: THREE.ImageUtils.loadTexture( "images/grass.png" ) }
};
var material_shh = new THREE.ShaderMaterial({
    uniforms: uniforms,
    vertexShader: vertShader,
    fragmentShader: fragShader
});
var mesh = new THREE.Mesh( cubeGeom, material_shh );
gfxScene.add(mesh);

这工作正常,但 three.js 在控制台中向我抱怨说 loadTexture 已被弃用。 (为什么?)。无论如何,我可以这样写它来使用 textureLoader:

var textureLoader = new THREE.TextureLoader();
var cover, grass;
textureLoader.load( "images/cover.png", function( texture ) {
    cover = texture;
});

textureLoader.load( "images/grass.png", function( texture ) {
    grass = texture;

   var uniforms = {
        tOne: { type: "t", value: cover  },
        tSec: { type: "t", value: grass  }
    };

    var material_shh = new THREE.ShaderMaterial({
        uniforms: uniforms,
        vertexShader: vertShader,
        fragmentShader: fragShader
    });

    var mesh = new THREE.Mesh( cubeGeom, material_shh );
    gfxScene.add(mesh);
});

这也行,但似乎相当复杂。我知道异步对 Web 应用程序等有好处,但是......它假设我保证 cover.png 将在 grass.png 之前加载。这实际上有保证吗?我宁愿坚持使用同步 loadTexture 实用程序,但也许有充分的理由不使用 loadTexture(除了它已被弃用)。 TIA.

TextureLoader.load() returns 纹理对象,所以如果您不关心同步负载,那么您可以以相同的方式使用新的 TextureLoader

var textureLoader = new THREE.TextureLoader();
var uniforms = {
    tOne: {type: "t", value: textureLoader.load("images/cover.png")},
    tSec: {type: "t", value: textureLoader.load("images/grass.png")}
};

编辑 #1

对于同步加载,您可以使用 LoadingManager。目前它不是最优雅的 API,但它应该适用于简单的情况。此示例适用于版本 r84.

function loadTextures(urls, callback) {

    var textures = [];

    var onLoad = function() {
        callback(null, textures);
    };

    var onProgress = function() {};

    var onError = function(url) {
        callback(new Error('Cannot load ' + url));
    };

    var manager = new THREE.LoadingManager(onLoad, onProgress, onError);

    var loader = new THREE.TextureLoader(manager);

    for (var i=0; i<urls.length; i++) {
        textures.push(loader.load(urls[i]));
    }
}

var urls = [
    "images/cover.png",
    "images/grass.png"
];

loadTextures(urls, function(error, textures) {
    if (error) {
        console.log(error);
        return;
    }
    // Main code goes here using the textures array
});

我的做法:

let urls = [
    'img/right.jpg',//right
    'img/left.jpg',//left
    'img/top.jpg',//top
    'img/top.jpg',//bottom
    'img/front.jpg',//front
    'img/front.jpg',//back
];

function loadTextures(urls) {
    return new Promise((resolve, reject) => {
        let textures = [];
        let onLoad = function () {
            resolve(textures);
        };
        let onProgress = function () { };
        let onError = function (url) {
            resolve(false);
        };

        let manager = new THREE.LoadingManager(onLoad, onProgress, onError);
        for (let i = 0; i < urls.length; i++) {
            let url = urls[i];
            let loader = new THREE.TextureLoader(manager);
            textures[i] = loader.load(url);
            console.log('textures 0', textures, url);
        }
    });
}

然后,在初始化函数中:

let init = async function () {
    let textures = await loadTextures(urls);
    if (textures) {
        let materials = [];
        for (var i = 0; i < textures.length; i++) {
            materials[i] = new THREE.MeshPhongMaterial({
                color: 0xf0f0f0,
                map: textures[i]
            });
        }

        box = new THREE.Mesh(geometryBox, materials);
        scene.add(box);
    }
}