Creating UV coordinates in THREE.js

I've been working on bringing a model into a scene using the OBJ loader in THREE.js.

Initially, I had no issues importing the geometry and visualizing it with MeshNormalMaterial. However, as soon as I tried to use textures that require UV coordinates, I encountered this error:

[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1 

The reason for this error is that the OBJ file lacks UV coordinates. I'm now exploring whether there's a way to automatically generate these texture coordinates instead of manually assigning them. I attempted methods like:

material.needsUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.buffersNeedUpdate = true;

...but unfortunately, none of these worked.

Are there any functions within three.js that can help auto-generate UV textures, or do I need to handle the coordinate assignment myself?

Answer №1

Unfortunately, there is no automated method available for calculating UV coordinates.

The process of determining UV values for a flat surface can be done manually. A helpful resource explaining this method can be found here: calculating texture coordinates

However, when dealing with more intricate shapes, it becomes more challenging. One approach could involve identifying planar surfaces within the shape.

UPDATE

Below is an example code snippet for establishing UV coordinates on a planar surface represented by points (x, y, z) where z equals 0:

geometry.computeBoundingBox();

var max = geometry.boundingBox.max,
    min = geometry.boundingBox.min;
var offset = new THREE.Vector2(0 - min.x, 0 - min.y);
var range = new THREE.Vector2(max.x - min.x, max.y - min.y);
var faces = geometry.faces;

geometry.faceVertexUvs[0] = [];

for (var i = 0; i < faces.length ; i++) {

    var v1 = geometry.vertices[faces[i].a], 
        v2 = geometry.vertices[faces[i].b], 
        v3 = geometry.vertices[faces[i].c];

    geometry.faceVertexUvs[0].push([
        new THREE.Vector2((v1.x + offset.x)/range.x ,(v1.y + offset.y)/range.y),
        new THREE.Vector2((v2.x + offset.x)/range.x ,(v2.y + offset.y)/range.y),
        new THREE.Vector2((v3.x + offset.x)/range.x ,(v3.y + offset.y)/range.y)
    ]);
}
geometry.uvsNeedUpdate = true;

Answer №2

The responses provided by others were quite helpful, but did not completely align with my specific needs for applying a recurring patterned texture to all sides of a shape primarily consisting of flat surfaces. One challenge encountered was the occurrence of distorted textures on vertical surfaces due to solely utilizing the x and y components as u and v.

In my approach detailed below, I leverage surface normals to determine which two components (x, y, and z) should be allocated to u and v. Although this method is fairly rudimentary, it functions effectively.

function assignUVs(geometry) {

    geometry.faceVertexUvs[0] = [];

    geometry.faces.forEach(function(face) {

        var components = ['x', 'y', 'z'].sort(function(a, b) {
            return Math.abs(face.normal[a]) > Math.abs(face.normal[b]);
        });

        var v1 = geometry.vertices[face.a];
        var v2 = geometry.vertices[face.b];
        var v3 = geometry.vertices[face.c];

        geometry.faceVertexUvs[0].push([
            new THREE.Vector2(v1[components[0]], v1[components[1]]),
            new THREE.Vector2(v2[components[0]], v2[components[1]]),
            new THREE.Vector2(v3[components[0]], v3[components[1]])
        ]);

    });

    geometry.uvsNeedUpdate = true;
}

This function does not standardize the UVs based on the object's size. However, it proves more effective when incorporating the same texture on objects of varying dimensions within the scene. Depending on the scale of your world coordinate system, adjustments to scaling and repetition of the texture may be required:

texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;

Answer №3

Understanding box UV mapping is crucial when working with three.js configurators for various applications. If you want to see it in action, check out this example.

This solution can handle both indexed and non-indexed buffer geometries on a per-face basis.

https://i.sstatic.net/iTi57.png

For reference, here's how you can utilize this technique:

// Create a mesh
var bufferGeometry = new THREE.BufferGeometry().fromGeometry(new THREE.DodecahedronGeometry(2.5, 0));
let material = new THREE.MeshPhongMaterial({
    color: 0x10f0f0,
    map: new THREE.TextureLoader().load('http://mbnsay.com/rayys/images/1K_UV_checker.jpg')
});

// Determine dimensions for optimal texture sizing without distortion
bufferGeometry.computeBoundingBox();
let bboxSize = bufferGeometry.boundingBox.getSize();
let uvMapSize = Math.min(bboxSize.x, bboxSize.y, bboxSize.z);

// Calculate UV coordinates (will add attribute if not present)
applyBoxUV(bufferGeometry, new THREE.Matrix4().getInverse(cube.matrix), uvMapSize);

// Notify three.js about the update
bufferGeometry.attributes.uv.needsUpdate = true;

The code above relies on the implementation of applyBoxUV shown below:

function _applyBoxUV(geom, transformMatrix, bbox, bbox_max_size) {
    // Implementation logic here 
}

function applyBoxUV(bufferGeometry, transformMatrix, boxSize) {
    // Implementation logic here 
}

Answer №4

I found the solutions provided here to be incredibly helpful. There is one key point that stood out to me: when updating vertices, it's important to set uvs rather than re-assign them within the scope of my geometry:

scope.updateUVs = (copy=true) => {

    scope.computeBoundingBox();

    var max     = scope.boundingBox.max;
    var min     = scope.boundingBox.min;

    var offset  = new THREE.Vector2(0 - min.x, 0 - min.y);
    var range   = new THREE.Vector2(max.x - min.x, max.y - min.y);

    if (!copy) {
        scope.faceVertexUvs[0] = [];
    }
    var faces = scope.faces;

    for (i = 0; i < scope.faces.length ; i++) {

      var v1 = scope.vertices[faces[i].a];
      var v2 = scope.vertices[faces[i].b];
      var v3 = scope.vertices[faces[i].c];

      var uv0 = new THREE.Vector2( ( v1.x + offset.x ) / range.x , ( v1.y + offset.y ) / range.y );
      var uv1 = new THREE.Vector2( ( v2.x + offset.x ) / range.x , ( v2.y + offset.y ) / range.y );
      var uv2 = new THREE.Vector2( ( v3.x + offset.x ) / range.x , ( v3.y + offset.y ) / range.y );

      if (copy) {
          var uvs =scope.faceVertexUvs[0][i];
          uvs[0].copy(uv0);
          uvs[1].copy(uv1);
          uvs[2].copy(uv2);
      } else {
          scope.faceVertexUvs[0].push([uv0, uv1, uv2]);
      }
    }

    scope.uvsNeedUpdate = true;

}

Answer №5

This code snippet provides a general solution for spherical mapping using yaw and pitch coordinates. Take a look at the example here (specifically the loadSuzanne function):

function assignUVs(geometry) {

    geometry.faceVertexUvs[0] = [];

    geometry.faces.forEach(function(face) {

        var uvs = [];
        var ids = [ 'a', 'b', 'c'];
        for( var i = 0; i < ids.length; i++ ) {
            var vertex = geometry.vertices[ face[ ids[ i ] ] ].clone();

            var n = vertex.normalize();
            var yaw = .5 - Math.atan( n.z, - n.x ) / ( 2.0 * Math.PI );
            var pitch = .5 - Math.asin( n.y ) / Math.PI;

            var u = yaw,
                v = pitch;
            uvs.push( new THREE.Vector2( u, v ) );
        }
        geometry.faceVertexUvs[ 0 ].push( uvs );
    });

    geometry.uvsNeedUpdate = true;
}

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

How to emphasize a clicked hyperlink in AngularJS

Here's the HTML code I've been working on: <div id="Navigation" class="md-dialog-full"> <div class="products-options"> <a ng-click='addType("Physical")' href="javascript:void(0);"> < ...

Comparing Nodes Armed with Clusters to Nodes Sporting Threads

Can you explain the key distinctions between node cluster and Threads agogo, including their respective advantages and disadvantages? From my understanding, Threads agogo creates a background thread while node cluster initiates a new process to run in th ...

What might be causing the issue of a click handler not registering during the initial page load when using Enquire.js

I am experimenting with different effects on various breakpoints. My main goal is to achieve the following behavior: when a category is clicked from the list within 720px, the category list should fade out while the data is revealed in its place. However, ...

Rejuvenating a template generated on the server in AngularJS

I have a specific partial which is a report - simply a static list of names and dates designed for viewing and printing purposes. For efficiency reasons, the report is rendered server-side, so when a report request is made, my API responds with HTML rathe ...

Creating an application for inputting data by utilizing angular material and javascript

I am looking to create an application using Angular Material Design, AngularJS (in HTML), and JavaScript. The application should take input such as name, place, phone number, and email, and once submitted, it should be stored in a table below. You can fin ...

Adding HTML content inside an iFrame at a specific cursor position in Internet Explorer

Does anyone know a method to insert HTML into an iFrame specifically for IE? I have been using execCommand insertHtml in Chrome and Firefox, but it doesn't seem to work in IE. I was able to paste HTML into a content editable div using pasteHTML, howe ...

What is the best way to structure a hierarchy in an array or object data using Node.js?

I have the following data arrangement: [{ Country: 'USA', State: 'CA', City: 'LA', District: 'LA-1',Town: 'LA-1,LA-1a,LA_AZ_A',Area: 'Area 51'}, { Country: 'USA', State: 'CA&ap ...

Creating a dynamic start page for Progressive Web Apps (PWA) involves determining the

Recently, I developed a project called "Progressive Web App" using JavaScript and Visual Studio 2017. One key element of the project is the file "package.appxmanifest", which defines the StartPage. I am curious to know if there is a way to dynamically se ...

Determine if a specific date occurred at least one day ago using momentjs

Is there a way to determine if a specific date is at least one day (24 hours) in the past using momentjs? For example: const currentDate = moment() const isAtLeastOneDayAgo = currentDate.subtract(dateToVerify) > 1 // How can this be done? ...

Efficiently managing classes with css modules and extractCSS in Nuxt 2

When using Nuxt 2 with CSS modules, I encountered an issue where multiple components resulted in multiple CSS files having the same class. Hello.vue <template> <div :class="$style.title">Hello, World</div> <div :class=&q ...

Upcoming verification with JSON Web Token

I am looking to incorporate JWT auth into my Next app. Currently, I have mapped out the flow as such: User enters email and password to log in Server responds with status 200 and a jwt access token in httpOnly cookies My main dilemma lies in deciding on ...

Live Search: Find Your Answers with Ajax

I've come across this issue multiple times, but haven't found a solution that fits my specific requirements. I've encountered several URLs with links like example.com/ajax/search?query=Thing I'm currently working on a header and using ...

Decoding a changeable JSON structure

As I am working on parsing a JSON and looking for a specific key within the JSON object, I am facing an issue due to the changing structure of the JSON. It is not feasible to hard code the path for searching. Are there any alternative methods to parse this ...

How can I save a PDF file using React?

My webpages contain several tables, and I am looking to add a dropdown feature that allows users to choose between saving the table in either PDF or Excel format. These tables are dynamically generated with the header structured in array form and data sto ...

How to set up objects with accessors and mutators in ES5 JavaScript?

creating a new object, obj1, with an enumerable property of value 42 > changing the value of obj1.property to 56 > output: 56 > accessing obj1.property returns: 42 When using use strict, an error occurs. To merge multiple objects: using jQuery ...

Is there a method for determining the distance between two points within a cluster?

I need to calculate the distance between two points within a cluster. The clusters were created using the code snippet below: X = np.arange(0,10.2,0.16) Y = np.arange(0,10.2,0.16) A = np.arange(0,2, 0.005) x, y = np.meshgrid(X, Y) z = np.zeros(x.shape) x ...

Step-by-step guide on sorting WordPress posts by a custom field date

I've created an event sidebar section that will only show the next 3 upcoming events. I have successfully set up the custom post type and custom fields, but I am struggling to figure out how to order the posts based on the start date of the events, wh ...

Exploring Particles with three.js

Could you please help me understand why there are no particles visible in this code snippet? I followed a tutorial and it all seems correct. (function() { var camera, scene, renderer; init(); animate(); function init() { scene = new THREE.Scene(); ...

Should I include JSX or JS when exporting ReactJS components as node modules to npm?

I've been busy developing React.js components and sharing them as modules on npm. My approach involves utilizing a gulp task to convert all jsx components into js, leveraging gulp-react: var react = require('gulp-react'); gulp.task(' ...

Utilizing Angular 2's *ngFor to conditionally wrap elements can be compared to organizing a layout with three columns in a Bootstrap row, then starting a

Currently I am facing a challenge with using *ngFor and it has me puzzled. My goal is to incorporate UIkit, but the same concept would apply to Bootstrap as well. <div *ngFor="let device of devices" > <div class="uk-child-width-expand@s uk-te ...