【问题标题】:Threejs update single material on glb with multiple objectsThreejs用多个对象更新glb上的单一材料
【发布时间】:2021-03-21 05:39:40
【问题描述】:

我有一个 .glb 模型,其中包含多个对象和材料。我正在更新运行时具有某种材料的孩子,但由于有近 1,000 个孩子使用这种材料,这有点令人窒息。如果存在,我正在寻找更优雅的解决方案。

AFRAME.registerComponent('apply-texture', {
    schema: {
        texture: { type: 'string' }
    },
    init: function() {
        var data = this.data;
        var el = this.el;
        var loader = new THREE.TextureLoader();
        var alphamap = loader.load("./images/PETAL-DATA.jpg");

        el.object3D.traverse(function(child) {
if (child.isMesh && child.material.name == "Material_to_update") {

                var texture = loader.load(data.texture,
                    function ( texture ) {
                        child.material.map = texture;
                        child.material.alphaMap = alphamap;
                        child.material.map.encoding = THREE.sRGBEncoding;
                        child.material.side = THREE.DoubleSide;
                        child.material.color.set("#FFFFFF");
                        child.material.alphaTest = 0.5;
                        child.material.opacity = 0.8;
                        child.material.needsUpdate = true;

                    },
                    function ( xhr ) {
                        console.log( (xhr.loaded / xhr.total * 100) + '% loaded' );
                    },
                    function ( xhr ) {
                        console.log( 'An error happened' );
                    }
                );
                
            }

由于所有子对象都使用相同的材​​质,我希望有一种方法可以只更新材质本身,而不是更新 1,000 个子对象。

【问题讨论】:

  • 你为什么要在每个循环中加载纹理??????您只需要加载一次纹理!
  • 好点@jscastro 我已经修改了,但仍然存在遍历有很多孩子的模型的问题

标签: three.js aframe


【解决方案1】:

不要创建 1000 个图像加载器,而是使用一个系统来为每个组件提供加载的纹理:

<script src="https://aframe.io/releases/1.1.0/aframe.min.js"></script>
<script>
  // register system
  AFRAME.registerSystem("image-provider", {
    init: function() {
      // load some external image
      this.image = new THREE.TextureLoader().load(
        "https://i.imgur.com/wjobVTN.jpg"
      );
    },
    getImage() {
      // the components will grab the image via this function
      return this.image;
    }
  });
  
  // register the component
  AFRAME.registerComponent("image-provider", {
    init: function() {
      // grab the system reference
      const system = this.system;
      // wait until the model is loaded
      this.el.addEventListener("model-loaded", e => {
        // grab the mesh
        const mesh = this.el.getObject3D("mesh");
        // apply the texture from the system
        mesh.traverse(node => {
          if (node.material) {
            node.material.map = system.getImage();
            node.material.needsUpdate = true;
          }
        });
      });
    }
  });
</script>

<body>
  <div style="z-index: 99999; position: fixed; top: 2.5%">
    <p>
      Model by <a href="https://poly.google.com/view/8kcvUY9eTf">Kelli Ray</a>
    </p>
  </div>
  <a-scene background="color: #FAFAFA">
    <a-assets>
      <a-asset-item id="model" src="https://cdn.glitch.com/994b27e1-a801-47b8-9b52-9547b8f25fc1%2Fgoogle%20poly.gltf"></a-asset-item>
      <a-mixin id="fish" gltf-model="#model" scale="0.001 0.001 0.001" image-provider></a-mixin>
    </a-assets>
    <a-text position="-1.5 2 -3" color="black" value="original"></a-text>
    <a-entity gltf-model="#model" position="-1 1 -3" scale="0.001 0.001 0.001"></a-entity>
    <a-entity position="0 1 -3" mixin="fish"></a-entity>
    <a-entity position="1 1 -3" mixin="fish"></a-entity>
    <a-entity position="2 1 -3" mixin="fish"></a-entity>
    <a-entity position="3 1 -3" mixin="fish"></a-entity>
  </a-scene>

如果您有多个相同的对象,您应该查看Instanced Meshes - 它允许您在只创建一个绘图调用时放置多个对象。查看this SO 线程。你可以去看看here(source)

【讨论】:

  • 以前从未使用过 registerSystem - 会调查一下 - 我在运行时将图像传递给组件 我猜语法仍然相同并且由模式处理 1 系统返回超过 1要使用的图像,即漫反射和 alpha 贴图,还是我必须为每个贴图编写另一个系统?
  • 我想我想要做的是避免遍历模型,只需更新模型中的 1 个材料,然后更新所有应用了该材料的对象
  • @heroicsatsuma 我们如何颠倒角色,系统会将纹理/法线/任何你想要的实体应用到实体 - 这将由组件提供 jsfiddle.net/dvpyjn8g
【解决方案2】:

“我想我想要做的是避免遍历模型,只需更新模型中的 1 个材料,然后更新所有应用了该材料的对象”

如果多个网格实际上共享相同的确切材质(意味着它们具有相同的 uuid),如果您更新材质的属性,那么它将在使用该材质的所有网格上更新。不过我猜他们并不都共享相同的材料。

根据您所说,我认为您只需要对第一个网格的材质进行更新,创建一个引用该材质的变量,然后遍历您的模型并将该材质应用于每个适用的网格。为了记忆起见,最好.dispose()您替换的材料。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-02-03
    • 2015-07-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-10-09
    • 1970-01-01
    • 2021-08-08
    相关资源
    最近更新 更多