javascript - texture - three.js tutorial
THREE.js genera coordenadas UV (4)
Estoy trabajando en la importación de un modelo en una escena utilizando el cargador THREE THJ.
Sé que puedo importar bien la geometría, porque cuando le asigno un MeshNormalMaterial, se muestra genial. Sin embargo, si uso cualquier cosa que requiera coordenadas UV, me da el error:
[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1
Sé que esto se debe a que el OBJ cargado no tiene coordenadas UV, pero me preguntaba si había alguna forma de generar las coordenadas de textura necesarias. Yo he tratado
material.needsUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.buffersNeedUpdate = true;
...pero fue en vano.
¿Hay alguna forma de generar automágicamente texturas UV usando three.js, o tengo que asignar las coordenadas yo mismo?
Esta es una versión general que funciona para mapeo esférico (guiñada, coordenadas de tono), ver ejemplo here (mire la función loadSuzanne ):
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;
}
Las otras respuestas aquí fueron de gran ayuda, pero no se ajustaban a mis requisitos para aplicar una textura de patrón repetitiva a todos los lados de una forma con superficies principalmente planas. El problema es que usar solo los componentes xey como uyv da como resultado texturas estiradas extrañas en superficies verticales.
Mi solución a continuación usa las normales de superficie para elegir los dos componentes (x, y y z) que se asignarán a uy v. Sigue siendo bastante crudo, pero funciona bastante 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;
}
Esta función no normaliza los UV al tamaño del objeto. Esto funciona mejor cuando se aplica la misma textura a objetos de diferentes tamaños en la misma escena. Sin embargo, dependiendo del tamaño de su sistema de coordenadas mundial, probablemente necesite escalar y repetir la textura también:
texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
Las respuestas aquí son brillantes y me ayudaron mucho. Solo una cosa: si está actualizando vértices, no reasigne uvs, sino configúrelos, como en (scope es mi geometría):
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;
}
Que yo sepa, no hay una forma automática de calcular UV.
Debes calcular tú mismo. Calcular un UV para un avión es bastante fácil, este sitio explica cómo: calcular las coordenadas de la textura
Para una forma compleja, no sé cómo. Tal vez podrías detectar la superficie plana.
EDITAR
Aquí hay un código de muestra para una superficie plana (x, y, z)
donde 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;