Three.js: Set different materials on cube's different side?

Created on 15 Feb 2012  路  8Comments  路  Source: mrdoob/three.js

In chrome console it says that material as an array has been deprecated.
I want to apply different material to cube's different side.

//convert from SVG
var imageCanvas = document.createElement("canvas");
canvg(imageCanvas, image.svg);

var texture = new THREE.Texture(imageCanvas);
texture.needsUpdate = true;

var geometry = new THREE.CubeGeometry(200, 10, 200);
var cubeMaterial = new THREE.MeshBasicMaterial({
    map : texture
});

var cube = new THREE.Mesh(geometry, cubeMaterial);

Like this, all 6 cube's sides have that picture as material. I would like to apply that picture material to only 2 sides an on the rest 4 I would like to put a color.
How can I do that?

And also, is there any other place between this issues list, API Docs and those examples here on GitHub where I can lear Three.js?

Question

Most helpful comment

Hi Dino4674,

This issue / solution might be what you are looking for: #744

This worked for me:

var materials = [
    leftSide,        // Left side
    rightSide,       // Right side
    topSide,         // Top side
    bottomSide,      // Bottom side
    frontSide,       // Front side
    backSide         // Back side
];
var geometry = new THREE.CubeGeometry(100, 75, 8, 1, 1, 1, materials);
var someMesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial());

All 8 comments

Hi Dino4674,

This issue / solution might be what you are looking for: #744

This worked for me:

var materials = [
    leftSide,        // Left side
    rightSide,       // Right side
    topSide,         // Top side
    bottomSide,      // Bottom side
    frontSide,       // Front side
    backSide         // Back side
];
var geometry = new THREE.CubeGeometry(100, 75, 8, 1, 1, 1, materials);
var someMesh = new THREE.Mesh(geometry, new THREE.MeshFaceMaterial());

http://learningwebgl.com/blog/

Also note that there are two APIs.

The old one is no longer being maintained:

https://github.com/mrdoob/three.js/wiki/API-Reference

This is the new one:

http://mrdoob.github.com/three.js/docs/latest/

src/core/Face3.js, src/core/Face4.js

You can see in these two files that every face (3-sided or 4-sided) has a materialIndex.

src/core/Projector.js

Line 175:
geometryMaterials = object.geometry.materials;

Line 308:
_face.faceMaterial = face.materialIndex !== null ? geometryMaterials[ face.materialIndex ] : null;

Showing how materialIndex is used to map the array of materials in geometry.

src/extras/geometries/CubeGeometry.js

Lines 17 - 33:

    if ( materials !== undefined ) {

        if ( materials instanceof Array ) {

            this.materials = materials;

        } else {

            this.materials = [];

            for ( var i = 0; i < 6; i ++ ) {

                this.materials.push( materials );

            }

        }

When we create a new THREE.CubeGeometry, the materials argument we provide can be an array of materials, or a single material, which is then turned into an array with six elements, each of which contains the (same) material provided.

Line 35:

        mpx = 0; mnx = 1; mpy = 2; mny = 3; mpz = 4; mnz = 5;

Line 59 - 66:

    this.sides.px && buildPlane( 'z', 'y', - 1, - 1, depth, height, width_half, mpx ); // px
    this.sides.nx && buildPlane( 'z', 'y',   1, - 1, depth, height, - width_half, mnx ); // nx
    this.sides.py && buildPlane( 'x', 'z',   1,   1, width, depth, height_half, mpy ); // py
    this.sides.ny && buildPlane( 'x', 'z',   1, - 1, width, depth, - height_half, mny ); // ny
    this.sides.pz && buildPlane( 'x', 'y',   1, - 1, width, height, depth_half, mpz ); // pz
    this.sides.nz && buildPlane( 'x', 'y', - 1, - 1, width, height, - depth_half, mnz ); // nz

    function buildPlane( u, v, udir, vdir, width, height, depth, material ) {

Line 123 - 126 (inside buildPlane function):

                var face = new THREE.Face4( a + offset, b + offset, c + offset, d + offset );
                face.normal.copy( normal );
                face.vertexNormals.push( normal.clone(), normal.clone(), normal.clone(), normal.clone() );
                face.materialIndex = material;

From the three snippets above, we can see that a THREE.CubeGeometry is made out of 6 faces, each with 4 sides (hence THREE.Face4). Each face is given a unique materialIndex, from 0 to 5.

Hmm, seems like maybe the last argument provided to buildPlane could be renamed from material to materialIndex.

src/renderers/WebGLRenderer.js

Lines 754 - 766:

    function getBufferMaterial( object, geometryGroup ) {

        if ( object.material && ! ( object.material instanceof THREE.MeshFaceMaterial ) ) {

            return object.material;

        } else if ( geometryGroup.materialIndex >= 0 ) {

            return object.geometry.materials[ geometryGroup.materialIndex ];

        }

    };

Lines 3617 - 3621:

            materialIndex = buffer.materialIndex;

            if ( materialIndex >= 0 ) {

                material = object.geometry.materials[ materialIndex ];

More examples of the association between materials & materialIndex.

Thank you mrienstra,

that did the trick.

And thank you for long posts :D I'm new to this library so I any help is good for me now :)

So I am trying to figure this:

var cube = new THREE.Mesh(new THREE.CubeGeometry(200, 200, 200, 1, 1, 1, materials), new THREE.MeshFaceMaterial());

If I defined CubeGeometry with it's materials and size, what role does MeshFaceMaterial play here?

I think you are wrong about indices of materials.
Here is my example.

Imagine picture wich has picture on both sides and rest 4 polygons are black.

var pictureMaterial = new THREE.MeshBasicMaterial({
    map : texture
});

var borderMaterial = new THREE.MeshBasicMaterial({
    color : 0x000000
});

var materials = [borderMaterial, // Left side
borderMaterial, // Right side
pictureMaterial, // Top side   ---> THIS IS THE FRONT
pictureMaterial, // Bottom side --> THIS IS THE BACK
borderMaterial, // Front side
borderMaterial  // Back side
];

Here I say that my picture is x = 200 wide, y = 10 depth and z = 200 tall.

var cube = new THREE.Mesh(new THREE.CubeGeometry(200, 10, 200, 1, 1, 1, materials), new THREE.MeshFaceMaterial());

Now this is based on a fact that Z-axis represents height which I think is more natural.

I'm still learning myself, the code sample I provided is something I wrote during a hack weekend when I first started playing with three.js. Maybe the demo I was using as a starting point was intended to be looking down on the scene from above, but I saw it as looking at the scene from the side? Who knows! I'll have to take a closer look at it later.

If it's incorrect, I'll try to update that code sample later so it doesn't confuse anyone else. :)

It looks like you were right after all.

I your say

cube.lookAt(camera.position)

you can see exactly which one is the front.

So the answer is that Three looks on Y as height and Z as depth.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

seep picture seep  路  3Comments

zsitro picture zsitro  路  3Comments

danieljack picture danieljack  路  3Comments

akshaysrin picture akshaysrin  路  3Comments

jlaquinte picture jlaquinte  路  3Comments