THREE.js生成UV坐标

Cab*_*ibo 30 javascript geometry texture-mapping uv-mapping three.js

我正在使用THREE.js OBJ加载器将模型导入场景.

我知道我可以很好地导入几何体,因为当我为它指定一个MeshNormalMaterial时,它显示出很棒的效果.但是,如果我使用任何需要UV坐标的东西,它会给我错误:

[.WebGLRenderingContext]GL ERROR :GL_INVALID_OPERATION : glDrawElements: attempt to access out of range vertices in attribute 1 
Run Code Online (Sandbox Code Playgroud)

我知道这是因为加载的OBJ没有UV坐标,但我想知道是否有任何方法可以生成所需的纹理坐标.我试过了

material.needsUpdate = true;
geometry.uvsNeedUpdate = true;
geometry.buffersNeedUpdate = true;
Run Code Online (Sandbox Code Playgroud)

......但无济于事.

有没有办法使用three.js自动生成UV纹理,还是我必须自己分配坐标?

小智 42

据我所知,没有自动计算紫外线的方法.

你必须自己计算.计算一个平面的紫外线非常容易,这个网站解释了如何:计算纹理坐标

对于复杂的形状,我不知道如何.也许你可以探测平面.

编辑

以下是平面表面的示例代码,(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;
Run Code Online (Sandbox Code Playgroud)

  • 为了帮助那些可能寻找不存在的解决方案...对于"复杂",不规则形状,唯一(理智)方式是使用像Blender这样的3D软件,手动将纹理放置在模型上并导出文件. (5认同)

Tam*_*lyn 14

这里的其他答案是一个很大的帮助,但不太符合我的要求,将重复的图案纹理应用于大多数平坦表面的形状的所有边.问题是仅使用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;
}
Run Code Online (Sandbox Code Playgroud)

此功能不会将UV规范化为对象的大小.将相同的纹理应用于同一场景中的不同大小的对象时,这种方法效果更好.但是,根据世界坐标系的大小,您可能还需要缩放和重复纹理:

texture.repeat.set(0.1, 0.1);
texture.wrapS = texture.wrapT = THREE.MirroredRepeatWrapping;
Run Code Online (Sandbox Code Playgroud)


小智 5

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

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;

}
Run Code Online (Sandbox Code Playgroud)


Ale*_*lov 5

Box UV 映射可能是任何类型的three.js 配置器中最有用的东西, - https://jsfiddle.net/mmalex/pcjbysn1/

该解决方案适用于索引和非索引缓冲区几何图形。

三.js UV贴图框

用法示例:

//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;
Run Code Online (Sandbox Code Playgroud)

该示例基于以下实现 applyBoxUV

//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;
Run Code Online (Sandbox Code Playgroud)