如何在 AFrame 中动态创建纹理

How to dynamically create texture in AFrame

我正在制作一个场景,在解析 json 文件后从代码生成实体。 一切正常,除了纹理加载。

为了简化问题,我重建了这个故障中的最低要求 https://glitch.com/~generate-scene

 <script>
        AFRAME.registerComponent("make-spheres", {
            init: function(){
                var sceneEl = document.querySelector('a-scene');
                var entityEl = document.createElement('a-entity');
                entityEl.setAttribute('id', 'sphere1');
                entityEl.setAttribute('geometry', { primitive: 'sphere', radius: 0.5});
                entityEl.setAttribute('material', { color: 'white', src: 'assets/pano_1.png', opacity: 0.5});
                entityEl.setAttribute('position',  "0 0.25 0");
                entityEl.setAttribute('animation__opacity',{ property: "components.material.material.opacity", from: 0.1, to: 1,  dur: 1500, loop: true, dir:"alternate"});
                entityEl.setAttribute('animation__pos1',{ property: 'position', to: "0 2 0", from: "0 0.85 0", dur: 1500, easing: "linear", loop: true, dir:"alternate"});
                entityEl.setAttribute('do-something-once-loaded', '');
                sceneEl.appendChild(entityEl);
            }
        });
    </script>

    <body>
        <a-scene>
        <a-entity id="setup" make-spheres></a-entity>

在这里,纹理加载并且一切正常。

但在我的大型项目中,隐藏在获取承诺中的动态加载组件代码导致纹理无法加载并抛出错误和警告。

HTTP 加载失败,状态为 404。加载媒体资源 http://localhost:3000/assets/pano_1.png 失败。 panomaker.html components:texture:warn $s 不是有效视频 assets/pano_1.png

AFRAME.registerComponent("init-spheres", {
    init: function(){
        let el = this.el;
        let self = this;
        self.parent = el; // Ref to 3D entity that will be the parent of all newly created entities (spheres, buttons, etc).
        // load the json file used to populate the scene
        fetch("../data.json")
            .then(function(response){
                return response.json(); // convert data to json object
            })
            .then(function(body) { 
                // When data has been converted, start generating 3D
                //console.log('fetch data response: ', body);
                let panosArray = body.data; 

                // Create the pano objects from the json data
                self.panos = getPanoramas(panosArray); 
                   ...
                // creating sphere panos
                self.panos.forEach(function(pano){
                    // Create the pano spheres.
                    pano['sphere'] = document.createElement("a-entity");
                    pano.sphere.setAttribute('id', "panoSphere_"+pano.id);
                    pano.sphere.setAttribute('geometry', {primitive: 'sphere', radius: pano.avgDistance});                   
                    pano.sphere.setAttribute('position', pano.position.x.toString()+' '+pano.position.y.toString()+' '+pano.position.z.toString() );
                    pano.sphere.setAttribute('scale', "-1 1 1");
                    pano.sphere.setAttribute('visible', true);
                    // set the src to the pano texture. src: pano.texture, opacity: 1, shader: "flat", side:"back", npot: true, blending: "normal", 
                    let texName = "assets/"+pano.texture;
                    pano.sphere.setAttribute('material', {color: "#ffffff", src: 'assets/pano_1.png', transparent: true } ); //src: texName,
                    self.sceneEl.appendChild(pano.sphere);
                });

为什么在大项目中加载失败,在小项目中却能正常工作?

问题似乎是纹理尚未准备好加载,或者有什么东西阻止它加载。我很纳闷。

我注意到有两种方法可以在 AFrame 中加载纹理。
1) 在material:src属性中使用直接路径

material="src: texture.png"

2) 将资产创建为 img 元素并在 material:src 属性中引用其 id 标签。

<a-assets>
    <img id="my-texture" src="texture.png">
  </a-assets>
material="src: #my-texture"

两种方法都可以,但首选第二种方法,因为它会预加载纹理,防止出错。 我上面的代码使用的是方法 1,它在简单版本中有效,在更复杂的项目中失败。所以我假设,如果我可以在代码中创建一个 a-asset img 源,那么这可能会起作用。但是我还没有找到让它工作的方法。 我正在尝试这个

 // Create the image assets for the scene
 let assets = document.querySelector('a-assets');
 // Create the pano textures.
 let name = 'panotex_'+pano.id;

 let texture = document.createElement('img');
 texture.setAttribute('id', name);
 texture.onload = function(){
     console.log('texture loaded');
 }
 texture.onerror = function(){
      console.log('texture failed');
 }
 let texPath = "assets/"+pano.texture;
 texture.setAttribute('src', texPath);
 assets.appendChild(texture);

但这会导致纹理无法加载并在控制台中抛出 'texture failed'。

预加载纹理、将其附加到 a-assets 并在加载纹理时执行将 运行 的处理函数的最佳方法是什么?

loading textures with THREE.js怎么样?

如果您有图像路径,可以将它们提供给 THREE.TextureLoader 对象:

// assuming this.el is the entity
var mesh = this.el.getObject3D('mesh');
var loader = new THREE.TextureLoader();
loader.load(url, 
            //onLoadCallback
            function(texture) {
              // apply the texture here
              mesh.material.map = texture
            },
            // onProgressCallback - not sure if working
            undefined,
            // onErrorCallback
            function(err) {
               console.log("error:", err)
            });

当然您不需要立即应用纹理,您可以将其存储(预加载)并在需要时使用它。

fiddle here.