首页 > 解决方案 > 三.JS UV Mapping for partial spheres

问题描述

上一个问题中,我希望将立体投影映射到球体上,以便实时流式传输虚拟现实事件。

我最终学到了很多关于 UV 映射的知识,并且能够构建一个非常精确的映射,看起来令人难以置信。然而,我不喜欢这种映射方法的一件事:有一个技巧,球体的底部必须映射到“无”,所以我将它映射到纹理的一个角落。

为了改进这个解决方案,我尝试通过调整SphereGeometry 上的 Three.JS 文档中的 theta 长度来仅创建一个部分球体。

虽然我得到了正确的球体形状,但我注意到 UV 映射有些奇怪:

var fov = 270;
var geometry = new THREE.SphereGeometry(
    1, // Radius
    50, // Horizontal segments
    50, // Vertical segments
    0, // Phi start
    2 * Math.PI, // Phi length
    Math.acos((360 - fov) * Math.PI / 180 / 2), // Theta start
    Math.PI - Math.acos((360 - fov) * Math.PI / 180 / 2) // Theta length
);
var minX, minY, minZ, maxX, maxY, maxZ;
var faceVertexUvs = geometry.faceVertexUvs[0];
for ( var i = 0; i < faceVertexUvs.length; i++ ) {
    var face = geometry.faces[i];
    for ( var j = 0; j < 3; j ++ ) {
        var x = face.vertexNormals[j].x;
        var y = face.vertexNormals[j].y;
        var z = face.vertexNormals[j].z;

        // Capture the upper and lower bounds for all points
        if (!minX || x < minX) minX = x;
        if (!maxX || x > maxX) maxX = x;
        if (!minY || y < minY) minY = y;
        if (!maxY || y > maxX) maxY = y;
        if (!minZ || z < minZ) minZ = z;
        if (!maxZ || z > maxZ) maxZ = z;
    }
}
console.log("minX: " + minX + ", maxX: " + maxX);
console.log("minY: " + minY + ", maxY: " + maxY);
console.log("minZ: " + minZ + ", maxZ: " + maxZ);

我之前在执行 UV 映射时了解到,无论网格的实际大小如何xy、 和z值的范围都是从-1到-。1如果球体的半径为 1 或半径为 100,则x值 1 表示“球体的最右侧”,y值 1 表示“球体的顶部”。然而,当我有一个像这样的不完整球体时(使用thetaStartthetaLength在球体顶部切一个洞)突然maxY等于大约 0.7854。

为什么这个网格以 0.7854 结束,而不是从 -1 缩放到 1?我曾希望通过改变球体的形状来简化我的 UV 映射逻辑(scaledY从我之前链接的问题中删除该术语)但是改变球体的形状似乎对UV 贴图没有影响。

有没有办法告诉 Three.JS 这个部分球体是完整的形状,它的坐标应该在 -1 到 1 的范围内?

标签: javascript3dthree.js

解决方案


我相信您在描述和代码片段中将法线与 UV 混淆了。您正在以下几行中阅读上述 Normals 的值:

    var x = face.vertexNormals[j].x;
    var y = face.vertexNormals[j].y;
    var z = face.vertexNormals[j].z;

使用 SphereGeometry 属性,最小和最大 y法线与预期结果匹配:在此处输入图像描述

您要做的是查看最小和最大UV值。我更新了代码以获取 UV 值:

var fov = 270;
var geometry:THREE.SphereGeometry = new THREE.SphereGeometry(
    2, // Radius
    50, // Horizontal segments
    50, // Vertical segments
    0, // Phi start
    2 * Math.PI, // Phi length
    Math.acos((360 - fov) * Math.PI / 180 / 2), // Theta start
    Math.PI - Math.acos((360 - fov) * Math.PI / 180 / 2) // Theta length
);
var minX, minY, minZ, maxX, maxY, maxZ;
var faceVertexUvs = geometry.faceVertexUvs[0];

for ( var i = 0; i < faceVertexUvs.length; i++ ) {
    var vertUV = faceVertexUvs[i];
    for ( var j = 0; j < 3; j ++ ) {
        var x = vertUV[j].x;
        var y = vertUV[j].y;

        // Capture the upper and lower bounds for all points
        if (!minX || x < minX) minX = x;
        if (!maxX || x > maxX) maxX = x;
        if (!minY || y < minY) minY = y;
        if (!maxY || y > maxX) maxY = y;
    }
}
console.log("minX: " + minX + ", maxX: " + maxX);
console.log("minY: " + minY + ", maxY: " + maxY);

运行此代码时,您会看到范围始终是[0, 1],而不是[-1, 1]。此外,请记住 UV 没有 Z 值;只有 X 和 Y,因为它们用于映射二维图像。

法线:

  • x: (-1, 1)
  • y: (-1, 1)
  • z: (-1, 1)

紫外线:

  • x: [0, 1]
  • y: [0, 1]

您可以在这段代码中看到 ThreeJS 如何构建球体 UV ,特别是在第 90、94 和 111 行。


推荐阅读