Utilizing Multiple UV maps in Three.js for Enhanced Object Texturing

I'm currently exploring how to apply two different textures on the front and back of a box. The issue I'm facing is that when I scale my box (using ExtrudeGeometry), the UV maps do not update as expected. As a result, I've resorted to defining my own UV maps for the front and back of the box.

For the front UV map, I use:

geometry.faceVertexUvs[0]

and it functions as intended.
To define the back UV map, I use:

geometry.faceVertexUvs[1];

However, I'm struggling to access this 'second' layer UV map.

So, my queries are:

  1. Is there a way to automatically update the UV maps based on the scale of the object?
  2. Can we access a 'second' layer UV map within a material?

I have provided an example here: jsfiddle.

In the example, you will see three boxes with varying scales - 0.01 x 2.97 x 2.1, 0.01 x 1 x 1, and 0.01 x 0.297 x 0.21. The leftmost box shows only a small portion of the texture due to scaling issues. The middle box displays correct texturing. The right box exhibits the updated uv map to show the entire texture properly.
(I must maintain the small-scale requirement on the box!)

Any assistance with this matter would be greatly appreciated!

Using Three.js version 84

Answer №1

To incorporate the attribute vec2 uv2 in your vertex shader, declare it and then access it as you would any other variable.

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);
}

Answer №2

Responding to @danyim.

Ultimately, I made the decision not to use shader codes. Instead, I opted to fully uv-unwrap my object and create my own texture using WebGL (for a simple front and back texture). In the function below, I demonstrate how to draw an object with varying subdivisions and unwrap them.

Further down in the code, you'll find the function drawTexture where I set 'O' and 'X' textures for the front and back of the object.

I hope this explanation proves helpful to you and potentially others as well. Feel free to ask if you have any more questions.

 drawObject() {
    //Draw a shape resembling an 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';

    //Determine 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

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Ways to stop the react-router from causing the page to refresh

Need assistance with React Router issue I'm working on a project in reactJs using react-router-dom v5. I have set up a default route for routing. <Redirect to={"someComponent"}/> The problem is that when I refresh the page, it auto ...

Is there a way to retrieve the quantity of children from an element using protractor?

I am currently working with Protractor and I need to determine the amount of child components associated with a specific element. The element in question belongs to a table category. let table = element(by.css('#myTable')); My objective now is ...

Pass back the jQuery deferred object from the error section of an Ajax request

I am facing a challenge with loading a file using jQuery Ajax. When the initial location fails, it should attempt to retrieve it from another path. Although the request redirects to the alternate URL, I am not receiving the necessary jQuery deferred from l ...

Determine which scroll bar is currently in use

I'm running into an issue with multiple scrollbars on my page - they just don't seem to be functioning correctly: <div class="dates-container" v-for="id in ids"> <overlay-scrollbars :ref="`datesHeader` ...

Error message received while converting webm video to images using Kagami/ffmpeg.js: Unable to locate an appropriate output format for '%04d.jpg'

These are the current versions: node v12.9.1, npm 6.10.2, [email protected] Repository: https://github.com/Kagami/ffmpeg.js The code in decode.js looks like this: const fs = require('fs'); const ffmpeg = require('ffmpeg.js'); c ...

Encountering a problem when making a HTTPS GET request to a REST API using

I am currently running an Angular application that utilizes an external API to fetch country ISOs. However, I am encountering an error with the API since it uses HTTPS. Interestingly, when I implement a proxy in my Angular local environment by mapping /is ...

Cannot adjust dimensions of Chart.js

Struggling to resize chart.js on an HTML page. Have attempted various methods like adjusting the canvas parameters, defining section and div tags, explicitly setting height and width in <style>, as well as using {responsive: true, maintainAspectRatio ...

Using the typeof operator to test a Typescript array being passed as an object

I have a puzzling query about this particular code snippet. It goes like this: export function parseSomething(someList: string[]): string[] { someList.forEach((someField: string) => { console.log(typeof someField) }) Despite passing a s ...

Customizing Echart tooltip appearance

I've integrated echart () into my Vue.js application, and I'm attempting to personalize the tooltip on a ring chart. However, I'm facing challenges in achieving this customization. My goal is to show my own JSON data in the tooltip when hove ...

UPDATE: Choosing several classes and then toggling the classes independently for each one

I have managed to make this work, but I am considering if there is a more efficient solution. My objective is to modify the divs using classes exclusively. I aim to toggle 4 classes with just one click. First, I obtain the class for the button and then a ...

Tips for managing React state when dealing with multiple axios callbacks that modify an array's state

I am currently working on a React component that allows users to upload images. In this scenario, I aim to upload 3 images. After each file is uploaded, I want to append it to the list of uploaded files. Issue: The setter method in useState is asynchronou ...

Please proceed by submitting all radio buttons that have been checked

Seeking assistance with creating a quiz application using the MEAN stack. Once all questions are attempted, there will be a submit button for checking all selected radio buttons - option 1 corresponds to 1, option 2 to 2, and so on. The current structure o ...

Chrome experiencing issues with jQuery AJAX, not related to cross-domain problems

I have a simple JS/jQuery code that makes an AJAX call to retrieve some HTML and insert it into a div on my webpage. While this process works smoothly in Firefox, it encounters a failure in Chrome. Upon checking the Chrome console, I can see the AJAX requ ...

The visibility of the Google +1 button is lost during the partial postback process in ASP.NET

When trying to implement the Google Plus One button using AddThis on one of our localized pages, we encountered a strange issue. Despite retrieving data from the backend (let's assume a database), the plus button was not loading during an AJAX based p ...

An unforeseen repetition of jQuery Ajax calls

I'm currently working on an application that utilizes ajax calls to load content. However, I've encountered a situation where an ajax call goes into a loop but seems to end randomly. Below is the code sequence starting from a double click event l ...

Creating a smooth fading effect for an element within a react component

I am currently working on implementing a fade out warning/error message (styled with Bootstrap) in a React component, however, I am encountering some challenges with the timing of the fade-out effect. Up to this point, the fade out effect is functioning c ...

Determining the optimal number of rows and columns based on an integer value

Here's a brain teaser for you: /** * Let's figure out the optimal number of rows and columns for your garden to be as square as possible, based on the number of seeds you have. * * @param {number} seedCount - The total number of seeds in you ...

Execute JavaScript function only if it is the final invocation

I'm currently experiencing an issue with a Javascript function that updates a bootstrap spinner. The function works well in general, but the problem arises when I click multiple times. I utilize ajax to update the quantity of selected products in the ...

Is there a way to monitor the movement of an animated object in three js?

for (let ai = 0, al = gltf.animations.length; ai < al; ai++ ) { for (let ti = 0, tl = gltf.animations[ai].tracks.length; ti < tl; ti++ ) { let parts = gltf.animations[ai].tracks[ti].name.split('.'); // name is 'uu ...

Display text automatically if the user fails to click on the image after a certain period

I'm working on a jQuery code but I'm not sure how to achieve this specific functionality. I want the text to show up automatically after a few seconds if the user doesn't click on the image. Do you think adding animation-delay in CSS would b ...