THREE.js生成UV坐标
我正在使用THREE.js OBJ加载器将模型导入场景。
我知道我可以很好地导入几何体,因为当我为它分配一个MeshNormalMaterial时,它显示很好。 但是,如果我使用任何需要UV坐标的东西,它给我的错误:
[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1
我知道这是因为加载的OBJ没有UV坐标,但我想知道是否有任何方法来生成所需的纹理坐标。 我努力了
material.needsUpdate = true; geometry.uvsNeedUpdate = true; geometry.buffersNeedUpdate = true;
…但无济于事。
有没有办法使用three.js自动生成UV纹理,还是我必须自己分配坐标?
据我所知,没有自动计算紫外线的方法。
你必须计算自己。 计算一个平面的UV是相当容易的,本网站解释如何: 计算纹理坐标
对于一个复杂的形状,我不知道如何。 也许你可以检测平面表面。
编辑
这里是z = 0
的平面曲面(x, y, z)
的示例代码:
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;
在这里的其他答案是一个很大的帮助,但并不完全符合我的要求重复的图案纹理应用于大多表面平坦的形状的所有面。 问题是仅使用x和y组件作为u和v导致在垂直表面上奇怪的拉伸纹理。
我下面的解决scheme使用曲面法线来select映射到u和v的哪两个分量(x,y和z)。它仍然非常粗糙,但它工作得很好。
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; }
这个函数并没有将UVs归一化到对象的大小。 将相同的纹理应用于同一场景中不同大小的对象时,效果会更好。 但是,根据世界坐标系的大小,您可能还需要缩放和重复纹理:
texture.repeat.set(0.1, 0.1); texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
这里的答案很棒,帮了我很大的忙。 只有一件事:如果你正在更新顶点,不要重新分配uvs,而是设置它们,如(范围是我的几何graphics):
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; }
这是一个适用于球形映射(偏航,俯仰坐标)的通用版本,请参阅此处的示例(查看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( nz, - nx ) / ( 2.0 * Math.PI ); var pitch = .5 - Math.asin( ny ) / Math.PI; var u = yaw, v = pitch; uvs.push( new THREE.Vector2( u, v ) ); } geometry.faceVertexUvs[ 0 ].push( uvs ); }); geometry.uvsNeedUpdate = true; }