【问题标题】:synch vs. asynch texture loading in three.jsThree.js 中的同步与异步纹理加载
【发布时间】:2017-06-25 03:19:46
【问题描述】:

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

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。 (为什么?)。无论如何,我可以编写它来使用这样的纹理加载器:

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);
});

这也有效,但看起来相当复杂。我知道异步对网络应用程序等有好处,但是......它假设我保证会在grass.png之前加载cover.png。这真的可以保证吗?我宁愿坚持使用同步 loadTexture 实用程序,但也许有充分的理由不使用 loadTexture(除了它已被弃用)。 TIA。

【问题讨论】:

  • 你可以先加载所有你需要的资源,使用THREE.LoadingManager(),完成后调用动画函数。

标签: asynchronous three.js textures


【解决方案1】:

TextureLoader.load() 返回纹理对象,因此如果您不关心同步加载,则可以以相同方式使用新的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
});

【讨论】:

  • 啊,文档中没有提到这一点,但是如果您阅读代码本身就可以看到这一点。谢谢。再次提醒我,使用 three.js RTFC!
  • 糟糕。事实证明,这并不总是有效。 textureLoader 总是返回一个纹理,但有时图像已加载,有时未定义。在后一种情况下,会出现错误:“THREE.WebGLRenderer:纹理标记为更新,但图像未定义纹理”来自three.js 行 17571(版本 82)。所以看起来我必须回到“不推荐使用”的方法或编写复杂的代码。但也许我仍然缺少一些东西。
  • 是的,阅读代码会有所帮助。我已经用一个示例更新了答案,以在运行主代码之前加载所有纹理。
  • 感谢您的反馈。我想这是正确的方法。以前简单多了。但它是 JavaScript 和 Web,而不是 C++
  • 请注意,它比这更“复杂”。如果使用纹理的代码正在创建一个网格,那么在它全部执行之前你不能使用网格。这包括任何可能影响如此创建的网格的动画。当然,您也可以将对 animateScene() 的初始调用放在从属子句中。但现在它变得非常复杂(恕我直言)。如果对 loadTexture 的调用是同步的,则简单得多。是的,这是阻止,但要付出很小的代价,IMO。
【解决方案2】:

我的做法:

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);
        }
    });
}

然后,在 init 函数内部:

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);
    }
}

【讨论】:

猜你喜欢
  • 2023-03-08
  • 1970-01-01
  • 1970-01-01
  • 2012-07-07
  • 2022-01-25
  • 1970-01-01
  • 2021-10-08
  • 1970-01-01
  • 2019-10-27
相关资源
最近更新 更多