简体   繁体   中英

How to change face color in Three.js

I am attempting to change the color on a single face of a mesh. This is in a WebGL context. I can change the entire mesh color, just not a single Face. Relevant code below:

// Updated Per Lee!

var camera = _this.camera;      
var projector = new THREE.Projector();
var vector = new THREE.Vector3( ( event.clientX / window.innerWidth ) * 2 - 1,                                  - ( event.clientY / window.innerHeight ) * 2 + 1, 0.5 );
projector.unprojectVector( vector, camera );

var ray = new THREE.Ray( camera.position, vector.subSelf( camera.position ).normalize() );
var intersects = ray.intersectObjects( kage.scene.children );

if ( intersects.length > 0 ) {
    face = intersects[0].face;
    var faceIndices = ['a', 'b', 'c', 'd'];         
    var numberOfSides = ( face instanceof THREE.Face3 ) ? 3 : 4;
    // assign color to each vertex of current face
    for( var j = 0; j < numberOfSides; j++ )  {
        var vertexIndex = face[ faceIndices[ j ] ];
    // initialize color variable
    var color = new THREE.Color( 0xffffff );
    color.setRGB( Math.random(), 0, 0 );
    face.vertexColors[ j ] = color;
    }
}

I also initialize the object, in this case a Cube as follows:

// this material causes a mesh to use colors assigned to vertices
var material = new THREE.MeshBasicMaterial( { 
    color: 0xf0f0f0, 
    shading: THREE.FlatShading,
    vertexColors: THREE.VertexColors 
});

var directionalLight = new THREE.DirectionalLight(0xEEEEEE);
directionalLight.position.set(10, 1, 1).normalize();
kage.scene.add(directionalLight);

var cube = new THREE.Mesh(new THREE.CubeGeometry(300, 300, 300,1,1,1), material);
cube.dynamic = true;
kage.scene.add(cube);

Changing the material makes the cube white regardless of the light color. The intersection logic still works, meaning i select the correct face, but alas the color does not change.

I'm new to Stackoverflow [well asking a question that is, so hopefully my edits are not confusing]

  • Update library to r53.
  • Add vertexColors: THREE.FaceColors in material.
  • And finally use face.color.setRGB( Math.random(), Math.random(), Math.random()) .

    Now no need to traverse loop for 4 sides (a,b,c,d) for THREE.Face4 or 3 sides (a,b,c) for THREE.Face3 .

    This works in both WebGL and Canvas rendering.

Example

three.js r53

Assuming that "myGeometry" is the geometry containing the face that you would like to change the color of, and "faceIndex" is the index of the particular face that you want to change the color of.

// the face's indices are labeled with these characters 
var faceIndices = ['a', 'b', 'c', 'd'];  

var face = myGeometry.faces[ faceIndex ];   

// determine if face is a tri or a quad
var numberOfSides = ( face instanceof THREE.Face3 ) ? 3 : 4;

// assign color to each vertex of current face
for( var j = 0; j < numberOfSides; j++ )  
{
    var vertexIndex = face[ faceIndices[ j ] ];
    // initialize color variable
    var color = new THREE.Color( 0xffffff );
    color.setRGB( Math.random(), 0, 0 );
    face.vertexColors[ j ] = color;
}

Then, the mesh needs to use the following material so that face colors are derived from the vertices:

// this material causes a mesh to use colors assigned to vertices
var cubeMaterial = new THREE.MeshBasicMaterial( 
    { color: 0xffffff, shading: THREE.FlatShading, 
    vertexColors: THREE.VertexColors } );

I'm rather new to three.js, but most of these examples seem overly long and complicated. The following code seems to color all 12 triangular faces for a cube... (WebGLRenderer r73).

One thing I noted when doing this is that the order of the faces is a little strange (to me as a novice at least).

var geometry = new THREE.BoxGeometry( 1, 1, 1 );
console.log(geometry.faces.length); // 12 triangles
geometry.faces[0].color = new THREE.Color(0x000000); //Right 1
geometry.faces[1].color = new THREE.Color(0xFF0000); //Right 2
geometry.faces[2].color = new THREE.Color(0xFF8C08); //Left 1
geometry.faces[3].color = new THREE.Color(0xFFF702); //Left 2
geometry.faces[4].color = new THREE.Color(0x00FF00); //Top 1
geometry.faces[5].color = new THREE.Color(0x0000FF); //Top 2
geometry.faces[6].color = new THREE.Color(0x6F00FF); //Bottom 1
geometry.faces[7].color = new THREE.Color(0x530070); //Bottom 2
geometry.faces[8].color = new THREE.Color(0x3F3F3F); //Front 1
geometry.faces[9].color = new THREE.Color(0x6C6C6C); //Front 2
geometry.faces[10].color = new THREE.Color(0xA7A7A7);//Rear 1
geometry.faces[11].color = new THREE.Color(0xFFFFFF);//Rear 2

As per Three JS Geometry Document, To signal an update in this faces, Geometry.elementsNeedUpdate needs to be set to true.

The following snippet changes the colors of all faces.

mesh.material.vertexColors = THREE.FaceColors

var faces = mesh.geometry.faces;

for(var i = 0 ; i < faces.length; i++){
  var face = faces[i];
  var color = new THREE.Color("rgb(255, 0, 0)");
  face.color = color;
}

mesh.geometry.elementsNeedUpdate = true;

render();

I think the method listed above only works in the WebGLRenderer . If you're going for something that works in both the CanvasRenderer and WebGLRenderer it's a bit more complicated, but I suspect the end result is more efficient both in terms of memory use and performance.

After going through the THREE.jS Projector.js source, this is how I did it:

// create the face materials
var material_1 = new THREE.MeshLambertMaterial(
    {color : 0xff0000, shading: THREE.FlatShading, overdraw : true}
);
var material_2 = new THREE.MeshLambertMaterial(
    {color : 0x00ff00, shading: THREE.FlatShading, overdraw : true}
);

// create a geometry (any should do)
var geom = new THREE.CubeGeometry(1,1,1);

// add the materials directly to the geometry
geom.materials.push(material_1);
geom.materials.push(material_2);

// assign the material to individual faces (note you assign the index in 
// the geometry, not the material)
for( var i in geom.faces ) {
    var face = geom.faces[i];
    face.materialIndex = i%geom.materials.length;
}

// create a special material for your mesh that tells the renderer to use the 
// face materials
var material = new THREE.MeshFaceMaterial();
var mesh = new THREE.Mesh(geom, material);

This example is adapted from the working code I have, but I have to admit I haven't actually run this exact block of code and I don't have a great track record of getting everything right first go, but hopefully it will help anyone who's struggling

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM