绘制立方体面作为一个整体,而不是构成面部的三角形 - three.js

时间:2017-06-29 15:00:38

标签: javascript three.js mesh face

尝试用不同颜色绘制每个立方体面,我发现thread提供了实现此目的的方法:

var geometry = new THREE.BoxGeometry(5, 5, 5);
for (var i = 0; i < geometry.faces.length; i++) {
    geometry.faces[i].color.setHex(Math.random() * 0xffffff);
}

var material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    vertexColors: THREE.FaceColors
});

但是使用three.js r86,我得到以下结果:

LeCube with painted triangles

获得构成每张脸的三角形,单独绘制。

为了达到理想的效果,我使用了上面代码的以下改编:

var geometry = new THREE.BoxGeometry(5, 5, 5);
for ( var i = 0; i < geometry.faces.length; i += 2 ) {
    var faceColor = Math.random() * 0xffffff;
    geometry.faces[i].color.setHex(faceColor);
    geometry.faces[i+1].color.setHex(faceColor);
}

var material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    vertexColors: THREE.FaceColors
});

LeCube with painted faces as a whole

但这一切似乎都有点过了!

&#13;
&#13;
'use strict';

var camera, scene, renderer, cube;

init();
render();

function init() {

  scene = new THREE.Scene();
  camera = new THREE.PerspectiveCamera(75, window.innerWidth / window.innerHeight, 0.1, 1000);

  // renderer

  renderer = new THREE.WebGLRenderer({
    alpha: true
  });
  renderer.setSize(window.innerWidth, window.innerHeight);
  document.body.appendChild(renderer.domElement);

  camera.position.z = 12;

  // Mesh - cube

  var geometry = new THREE.BoxGeometry(5, 5, 5);
  for (var i = 0; i < geometry.faces.length; i += 2) {
    var faceColor = Math.random() * 0xffffff;
    geometry.faces[i].color.setHex(faceColor);
    geometry.faces[i + 1].color.setHex(faceColor);
  }

  var material = new THREE.MeshBasicMaterial({
    color: 0xffffff,
    vertexColors: THREE.FaceColors
  });

  cube = new THREE.Mesh(geometry, material);
  scene.add(cube);

  // Light

  var pointLight = new THREE.PointLight(0xFFFFFF);

  pointLight.position.x = 10;
  pointLight.position.y = 50;
  pointLight.position.z = 130;

  scene.add(pointLight);

}

function render() {

  cube.rotation.x = 16;
  cube.rotation.y = 4;
  cube.rotation.z -= 5;

  renderer.render(scene, camera);

}
&#13;
body,
canvas {
  margin: 0;
  padding: 0;
}

body {
  overflow: hidden;
  background-color: #fff;
}
&#13;
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/86/three.js"></script>
&#13;
&#13;
&#13;

我是否遗漏了three.js上的内容以完成整个脸部绘画?

2 个答案:

答案 0 :(得分:5)

如果您切换到BufferGeometry,则可以使用groups来控制几何图形的材质。组基于顶点索引,并允许您定义材质索引,该索引将引用材质数组内的材质。

考虑:

// start, count, material index
bufferGeometry.addGroup(12, 6, 2)

这告诉几何体在索引索引 12处开始一组新的三角形,并占6个索引(引用6个顶点)。最后一个参数告诉该组三角形使用材料索引 2(用于创建网格的材料数组的索引2)。

在下面的例子中,我给了一个立方体的每一面不同的颜色。您可能认为这与设置面部颜色具有相同的效果,但请注意,这是为每个组设置材质,而不仅仅是一种颜色,这可能会产生一些非常酷的效果。

var renderer, scene, camera, controls, stats, mesh;

var WIDTH = window.innerWidth,
  HEIGHT = window.innerHeight,
  FOV = 35,
  NEAR = 1,
  FAR = 1000;

function populateScene() {
  var bg = new THREE.BufferGeometry();
  bg.addAttribute("position", new THREE.BufferAttribute(new Float32Array([
    // front
    -1, 1, 1, // 0
    -1, -1, 1, // 1
    1, 1, 1, // 2
    1, -1, 1, // 3
    // right
    1, 1, 1, // 4
    1, -1, 1, // 5
    1, 1, -1, // 6
    1, -1, -1, // 7
    // back
    1, 1, -1, // 8
    1, -1, -1, // 9
    -1, 1, -1, // 10
    -1, -1, -1, // 11
    // left
    -1, 1, -1, // 12
    -1, -1, -1, // 13
    -1, 1, 1, // 14
    -1, -1, 1, // 15
    // top
    -1, 1, -1, // 16
    -1, 1, 1, // 17
    1, 1, -1, // 18
    1, 1, 1, // 19
    // bottom
    -1, -1, 1, // 20
    -1, -1, -1, // 21
    1, -1, 1, // 22
    1, -1, -1 // 23
  ]), 3));
  bg.addAttribute("normal", new THREE.BufferAttribute(new Float32Array([
    // front
    0, 0, 1, // 0
    0, 0, 1, // 1
    0, 0, 1, // 2
    0, 0, 1, // 3
    // right
    1, 0, 0, // 4
    1, 0, 0, // 5
    1, 0, 0, // 6
    1, 0, 0, // 7
    // back
    0, 0, -1, // 8
    0, 0, -1, // 9
    0, 0, -1, // 10
    0, 0, -1, // 11
    // left
    -1, 0, 0, // 12
    -1, 0, 0, // 13
    -1, 0, 0, // 14
    -1, 0, 0, // 15
    // top
    0, 1, 0, // 16
    0, 1, 0, // 17
    0, 1, 0, // 18
    0, 1, 0, // 19
    // bottom
    0, -1, 0, // 20
    0, -1, 0, // 21
    0, -1, 0, // 22
    0, -1, 0 // 23
  ]), 3));
  bg.setIndex(new THREE.BufferAttribute(new Uint32Array([
    // front 0
    0, 1, 2,
    3, 2, 1,
    // right 6
    4, 5, 6,
    7, 6, 5,
    // back 12
    8, 9, 10,
    11, 10, 9,
    // left 18
    12, 13, 14,
    15, 14, 13,
    // top 24
    16, 17, 18,
    19, 18, 17,
    // bottom 30
    20, 21, 22,
    23, 22, 21
  ]), 1));
  
  bg.clearGroups();
  // start, count, material index
  bg.addGroup(0, 6, 0);
  bg.addGroup(6, 6, 1);
  bg.addGroup(12, 6, 2);
  bg.addGroup(18, 6, 3);
  bg.addGroup(24, 6, 4);
  bg.addGroup(30, 6, 5);
  
  var materials = [
    new THREE.MeshLambertMaterial({color:"red"}),
    new THREE.MeshLambertMaterial({color:"green"}),
    new THREE.MeshLambertMaterial({color:"blue"}),
    new THREE.MeshLambertMaterial({color:"cyan"}),
    new THREE.MeshLambertMaterial({color:"magenta"}),
    new THREE.MeshLambertMaterial({color:"yellow"})
  ];
  
  mesh = new THREE.Mesh(bg, materials);
  mesh.scale.set(5, 5, 5);
  
  scene.add(mesh);
}

function init() {
  document.body.style.backgroundColor = "slateGray";

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;

  document.body.appendChild(renderer.domElement);
  document.body.style.overflow = "hidden";
  document.body.style.margin = "0";
  document.body.style.padding = "0";

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
  camera.position.z = 50;
  scene.add(camera);

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.dynamicDampingFactor = 0.5;
  controls.rotateSpeed = 3;

  var light = new THREE.PointLight(0xffffff, 1, Infinity);
  camera.add(light);

  stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0';
  document.body.appendChild(stats.domElement);

  resize();
  window.onresize = resize;

  populateScene();

  animate();
}

function resize() {
  WIDTH = window.innerWidth;
  HEIGHT = window.innerHeight;
  if (renderer && camera && controls) {
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
    controls.handleResize();
  }
}

function render() {
  renderer.render(scene, camera);
}

function animate() {
  mesh.rotation.x += 0.015;
  mesh.rotation.y += 0.017;
  mesh.rotation.z += 0.019;
  requestAnimationFrame(animate);
  render();
  controls.update();
  stats.update();
}

function threeReady() {
  init();
}

(function() {
  function addScript(url, callback) {
    callback = callback || function() {};
    var script = document.createElement("script");
    script.addEventListener("load", callback);
    script.setAttribute("src", url);
    document.head.appendChild(script);
  }

  addScript("https://threejs.org/build/three.js", function() {
    addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
      addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
        threeReady();
      })
    })
  })
})();

修改:使用基础BoxBufferGeometry

添加第二个示例

根据pailhead对原帖的评论,这是一个使用未经修改的BoxBufferGeometry的片段。但正如他们在评论中提到的那样,你仍然需要知道哪个组对应哪个面。

var renderer, scene, camera, controls, stats, mesh;

var WIDTH = window.innerWidth,
  HEIGHT = window.innerHeight,
  FOV = 35,
  NEAR = 1,
  FAR = 1000;

function populateScene() {
  var bg = new THREE.BoxBufferGeometry(1, 1, 1);
  
  var materials = [
    new THREE.MeshLambertMaterial({color:"red"}),
    new THREE.MeshLambertMaterial({color:"green"}),
    new THREE.MeshLambertMaterial({color:"blue"}),
    new THREE.MeshLambertMaterial({color:"cyan"}),
    new THREE.MeshLambertMaterial({color:"magenta"}),
    new THREE.MeshLambertMaterial({color:"yellow"})
  ];
  
  mesh = new THREE.Mesh(bg, materials);
  mesh.scale.set(10, 10, 10);
  
  scene.add(mesh);
}

function init() {
  document.body.style.backgroundColor = "slateGray";

  renderer = new THREE.WebGLRenderer({
    antialias: true,
    alpha: true
  });
  renderer.shadowMap.enabled = true;
  renderer.shadowMap.type = THREE.PCFSoftShadowMap;

  document.body.appendChild(renderer.domElement);
  document.body.style.overflow = "hidden";
  document.body.style.margin = "0";
  document.body.style.padding = "0";

  scene = new THREE.Scene();

  camera = new THREE.PerspectiveCamera(FOV, WIDTH / HEIGHT, NEAR, FAR);
  camera.position.z = 50;
  scene.add(camera);

  controls = new THREE.TrackballControls(camera, renderer.domElement);
  controls.dynamicDampingFactor = 0.5;
  controls.rotateSpeed = 3;

  var light = new THREE.PointLight(0xffffff, 1, Infinity);
  camera.add(light);

  stats = new Stats();
  stats.domElement.style.position = 'absolute';
  stats.domElement.style.top = '0';
  document.body.appendChild(stats.domElement);

  resize();
  window.onresize = resize;

  populateScene();

  animate();
}

function resize() {
  WIDTH = window.innerWidth;
  HEIGHT = window.innerHeight;
  if (renderer && camera && controls) {
    renderer.setSize(WIDTH, HEIGHT);
    camera.aspect = WIDTH / HEIGHT;
    camera.updateProjectionMatrix();
    controls.handleResize();
  }
}

function render() {
  renderer.render(scene, camera);
}

function animate() {
  mesh.rotation.x += 0.015;
  mesh.rotation.y += 0.017;
  mesh.rotation.z += 0.019;
  requestAnimationFrame(animate);
  render();
  controls.update();
  stats.update();
}

function threeReady() {
  init();
}

(function() {
  function addScript(url, callback) {
    callback = callback || function() {};
    var script = document.createElement("script");
    script.addEventListener("load", callback);
    script.setAttribute("src", url);
    document.head.appendChild(script);
  }

  addScript("https://threejs.org/build/three.js", function() {
    addScript("https://threejs.org/examples/js/controls/TrackballControls.js", function() {
      addScript("https://threejs.org/examples/js/libs/stats.min.js", function() {
        threeReady();
      })
    })
  })
})();

答案 1 :(得分:0)

使用组将几何图形分成6个面,要绘制简单的立方体,您还可以使用简单的自定义ShaderMaterial

将几何图形分成6组需要更多的绘制调用,而不是使用1个绘制调用来绘制立方体,而是使用6个,每个面一个。

使用ShaderMaterial仅需要进行一次绘画调用:

顶点着色器:

attribute vec3 vertexColor;
varying vec3 vColor;

void main() {
  vColor = vertexColor;
  gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.);
}

片段着色器:

varying vec3 vColor;

void main() {
  gl_FragColor = vec4(vColor, 1.);
}

这样,您还可以使用GLSL颜色混合来合并不同的颜色。

自定义ShaderMaterial仅设置顶点和片段着色器源字符串:

const ColorCubeShader = function () {
  THREE.ShaderMaterial.call(this, {
    vertexShader: vertexShaderSrc,
    fragmentShader: fragmentShaderSrc
  })
}

ColorCubeShader.prototype = Object.create(THREE.ShaderMaterial.prototype)
ColorCubeShader.prototype.constructor = ColorCubeShader

Color Cube自定义Mesh

/**
 * Convenience method for coloring a face
 * @param {Number} r 
 * @param {Number} g 
 * @param {Number} b 
 * @returns {Array}
 */
const buildVertexColorArrayFace = function (r, g, b) {
  return [
    r, g, b,
    r, g, b,
    r, g, b,
    r, g, b
  ]
}

const ColorCube = function (size) {
  const geometry = new THREE.BoxBufferGeometry(size, size, size)

  // build color array
  let colorArray = []
  colorArray = colorArray
  .concat(buildVertexColorArrayFace(1, 0, 0))
  .concat(buildVertexColorArrayFace(0, 1, 0))
  .concat(buildVertexColorArrayFace(0, 0, 1))
  .concat(buildVertexColorArrayFace(1, 0, 1))
  .concat(buildVertexColorArrayFace(1, 1, 0))
  .concat(buildVertexColorArrayFace(0, 1, 1))

  // create a buffer attribute for the colors (for attribute vec3 vertexColor)
  const colorAttribute = new THREE.Float32BufferAttribute(
    new Float32Array(colorArray), 3)

  // set attribute vertexColor in vertex shader
  geometry.setAttribute('vertexColor', colorAttribute)

  // custom Shader Material instance
  const material = new ColorCubeShader()

  THREE.Mesh.call(this, geometry, material)
}

ColorCube.prototype = Object.create(THREE.Mesh.prototype)
ColorCube.prototype.constructor = ColorCube

使用它:

const cube = new ColorCube(1)
cube.position.set(0, 2, -2)
scene.add(cube)