3

I am trying to figure out how to use two different textures on the front and back of a box. Whenever I scale my box (ExtrudeGeometry), the UV maps do not seem to update. Therefore I am defining my own UV maps for the front and back of the box.

To define the front UV map I use:

geometry.faceVertexUvs[0]

which works accordingly.
For the back UV map I use:

geometry.faceVertexUvs[1];

However I am not able to access this 'second' layer UV map.

So my question is:

  1. Is it possible to update the UV maps accordingly to the scale of the object?
  2. Is it possible to access a 'second' layer UV map within a material?

I created an example here: jsfiddle.

I created three different boxes, with 3 different scales. From left to right: 0.01 x 2.97 x 2.1, 0.01 x 1 x 1 and 0.01 x 0.297 x 0.21. On the most left box, the textures are only covering a small portion of the box. The middle box has correct texturing. The right box has the updated uv map (otherwise only a small portion of the texture would show up).
(I am required to use the small scale on the box!)

I hope someone can help me out!

Three.js 84

Yannickd
  • 55
  • 1
  • 6

2 Answers2

2

You can define attribute vec2 uv2 in your vertex shader, and then access it as normal.

precision highp int;
precision highp float;

uniform mat4 modelViewMatrix;
uniform mat4 projectionMatrix;

attribute vec2 uv;
attribute vec2 uv2;
attribute vec3 normal;
attribute vec3 position;

void main() {
    gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
}
Will
  • 171
  • 1
  • 6
  • 1
    Thank you for your quick response! I haven't come around to actually implementing it yet. Once I do, I will accept the answer and provide my implementation. – Yannickd Feb 01 '17 at 09:25
  • @Yannickd Did this solution work for you? I'm also running into this issue but I'm trying to stay away from shader code as much as I can. – danyim Oct 31 '17 at 22:23
1

As an answer to @danyim.

In the end I also avoided using shader codes. I chose to completely uv-unwrap my object and draw my own texture with WebGL (as I only needed a simple front and back texture). In the function below I draw an object with a variable amount of subdivisions and unwrap these.

With the function drawTexture (further below) I provide an 'O' and a 'X' as textures for my front and rear of the object.

Hopefully this helps you (and possible others) out. If you have any further questions, do not hesitate to ask.

 drawObject() {
    //Draw a shape with the measures of a object.
    var length = 0.01, width = 0.297;
    var shape = new THREE.Shape();
    shape.moveTo(0, 0);
    shape.lineTo(0, width);
    shape.lineTo(length, width);
    shape.lineTo(length, 0);
    shape.lineTo(0, 0);

    var canvasTexture = drawTexture('x', 'o');
    var textures = [
        new THREE.MeshPhongMaterial({ color: 0xf9f9f9, side: THREE.BackSide, overdraw: 0.5 }),//GENERAL 0
        new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.BackSide, overdraw: 0.5 }), //FRONT 1
        new THREE.MeshPhongMaterial({ map: canvasTexture, side: THREE.BackSide, overdraw: 0.5 }), //BACK 2
        new THREE.MeshPhongMaterial({ color: 0x00ff00, side: THREE.BackSide, overdraw: 0.5 })
    ];

    var material = new THREE.MultiMaterial(textures);

    subDivs = parseInt(objectLength / 40); //Amount of subdivision (more provides a smoother result)
    subDivs = 5;
    var extrudeSettings = {
        amount: 0.21,
        steps: this.subDivs,
        bevelEnabled: false
    };
    //Create a UVMap(u,v). 
    var uvMap = [];
    for (let i = 0; i <= (subDivs) * 2; i++) {
        var u = i / (subDivs * 2);
        uvMap.push(new THREE.Vector2(u, 0));
        uvMap.push(new THREE.Vector2(u, 1));
    }

    //Create the object.
    var geometry = new THREE.ExtrudeGeometry(shape, extrudeSettings);
    var tempI = (uvMap.length - 2) / 2;
    //Map the vertices to the UVMap (only mapping top and bottom of the object (for 'x' and 'o' texture))
    for (let i = 0; i < geometry.faceVertexUvs[0].length; i++) {
        if (i >= 4 && i < 4 + (subDivs * 2)) {
            if (isOdd(i)) {
                geometry.faceVertexUvs[0][i] = [uvMap[i - 4], uvMap[i - 2], uvMap[i - 3]];
            } else {
                geometry.faceVertexUvs[0][i] = [uvMap[i - 4], uvMap[i - 3], uvMap[i - 2]];
            }
        }
        else if (i >= 4 + (subDivs * 4) && i < 4 + (subDivs * 4) + (subDivs * 2)) {
            if (isOdd(i)) {
                geometry.faceVertexUvs[0][i] = [uvMap[tempI], uvMap[tempI + 2], uvMap[tempI + 1]];
            } else {
                geometry.faceVertexUvs[0][i] = [uvMap[tempI], uvMap[tempI + 1], uvMap[tempI + 2]];
            }
            tempI++;
        }
    }

    //Assigning different materialIndices to different faces
    for (var i = 4; i <= 13; i++) { //Front
        geometry.faces[i].materialIndex = 1;
    }

    for (var i = 4 + (subDivs * 4); i < 4 + (subDivs * 4) + (subDivs * 2); i++) { //Back
        geometry.faces[i].materialIndex = 2;
    }

    for (var i = 0; i <= 1; i++) { 
        geometry.faces[i].materialIndex = 3;
    }
    var plane = new THREE.Mesh(geometry, material);
    return plane;

function drawTexture (msg1, msg2) {
    var canvas = document.createElement('canvas'); //Create a canvas element.
    var size = 128;
    canvas.width = size;
    canvas.height = size;

    var ctx = canvas.getContext('2d');
    //Draw a white background
    ctx.beginPath();
    ctx.rect(0, 0, size, size);
    ctx.fillStyle = 'white';
    ctx.fill();

    //Draw the message (e.g. 'x' or 'o')
    ctx.fillStyle = 'black';
    ctx.font = '64px Arial';

    //Determien the size of the letters.
    var metrics1 = ctx.measureText(msg1);
    var textWidth1 = metrics1.width;
    var textHeight = parseInt(ctx.font);

    var metrics2 = ctx.measureText(msg2);
    var textWidth2 = metrics2.width;

    ctx.fillText(msg1, size / 4 - textWidth1 / 2, size / 2 + (textHeight / 4));
    ctx.fillText(msg2, (3 * size / 4) - textWidth2 / 2, size / 2 + (textHeight / 4));

    //Store the canvas in a THREE.js texture.
    var texture = new THREE.Texture(canvas);
    texture.needsUpdate = true;
    return texture; //Return Three.Texture
Yannickd
  • 55
  • 1
  • 6