Threejs合并乘法材料,但渲染不正确

时间:2019-06-27 09:12:47

标签: javascript three.js

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

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

这是我在codeandbox上的代码:

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>

1 个答案:

答案 0 :(得分:1)

之所以会发生此行为,是因为BoxGeometrysource)为每个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>