How to color individual faces of a BoxGeometry.toNonIndexed() object using vertices

I saw this post about using vertices to change the face colors of a BoxGeometry object, but still am struggling to distinguish each face rather than coloring all 6 faces.

I currently have a Piece() class that creates a BoxGeometry object and applies the following to color its sides:

const piece = new THREE.BoxGeometry(1,1,1).toNonIndexed();
const material = new THREE.MeshBasicMaterial( { vertexColors: true } );
const positionAttribute = piece.getAttribute( 'position' );
const colors = []; 
const colorRed = new THREE.Color(0xff0000);
const colorWhite = new THREE.Color(0xffffff);
if (color == 'red') {
  // set all sides of one cube red
  // TODO set color depending on face type
  for ( let i = 0; i < positionAttribute.count; i += 3) {
     // define the same color for each vertex of a triangle
     colors.push( colorRed.r, colorRed.g, colorRed.b );
     colors.push( colorRed.r, colorRed.g, colorRed.b );
     colors.push( colorRed.r, colorRed.g, colorRed.b );
   } // for

  // define the new attribute
  piece.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
} // if

if (color == 'white') {
  // TODO set color depending on face type 
  for ( let i = 0; i < positionAttribute.count; i += 3) {
    // define the same color for each vertex of a triangle
    colors.push( colorWhite.r, colorWhite.g, colorWhite.b );
    colors.push( colorWhite.r, colorWhite.g, colorWhite.b );
    colors.push( colorWhite.r, colorWhite.g, colorWhite.b );
  } // for

  // define the new attribute
  piece.setAttribute( 'color', new THREE.Float32BufferAttribute( colors, 3 ) );
} // if
const cube = new THREE.Mesh(piece, material);

Any help is appreciated.

Try it like so: Edit fiddle - JSFiddle - Code Playground

Each side of your cube consists of two triangles. So the idea is to process six vertices per iteration in order to create a single (random) color per side.

1 Like

Thank you! My code is similar to that but still am not able to just iterate for one side. When I set my for loop to i < 6 instead of positionAttribute.count, I get a lot of WegGL errors.

What is the positionAttribute.count used for?

It indicates how many vertices are defined in the position buffer attribute. The count property is primarily used when iterating over buffer attributes.

So does that mean that the for loop should iterate 36 times since there are 36 vertices for one BoxGeometry object?

That depends. If you want to process each vertex independently then yes. However, sometimes you want to process all vertices of a triangle. In this case, you increment the loop variable by 3. In the above fiddle we use 6 since we want to process two triangles per iteration.

2 Likes

Adding the code from JSFiddle below for reference:

let camera, scene, renderer, cube;

init();

function init() {

  camera = new THREE.PerspectiveCamera(70, window.innerWidth / window.innerHeight, 0.01, 10);
  camera.position.z = 4;

  scene = new THREE.Scene();

  const piece = new THREE.BoxGeometry(1, 1, 1).toNonIndexed();
  const material = new THREE.MeshBasicMaterial({
    vertexColors: true
  });
  const positionAttribute = piece.getAttribute('position');
  const colors = [];

  const color = new THREE.Color();

  for (let i = 0; i < positionAttribute.count; i += 6) {

    color.setHex(0xffffff * Math.random());

    colors.push(color.r, color.g, color.b);
    colors.push(color.r, color.g, color.b);
    colors.push(color.r, color.g, color.b);

    colors.push(color.r, color.g, color.b);
    colors.push(color.r, color.g, color.b);
    colors.push(color.r, color.g, color.b);
  } // for

  // define the new attribute
  piece.setAttribute('color', new THREE.Float32BufferAttribute(colors, 3));

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

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

}

function animation(time) {

  cube.rotation.x = time / 2000;
  cube.rotation.y = time / 1000;

  renderer.render(scene, camera);

}