【问题标题】:Adding a different colour to each side of this obj为这个 obj 的每一侧添加不同的颜色
【发布时间】:2018-06-19 00:51:45
【问题描述】:

我为我的应用程序重新创建了一个包模型并将其作为 .obj 导出到 ThreeJs:

我已经为模型几何中的每个面分配了不同的颜色,如下所示:

var geometry = new THREE.Geometry().fromBufferGeometry( bagMesh.children[0].geometry );

for (var i = 0; i < geometry.faces.length; i ++ ) {
  var face = geometry.faces[i];
  // 7 & 8 = front side
  // can we flip its normal?
  if(i === 7 || i === 8) { 
    face.color.setHex( 0xff0000 );
  } else {
    face.color.setHex( Math.random() * 0xffffff );
  }
}
geometry.translate( 0, -1, 0.75);
mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
scene.add(mesh);

我已经在 faces 数组的索引 7 和 8 处识别出正面的面,并将它们变为红色。

问题是当我看包里面时也可以看到这种颜色:

我意识到这是因为我已将对象设置为 THREE.DoubleSide,但如果我将其更改为 THREE.FrontSide,则侧面仅部分可见。

所以我的问题是如何为每一面分配不同的独特颜色(全部 11 种,也包括内部),而该颜色不会出现在相对的那一边?

我试图通过仅使用颜色而不是将图像映射到其上来使事情变得简单,这是我最终想要实现的。

注意 - 我之前的模型通过将每一面视为单独的网格解决了这个问题,但这会导致其他问题,例如 z 隐藏和闪烁问题。

谢谢

编辑

@WestLangley 我设置了一个小提琴来演示您在评论中添加的内容。假设我做对了它并没有达到预期的效果:

(function onLoad() {
  var canvasElement;
  var width, height;
  var scene, camera;
  var renderer;
  var controls;

  var pivot;
  var bagMesh;
  var planeMesh;
  
  const objLoader = new THREE.OBJLoader2();
  const fileLoader = new THREE.FileLoader();
 
  init();

  function init() {
    container = document.getElementById('container');
    initScene();
    addGridHelper();
    addCamera();
    addLighting();
    addRenderer();
    addOrbitControls();

    loadPlaneObj();
    
    // Logic
		var update = function() {};

		// Draw scene
		var render = function() {
			renderer.render(scene, camera);
		};

		// Run game logic (update, render, repeat)
		var gameLoop = function() {
			requestAnimationFrame(gameLoop);
			update();
			render();
		};
		gameLoop();
  }

  /**** Basic Scene Setup ****/
  function initScene() {
    scene = new THREE.Scene();
    scene.background = new THREE.Color(0xd3d3d3);
    var axis = new THREE.AxesHelper();
    scene.add(axis);
  }

  function addCamera() {
    camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 1, 1000);
    camera.position.set(3,3,3);
    scene.add(camera);
  }

  function addGridHelper() {
    var planeGeometry = new THREE.PlaneGeometry(2000, 2000);
    planeGeometry.rotateX(-Math.PI / 2);

    var planeMaterial = new THREE.ShadowMaterial({
      opacity: 0.2
    });
    var plane = new THREE.Mesh(planeGeometry, planeMaterial);
    plane.position.y = -200;
    plane.receiveShadow = true;
    scene.add(plane);

    var helper = new THREE.GridHelper(2000, 100);
    helper.material.opacity = 0.25;
    helper.material.transparent = true;
    scene.add(helper);

    var axis = new THREE.AxesHelper();
    scene.add(axis);
  }

  // *********** Lighting settings **********************
  function addLighting() {
    var light = new THREE.HemisphereLight(0xffffff, 0xffffff, 1);
    scene.add(light);
  }

  // ************** Material settings **************
  function setMaterial(materialName) {
    // get the object from the scene
    var bagMesh = scene.getObjectByName('bag');
    var material;

    if (!materialName) {
      materialName = materials.material;
    }

    if (bagMesh) {
      var colour = parseInt(materials.colour);
      switch (materialName) {
        case 'MeshBasicMaterial':
          material = new THREE.MeshBasicMaterial({
            color: colour
          });
          break;
        case 'MeshDepthMaterial':
          material = new THREE.MeshDepthMaterial();
          break;
        case 'MeshLambertMaterial':
          material = new THREE.MeshLambertMaterial({
            color: colour
          });
          break;
        case 'MeshNormalMaterial':
          material = new THREE.MeshNormalMaterial();
          break;
        case 'MeshPhongMaterial':
          material = new THREE.MeshPhongMaterial({
            color: colour
          });
          break;
        case 'MeshPhysicalMaterial':
          material = new THREE.MeshPhysicalMaterial({
            color: colour
          });
          break;
        case 'MeshStandardMaterial':
          material = new THREE.MeshStandardMaterial({
            color: colour
          });
          break;
        case 'MeshToonMaterial':
          material = new THREE.MeshToonMaterial({
            color: colour
          });
          break;
      }
      bagMesh.children.forEach(function(c) {
        c.material = material;
      });
    }
  }

  function setMaterialColour(colour) {
    materials.colour = colour;
    setMaterial(null);
  }
  // ************** End of materials ***************

  function addRenderer() {
    renderer = new THREE.WebGLRenderer({
      antialias: true
    });
    renderer.setPixelRatio(window.devicePixelRatio);
    renderer.setSize(window.innerWidth, window.innerHeight);
    renderer.shadowMap.enabled = true;
    container.appendChild(renderer.domElement);
  }

  function addOrbitControls() {
    var controls = new THREE.OrbitControls(camera, renderer.domElement);
  }

  function addPivot() {
    var cubeGeo = new THREE.BoxBufferGeometry(5, 5, 5);
    var cubeMat = new THREE.MeshBasicMaterial();
    pivot = new THREE.Mesh(cubeGeo, cubeMat);
    bagMesh.position.x -= 15;
    bagMesh.position.z -= 55;

    pivot.add(bagMesh);
    pivot.add(handle);
    scene.add(pivot);
  }
  
  function loadPlaneObj() {
		loadObj('Plane', 'https://rawgit.com/Katana24/threejs-experimentation/master/models/Plane.obj', 'https://rawgit.com/Katana24/threejs-experimentation/master/models/Plane.mtl', addPlaneToSceneSOAnswer);
	}
  
  function loadObj(objName, objUrl, mtlUrl, onLoadFunc) {
		var onLoadMtl = function(materials) {
	    objLoader.setModelName(objName);
	    objLoader.setMaterials(materials);

	    fileLoader.setPath('');
	    fileLoader.setResponseType('arraybuffer');
	    fileLoader.load(objUrl,
	      function(onLoadContent) {
	        var mesh = objLoader.parse(onLoadContent);
	        onLoadFunc(mesh);
	      },
	      function(inProgress) {},
	      function(error) {
	        throw new Error('Couldnt load the model: ', error);
	      });
	  };
	  objLoader.loadMtl(mtlUrl, objName+'.mtl', onLoadMtl);
	}
  
  function addPlaneToSceneSOAnswer(mesh) {
		var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
		var backMaterial 	= new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
		
		var geometry = new THREE.Geometry().fromBufferGeometry( mesh.children[0].geometry );
		var length = geometry.faces.length;
		geometry.faces.splice(14, 1);

		for (var i = 0; i < geometry.faces.length; i ++ ) {
			var face = geometry.faces[i];
			face.color.setHex(Math.random() * 0xffffff);
		}
		mesh = new THREE.Mesh( geometry, new THREE.MeshBasicMaterial({ vertexColors: THREE.FaceColors, side: THREE.DoubleSide }) );
		mesh.material.side = THREE.FrontSide; 
		
		var mesh2 = new THREE.Mesh( geometry, mesh.material.clone() ); 
		mesh2.material.side = THREE.BackSide; 
		// mesh2.material.vertexColors = THREE.NoColors; 
		mesh2.material.vertexColors = [new THREE.Color(0xff0000), new THREE.Color(0x00ff00), new THREE.Color(0x0000ff)];

		mesh.add( mesh2 );
		scene.add(mesh);
	}
})();
body {
  background: transparent;
  padding: 0;
  margin: 0;
  font-family: sans-serif;
}

#canvas {
  margin: 10px auto;
  width: 800px;
  height: 350px;
  margin-top: -44px;
}
<body>
  <div id="container"></div>
  <script src="https://threejs.org/build/three.js"></script>
  <script src="https://threejs.org/examples/js/libs/dat.gui.min.js"></script>
  <script src="https://threejs.org/examples/js/controls/OrbitControls.js"></script>
  <script src="https://threejs.org/examples/js/loaders/MTLLoader.js"></script>
  <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/LoaderSupport.js"></script>
  <script src="https://rawgit.com/mrdoob/three.js/dev/examples/js/loaders/OBJLoader2.js"></script>
</body>

我在这里错过了什么?

【问题讨论】:

  • 在您之前将每一面视为单独网格的方法中,您是否在一个上使用 THREE.FrontSide 而在另一个上使用 THREE.BackSide?我不认为你在这里使用的面部颜色不会在不复制网格的情况下工作......使用图像(一个网格上有两种材料)是另一种选择。
  • 您能否详细说明一下使用两种材料制作一个网格是什么意思以及如何区分两侧?
  • mesh.material.side = THREE.FrontSide; var mesh2 = new THREE.Mesh( geometry, material.clone() ); mesh2.material.side = THREE.BackSide; mesh2.material.vertexColors = THREE.NoColors; mesh.add( mesh2 );
  • @WestLangley 我已经用您的评论编辑了我的答案。你能详细说明一下吗?
  • 如果您希望内部为纯色,我发布的内容将起作用。如果您希望内部具有顶点颜色,那么您也需要克隆几何体 (geometry.clone()),并在克隆的几何体上设置所需的内部顶点颜色。

标签: three.js


【解决方案1】:

我听从了 Don 关于不同材料的建议,但完全不明白他的意思。

我检查了这个question,其中详细说明了设置materialIndex。我调查了这意味着什么,这意味着当您将geometrymaterials 数组传递给这样的网格时:

mesh = new THREE.Mesh( geometry, [frontMaterial, backMaterial, otherMaterial] );

然后该面将获得分配给它的材质(frontMaterial,因为它位于位置 0)。

回到我最初的问题,我决定(暂时)简化一下,看看我是否可以将我想要的应用到从 Blender 导出的平面网格中。

当添加到 3JS 中时,Plane 有两个 Faces。我发现我可以翻转每个面或为每个面分配不同的材质,但我需要复制面以实现此目的:

function addMeshTwoToScene() {
    var frontMaterial = new THREE.MeshBasicMaterial( { color : 0xff0000, side: THREE.FrontSide } );
    var backMaterial    = new THREE.MeshBasicMaterial( { color : 0x00ff00, side: THREE.BackSide } );
    var geometry = new THREE.Geometry().fromBufferGeometry( planeMesh.children[0].geometry );

    // Duplicates the face
    var length = geometry.faces.length;

    for (var i = 0; i < length; i++ ) {
      var face = geometry.faces[i];
      var newFace = Object.assign({}, face);
      geometry.faces.push(newFace);
    }

    for (var i = 0; i < geometry.faces.length; i ++ ) {
      var face = geometry.faces[i];
      if(i === 0 || i === 3) {
        face.materialIndex = 0;
      } else {
        face.materialIndex = 1;
      }
    }
    var mesh = new THREE.Mesh( geometry, [frontMaterial, backMaterial] );
    scene.add(mesh);
}

这会导致以下结果:

我不会将此标记为已接受的答案,因为我仍需要将其应用于问题中更复杂的模型,而且我认为仍有更好的方法来做到这一点,例如翻转特定顶点到其他值。

【讨论】:

    【解决方案2】:

    一种解决方案是使用 ShaderMaterial 并根据面是正面还是背面来定义颜色。

    让我带你看看this simple example

    按住左键旋转网格。如果您对 ShaderFrog 不熟悉,请点击右侧的“Edit Source”并向下滚动片段着色器底部。

    if (!gl_FrontFacing) gl_FragColor = vec4(vec3(0.0, 0.0, 1.0) * brightness, 1.0);

    gl_FrontFacing 是一个布尔值。不言自明,如果脸在前面,它将返回 true,否则返回 false。 该行显示“如果面部不是正面,则在 alpha = 1.0 时将其渲染为蓝色。

    希望对您有所帮助。

    【讨论】:

    • 我曾考虑使用 ShaderMaterial 来获得所需的颜色,但这仍然适用于将图像附加到材质上吗?
    • 或者当然,如果你的模型的 uvs 已经设置好了,将纹理作为制服传递就可以了。从这里,您将能够添加纹理、混合多个纹理......天空是极限
    • 好的 - 我会进一步检查
    猜你喜欢
    • 1970-01-01
    • 2014-02-14
    • 2020-09-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-12-15
    • 1970-01-01
    相关资源
    最近更新 更多