以编程方式为模型生成简单的 UV 贴图

Programmatically generate simple UV Mapping for models

来自 question I'm trying to generate UV Mappings programmatically with Three.js for some models, I need this because my models are being generated programmatically too and I need to apply a simple texture to them. I have read here 并成功地为一些简单的 3D 文本生成了 UV 贴图,但是当将相同的贴图应用于更复杂的模型时它就不起作用了。

我尝试应用的纹理是这样的:

黑色背景在 PNG 图像中只是透明的。我需要将它应用于我的模型,它只是一种闪光效果,所以我不关心模型中的确切位置,有什么方法可以为这种情况以编程方式创建简单的 UV 贴图吗?

我正在使用链接问题中的这段代码,它适用于平面模型但不适用于非平面模型:

assignUVs = function( geometry ){

    geometry.computeBoundingBox();

    var max     = geometry.boundingBox.max;
    var 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);

    geometry.faceVertexUvs[0] = [];
    var faces = geometry.faces;

    for (i = 0; i < geometry.faces.length ; i++) {

      var v1 = geometry.vertices[faces[i].a];
      var v2 = geometry.vertices[faces[i].b];
      var 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;
}

你需要更具体一些。在这里,我将以编程方式应用 UV 映射

for (i = 0; i < geometry.faces.length ; i++) {
   geometry.faceVertexUvs[0].push([
     new THREE.Vector2( 0, 0 ),
     new THREE.Vector2( 0, 0 ),
     new THREE.Vector2( 0, 0 ),
   ]);
}

开心吗?

有无数种应用 UV 坐标的方法。这个怎么样

for (i = 0; i < geometry.faces.length ; i++) {
   geometry.faceVertexUvs[0].push([
     new THREE.Vector2( Math.random(), Math.random() ),
     new THREE.Vector2( Math.random(), Math.random() ),
     new THREE.Vector2( Math.random(), Math.random() ),
   ]);
}

没有正确的答案。您想做什么取决于您。这有点像问我如何在纸上涂铅笔。

抱歉这么尖刻,只是指出这个问题在某种意义上是荒谬的。

无论如何,应用纹理有几种常用方法。

  • 球面映射

    假设您的模型是半透明的,里面有一个由胶片制成的球体,球体内部是一个点光源,因此它从球体向各个方向投射(就像电影放映机一样)。因此,您可以计算出适合该情况的正确 UV

    要在球体上得到一个点,请将您的点乘以球体的世界矩阵的倒数,然后将结果归一化。在那之后,尽管仍然存在纹理本身如何映射到假想球体的问题,但仍然有无数种方法。

    我猜最简单的方法叫做 mercator projection,世界上大多数二维地图都是这样工作的。他们有很多 space 被浪费在北极和南极的问题。假设x,y,z是上一段提到的归一化坐标那么

    U = Math.atan2(z, x) / Math.PI * 0.5 - 0.5;
    V = 0.5 - Math.asin(y) / Math.PI;
    
  • 投影映射

    这就像一部电影。你有一个从一个点投影的二维图像。想象一下,您将电影放映机(或投影电视)对准椅子。计算那些点

    计算这些点与几乎所有 WebGL 应用程序从 3D 数据计算 2D 图像完全一样。通常他们在顶点着色器中有这样一条线

    gl_Position = matrix * position;
    

    其中 matrix = worldViewProjection。然后你可以做

    clipSpace = gl_Position.xy / gl_Position.w
    

    您现在有从 -1 到 +1 的 x,y 值。然后你转换它们 UV 坐标从 0 到 1

    uv = clipSpace * 0.5 + 0.5;
    

    当然,通常您会在 JavaScript 的初始时间计算 UV 坐标,但概念是相同的。

  • 平面映射

    这与投影映射几乎相同,只是想象投影仪不是一个点,而是与您要投影的尺寸相同。换句话说,使用投影贴图,当您将模型移近投影仪时,投影的图片会变小,但平面不会。

    按照投影映射示例,这里唯一的区别是使用正交投影而不是透视投影。

  • 立方体映射?

    这实际上是从 6 个方向进行的平面映射。由你决定 决定哪个 UV 坐标获得 6 个平面中的哪个。我猜 大多数时候你会用三角形的法线来查看哪个 平面最面向,然后从该平面进行平面映射。

    实际上我可能混淆了我的术语。你也可以做 真正的立方体贴图,你有一个立方体纹理,但这需要 U,V,W 而不仅仅是 U,V。为此,它与球体相同 示例,除了您直接使用归一化坐标作为 U,V,W.

  • 圆柱映射

    这类似于球体贴图,只是假设有微小的圆柱体投射到您的模型上。与球体不同,圆柱体具有方向,但基本上您可以将模型的点移动到圆柱体的方向,然后假设 x、y、z 现在是相对于圆柱体的(换句话说,您将它们乘以矩阵的逆矩阵表示圆柱体的方向),然后 .

    U = Math.atan2(x, z) / Math.PI * 0.5 + 0.5
    V = y
    

另外 2 个解决方案

  1. 也许您需要环境映射?

    Here's 1 example and Here's another.

  2. 也许您应该考虑使用内置 UV editors 和 UV 投影仪的建模包,例如 Maya 或 Blender。