TROIS.js génère des coordonnées UV

je travaille sur l'importation d'un modèle dans une scène en utilisant les trois.js OBJ loader.

je sais que je suis capable d'importer la géométrie fine, parce que quand je lui attribue un Meshnormalmatérial, il apparaît grand. Cependant, si j'utilise quelque chose qui nécessite des coordonnées UV, cela me donne l'erreur:

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

je sais que c'est parce que L'OBJ chargé n'a pas de coordonnées UV, mais je me demandais s'il y avait un moyen de générer les coordonnées de texture nécessaires. J'ai essayé

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

...mais en vain.

y a-t-il un moyen de générer automatiquement des textures UV en utilisant trois.js, ou dois-je attribuer les coordonnées moi-même?

23
demandé sur Peter O. 2013-12-25 20:13:31

4 réponses

à ma connaissance, il n'existe aucun moyen automatique de calculer les UV.

vous devez vous calculer. Calculer un UV pour un avion est assez facile, ce site explique comment: calcul des coordonnées de texture

Pour une forme complexe, je ne sais pas comment. Peut-être que vous pourriez détecter la surface plane.

EDIT

Voici un exemple de code pour une surface plane (x, y, z)z = 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;
37
répondu Sayris 2016-06-16 07:27:34

les autres réponses ici ont été d'une grande aide mais n'ont pas tout à fait adapté mes exigences pour appliquer une texture de motif répétitif à tous les côtés d'une forme avec des surfaces presque plates. Le problème est que l'utilisation seulement des composants x et y comme u et v entraîne des textures étirées bizarres sur les surfaces verticales.

ma solution ci-dessous utilise des normales de surface pour choisir les deux composants (x, y et z) à associer à u et v. Elle est encore assez rudimentaire mais elle fonctionne assez bien.

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

C' fonction ne normalise pas les UVs à la taille de l'objet. Cela fonctionne mieux lorsqu'on applique la même texture à des objets de différentes tailles dans la même scène. Toutefois, selon la taille de votre système de coordonnées monde, vous aurez probablement besoin d'échelle et de répéter la texture ainsi:

texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
13
répondu Tamlyn 2014-12-05 14:20:29

Les réponses ici sont super et m'a beaucoup aidé. Une seule chose: si vous mettez à jour les vertices, ne réattribuez pas les UV, mais définissez-les, comme dans (scope est ma géométrie):

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;

}
3
répondu Jörg Viola 2016-04-11 10:04:10

Il s'agit d'une version générale qui fonctionne pour la cartographie sphérique (lacet, coordonnées pitch), voir Exemple ici, (regardez loadSuzanne fonction):

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;
}
2
répondu Martin Alcubierre Arenillas 2016-09-11 06:49:36