You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何为三角形带构建的14顶点立方体设置纹理坐标?

绝对可以搞定!首先得明确你这个14顶点三角形带立方体的顶点结构——这类立方体一般是按环形顺序构建的,用最少的顶点生成6个面。核心思路是先划分你的纹理图集的UV区域,再给每个顶点(或根据顶点所在的面)分配对应的UV坐标。下面给你一步步拆解:

第一步:定义纹理图集的UV区域

假设你的纹理图是纵向划分的(你可以根据实际调整):

  • 顶面:占据纹理的上1/3,UV范围是 (0.0, 2/3) 到 (1.0, 1.0)
  • 底面:占据纹理的中1/3,UV范围是 (0.0, 1/3) 到 (1.0, 2/3)
  • 四个侧面:共享纹理的下1/3,UV范围是 (0.0, 0.0) 到 (1.0, 1/3)

如果你的纹理是横向划分的,只需要把UV的x和y范围调换即可,比如顶面占左1/3,底面中1/3,侧面右1/3。

第二步:给顶点分配UV(两种方案)

方案1:CPU端预生成UV数据(简单直接)

如果你的14顶点顺序是标准的三角形带立方体(底面4个→顶面4个→闭合用的6个),可以直接给每个顶点绑定对应面的UV:

假设你的立方体是轴对齐的,中心在原点,边长为1(坐标范围 -0.5 到 0.5),伪代码示例:

// 定义各区域的UV边界
struct UVRegion {
    float uMin, uMax;
    float vMin, vMax;
};

UVRegion top = {0.0f, 1.0f, 2.0f/3.0f, 1.0f};
UVRegion bottom = {0.0f, 1.0f, 1.0f/3.0f, 2.0f/3.0f};
UVRegion side = {0.0f, 1.0f, 0.0f, 1.0f/3.0f};

// 14个顶点的UV数组
vec2 uvs[14];

// 底面顶点(索引0-3:左前、右前、左后、右后)
uvs[0] = {bottom.uMin, bottom.vMin};
uvs[1] = {bottom.uMax, bottom.vMin};
uvs[2] = {bottom.uMin, bottom.vMax};
uvs[3] = {bottom.uMax, bottom.vMax};

// 顶面顶点(索引4-7:左后、右后、左前、右前)
uvs[4] = {top.uMin, top.vMin};
uvs[5] = {top.uMax, top.vMin};
uvs[6] = {top.uMin, top.vMax};
uvs[7] = {top.uMax, top.vMax};

// 侧面闭合顶点(索引8-13:右前、右后、右后、左后、左后、左前)
// 这些顶点属于侧面,直接用侧面区域的UV
uvs[8] = {side.uMin, side.vMin};
uvs[9] = {side.uMax, side.vMin};
uvs[10] = {side.uMin, side.vMax};
uvs[11] = {side.uMax, side.vMax};
uvs[12] = {side.uMin, side.vMin};
uvs[13] = {side.uMax, side.vMin};

如果你的顶点顺序不同,只需要对应调整每个索引的UV即可——核心是判断每个顶点属于顶面、底面还是侧面,然后映射到对应的纹理区域。

方案2:几何着色器中动态计算UV(灵活适配)

如果你的顶点顺序不固定,或者想在GPU端处理,可以在几何着色器中根据顶点的位置/法向量判断所属面,动态计算UV:

#version 330 core
layout (triangles_adjacency) in;
layout (triangle_strip, max_vertices = 14) out;

out vec2 TexCoords;

// 纹理区域定义(根据你的实际图集调整)
const vec2 TOP_UV_MIN = vec2(0.0, 2.0/3.0);
const vec2 TOP_UV_MAX = vec2(1.0, 1.0);
const vec2 BOTTOM_UV_MIN = vec2(0.0, 1.0/3.0);
const vec2 BOTTOM_UV_MAX = vec2(1.0, 2.0/3.0);
const vec2 SIDE_UV_MIN = vec2(0.0, 0.0);
const vec2 SIDE_UV_MAX = vec2(1.0, 1.0/3.0);

// 立方体的尺寸(假设中心在原点,边长1)
const float HALF_SIZE = 0.5;

void main() {
    for(int i = 0; i < gl_in.length(); i++) {
        vec4 worldPos = gl_in[i].gl_Position;
        vec2 uv;

        // 判断顶点所属面
        if(abs(worldPos.y - HALF_SIZE) < 0.01) { // 顶面
            // 用X/Z坐标映射到0-1的局部UV
            float u = (worldPos.x + HALF_SIZE) / (2 * HALF_SIZE);
            float v = (worldPos.z + HALF_SIZE) / (2 * HALF_SIZE);
            uv = vec2(
                mix(TOP_UV_MIN.x, TOP_UV_MAX.x, u),
                mix(TOP_UV_MIN.y, TOP_UV_MAX.y, v)
            );
        } else if(abs(worldPos.y + HALF_SIZE) < 0.01) { // 底面
            float u = (worldPos.x + HALF_SIZE) / (2 * HALF_SIZE);
            float v = (worldPos.z + HALF_SIZE) / (2 * HALF_SIZE);
            uv = vec2(
                mix(BOTTOM_UV_MIN.x, BOTTOM_UV_MAX.x, u),
                mix(BOTTOM_UV_MIN.y, BOTTOM_UV_MAX.y, v)
            );
        } else { // 侧面
            // 根据X/Z的绝对值判断是左右还是前后侧面,计算局部UV
            float u;
            if(abs(worldPos.x) > HALF_SIZE - 0.01) { // 左右侧面,用Z坐标做U
                u = (worldPos.z + HALF_SIZE) / (2 * HALF_SIZE);
            } else { // 前后侧面,用X坐标做U
                u = (worldPos.x + HALF_SIZE) / (2 * HALF_SIZE);
            }
            // 用Y坐标做V
            float v = (worldPos.y + HALF_SIZE) / (2 * HALF_SIZE);
            uv = vec2(
                mix(SIDE_UV_MIN.x, SIDE_UV_MAX.x, u),
                mix(SIDE_UV_MIN.y, SIDE_UV_MAX.y, v)
            );
        }

        TexCoords = uv;
        gl_Position = worldPos;
        EmitVertex();
    }
    EndPrimitive();
}
常见问题排查
  • 纹理翻转:如果某个面的纹理是倒的,只需要把对应的UV分量取反(比如 1.0 - u 或 1.0 - v)。
  • 边缘拉伸:确保纹理的Wrap模式设置为CLAMP_TO_EDGE,避免纹理边缘的重复采样。
  • 浮点误差:用顶点法向量判断面比用位置更可靠——如果你的顶点属性包含法向量,直接判断法向量的轴向(比如法向量Y=1是顶面)即可。

内容的提问来源于stack exchange,提问作者Adrien Givry

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 06:28:02