Javascript THREE.js生成UV坐标

Javascript THREE.js生成UV坐标,javascript,three.js,geometry,texture-mapping,uv-mapping,Javascript,Three.js,Geometry,Texture Mapping,Uv Mapping,我正在使用THREE.js OBJ loader将模型导入到场景中 我知道我能够很好地导入几何体,因为当我为其指定MeshNormalMaterial时,它显示得非常好。但是,如果我使用需要UV坐标的任何东西,它会给出错误: [.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1 我知道这是因为

我正在使用THREE.js OBJ loader将模型导入到场景中

我知道我能够很好地导入几何体,因为当我为其指定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的方法

你必须自己计算。计算平面的UV非常简单,本网站介绍了如何:

对于一个复杂的形状,我不知道怎么做。也许你能探测到平面

编辑

下面是平面的示例代码
(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;
geometry.computeBoundingBox();
var max=geometry.boundingBox.max,
min=geometry.boundingBox.min;
var偏移=新的三个矢量2(0-min.x,0-min.y);
var范围=新的三个矢量2(最大x-最小x,最大y-最小y);
var faces=geometry.faces;
geometry.faceVertexUvs[0]=[];
对于(变量i=0;i
这里的其他答案很有帮助,但不太符合我的要求,无法将重复图案纹理应用于表面基本平坦的形状的所有侧面。问题在于,仅将x和y分量用作u和v会在垂直曲面上产生奇怪的拉伸纹理

下面我的解决方案使用曲面法线选择要映射到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;
}
此函数不会将UV标准化为对象的大小。将相同纹理应用于同一场景中不同大小的对象时,效果会更好。但是,根据世界坐标系的大小,可能还需要缩放和重复纹理:

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

这里的答案很精彩,对我帮助很大。 只有一件事:如果要更新顶点,请不要重新指定UV,而是设置它们,如(范围是我的几何体):

scope.updateUVs=(copy=true)=>{
scope.computeBoundingBox();
var max=scope.boundingBox.max;
var min=scope.boundingBox.min;
var偏移=新的三个矢量2(0-min.x,0-min.y);
var范围=新的三个矢量2(最大x-最小x,最大y-最小y);
如果(!复制){
scope.faceVertexUvs[0]=[];
}
var faces=scope.faces;
对于(i=0;i
这是一个通用版本,适用于球形映射(偏航、俯仰坐标),参见示例(查看loadSuzanne函数):

函数赋值UV(几何体){
geometry.faceVertexUvs[0]=[];
几何.面.forEach(函数(面){
var-uvs=[];
变量id=['a','b','c'];
对于(变量i=0;i
Box UV映射可能是three.js任何配置程序中最有用的东西-

该解决方案适用于索引和非索引缓冲区几何体的每个面

用法示例:

//build some 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')
});

//find out the dimensions, to let texture size 100% fit without stretching
bufferGeometry.computeBoundingBox();
let bboxSize = bufferGeometry.boundingBox.getSize();
let uvMapSize = Math.min(bboxSize.x, bboxSize.y, bboxSize.z);

//calculate UV coordinates, if uv attribute is not present, it will be added
applyBoxUV(bufferGeometry, new THREE.Matrix4().getInverse(cube.matrix), uvMapSize);

//let three.js know
bufferGeometry.attributes.uv.needsUpdate = true;
该示例基于以下
applyBoxUV

function\u applyBoxUV(几何、变换矩阵、bbox、bbox\u最大大小){
设coords=[];
coords.length=2*geom.attributes.position.array.length/3;
//几何删除属性(“uv”);
if(geom.attributes.uv==未定义){
geom.addAttribute('uv',新的三个.Float32BufferAttribute(coords,2));
}
//将一个面的3个顶点映射到立方体的较好一侧
//立方体的侧面可以是XY、XZ或YZ
设makeUVs=函数(v0、v1、v2){
//预旋转模型,使立方体边与世界轴匹配
v0.应用矩阵X4(转换矩阵);
v1.ApplyMatrix X4(转换矩阵);
v2.应用矩阵X4(转换矩阵);
//获取面的法线,以便更好地了解它映射到哪个立方体边
设n=new-THREE.Vector3();
n、 交叉向量(v1.clone().sub(v0),v1.clone().sub(v2)).normalize();
n、 x=数学绝对值(n.x);
n、 y=数学绝对值(n.y);
n、 z=数学绝对值(n.z);
设uv0=new-THREE.Vector2();
设uv1=new-THREE.Vector2();
让
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;
}
//build some 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')
});

//find out the dimensions, to let texture size 100% fit without stretching
bufferGeometry.computeBoundingBox();
let bboxSize = bufferGeometry.boundingBox.getSize();
let uvMapSize = Math.min(bboxSize.x, bboxSize.y, bboxSize.z);

//calculate UV coordinates, if uv attribute is not present, it will be added
applyBoxUV(bufferGeometry, new THREE.Matrix4().getInverse(cube.matrix), uvMapSize);

//let three.js know
bufferGeometry.attributes.uv.needsUpdate = true;