【问题标题】:Threejs merge multiply materials, but render incorrectly三个J合并多个材质,但是渲染不正确
【发布时间】:2019-11-09 06:42:14
【问题描述】:

我想合并两个不同材质的网格。

这是我所做的,但渲染不正确,就像渲染对象的一侧。

这是我在代码框上的代码:

https://codesandbox.io/s/summer-dawn-5nq05?fontsize=14

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <script src="https://threejs.org/build/three.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  </head>

  <body>
    <script>
      let renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);

      // Scene

      let scene = new THREE.Scene();
      scene.background = new THREE.Color(0xcce0ff);

      // Camera

      let camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        1,
        1000
      );
      camera.position.set(30, 0, 100);

      let controls = new THREE.OrbitControls(camera);

      for (let i = 0; i < 1; i++) {
        // Geometry

        let cube1Geometry = new THREE.PlaneGeometry(10, 10, 10);
        let cube2Geometry = new THREE.PlaneGeometry(10, 15, 5);

        // Material

        let cube1Material = new THREE.MeshStandardMaterial({
          color: 0x7c3c3c,
          roughness: 1
        });
        let cube2Material = new THREE.MeshStandardMaterial({
          color: 0x01b8ba,
          roughness: 0.1
        });

        // Mesh

        let cube1 = new THREE.Mesh(cube1Geometry, cube1Material);
        let cube2 = new THREE.Mesh(cube2Geometry, cube2Material);

        // Combine

        let singleGeometry = combine([cube1, cube2]);

        let single = new THREE.Mesh(singleGeometry, [
          cube1Material,
          cube2Material
        ]);
        // scene.add(cube1);
        // scene.add(cube2);
        scene.add(single);

        single.position.set(
          Math.random() * 5,
          Math.random() * 10,
          Math.random() * 20
        );
      }

      function combine(meshes) {
        let mergeGeometry = new THREE.Geometry();

        for (let i = 0; i < meshes.length; i++) {
          meshes[i].updateMatrix();
          // update materialIndex
          for (let j = 0; j < meshes[i].geometry.faces.length; j++) {
            meshes[i].geometry.faces[j].materialIndex = 0;
          }
          mergeGeometry.merge(meshes[i].geometry, meshes[i].matrix, i);
        }

        return mergeGeometry;
      }

      // LIGHT
      let light1 = new THREE.AmbientLight(0x666666);
      scene.add(light1);
      let light = new THREE.SpotLight(0xdfebff, 1);
      light.position.set(50, 200, 100);
      scene.add(light);

      requestAnimationFrame(function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      });
    </script>
  </body>
</html>

【问题讨论】:

  • 欢迎来到 Stack Overflow。请花点时间拨打tour,访问Help Center,并阅读How do I ask a good question?
  • 针对您的问题:请在您的问题正文中包含您的代码。当您的链接无效时(而不是如果),这可以防止上下文丢失。

标签: javascript three.js


【解决方案1】:

答案已过时

Threejs 的 API 发生了变化,不再支持旧的 Geometry 接口,现在你需要直接处理 BufferGeometries 及其属性。您可以在 DOCS 上找到更多合并实用程序:BufferGeometryUtils


发生这种行为是因为BoxGeometry (source) 为其每个faces 规定了不同的MaterialIndex。当使用材质数组时,这反过来又控制了该面将使用哪种材质。

为确保每个几何体仅使用阵列中的指定材质,您需要将几何体的每个面的MaterialIndex 重置为0。这样,geometry.merge() 将正确调整每个几何图形以使用正确的材料。

我已经修改了您的 combine 函数以即时执行此操作。我不确定是否有更好的方法来重置MaterialIndex,至少我找不到。

function combine(meshes) {
  
    let mergeGeometry = new THREE.Geometry();
    
    for (let i = 0; i < meshes.length; i++) {
    
      meshes[i].updateMatrix();
      
      // update materialIndex
      for ( let j = 0; j < meshes[i].geometry.faces.length; j++ ) {
      
        meshes[i].geometry.faces[j].materialIndex = 0;
        
      }
      
      mergeGeometry.merge( meshes[i].geometry, meshes[i].matrix, i );
      
    }
    
    return mergeGeometry;
    
}

JSFiddle Example

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <title>Title</title>
    <style>
      body {
        margin: 0;
        position: fixed;
      }
      canvas {
        width: 100%;
        height: 100%;
        display: block;
      }
    </style>
    <script src="https://threejs.org/build/three.js"></script>
    <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  </head>

  <body>
    <script>
      let renderer = new THREE.WebGLRenderer({ antialias: true });
      renderer.setSize(window.innerWidth, window.innerHeight);
      document.body.appendChild(renderer.domElement);


      // Scene

      let scene = new THREE.Scene();
      scene.background = new THREE.Color(0xcce0ff);

      // Camera

      let camera = new THREE.PerspectiveCamera(
        75,
        window.innerWidth / window.innerHeight,
        1,
        1000
      );
      camera.position.set(30, 0, 50);

      let controls = new THREE.OrbitControls(camera);

      // Geometry

      let cube1Geometry = new THREE.BoxGeometry(10, 10, 10);
      let cube2Geometry = new THREE.BoxGeometry(10, 15, 5);
      

      // Material

      let cube1Material = new THREE.MeshStandardMaterial({
        color: 0x7c3c3c,
        roughness: 1
      });
      let cube2Material = new THREE.MeshStandardMaterial({
        color: 0x01b8ba,
        roughness: 0.1
      });

      // Mesh

      let cube1 = new THREE.Mesh(cube1Geometry, cube1Material);
      let cube2 = new THREE.Mesh(cube2Geometry, cube2Material);

      // Combine

      let singleGeometry = combine([cube1, cube2]);

      let single = new THREE.Mesh(singleGeometry, [
        cube1Material,
        cube2Material
      ]);

      scene.add(single);

      function combine(meshes) {
      
        let mergeGeometry = new THREE.Geometry();
        
        for (let i = 0; i < meshes.length; i++) {
        
          meshes[i].updateMatrix();
          
          // update materialIndex
          for ( let j = 0; j < meshes[i].geometry.faces.length; j++ ) {
          
            meshes[i].geometry.faces[j].materialIndex = 0;
            
          }
          
          mergeGeometry.merge( meshes[i].geometry, meshes[i].matrix, i );
          
        }
        
        return mergeGeometry;
        
      }

      // LIGHT
      let light1 = new THREE.AmbientLight(0x666666);
      scene.add(light1);
      let light = new THREE.SpotLight(0xdfebff, 1);
      light.position.set(50, 200, 100);
      scene.add(light);

      requestAnimationFrame(function animate() {
        requestAnimationFrame(animate);
        renderer.render(scene, camera);
      });
    </script>
  </body>
</html>

【讨论】:

  • 很高兴为您提供帮助,如果正确解决了您的问题,请将答案标记为正确。
  • 知道为什么 JSFiddle 似乎不再工作了@ScieCode?​​span>
  • Threejs 的 API 发生了变化,不再支持旧的 Geometry 接口,现在需要直接处理BufferGeometries 及其属性。您可以在DOCS: BufferGeometryUtils 上找到更多合并实用程序。
猜你喜欢
  • 2018-03-19
  • 2019-10-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-23
  • 2020-04-11
  • 2018-11-24
相关资源
最近更新 更多