【问题标题】:How to dynamically create texture in AFrame如何在 AFrame 中动态创建纹理
【发布时间】:2020-01-09 01:18:09
【问题描述】:

我正在制作一个在解析 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>

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

但在我较大的项目中,动态加载组件代码隐藏在 fetch promise 中,纹理无法加载并引发错误和警告。

HTTP 加载失败,状态为 404。媒体资源 http://localhost:3000/assets/pano_1.png 加载失败。 panomaker.html components:texture:warn $s 不是有效的视频资产/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)在材质中使用直接路径: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,它在简单版本中有效,在更复杂的项目中失败。所以我假设,如果我可以在代码中创建一个资产 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);

但这会导致纹理无法加载并在控制台中抛出“纹理失败”。

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

【问题讨论】:

    标签: aframe


    【解决方案1】:

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

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

    小提琴here.

    【讨论】:

    • 太棒了!几乎你给我的每一个见解都指向以三种方式来做这件事。也许是时候卸下训练轮了。
    • 有效!!我认为我最初的问题与错误的路径有关(我的图像没有放在服务器托管的文件夹中)。不过,我更喜欢这个加载器解决方案,因为它有可靠的成功或错误回调。
    • @ThomasWilliams 我也很难潜入三个,但是你可以用三个来补充/解决框架是多么容易:)
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-20
    • 2018-12-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-23
    相关资源
    最近更新 更多