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

如何为OpenGL项目中生成的UV球体计算正确的顶点索引?

OpenGL UV球体索引生成解决方案

现有代码问题排查

  • 数组容量不足:你当前定义的顶点、索引数组大小为numVerticalSegments * numVerticalSegments,既和实际顶点数量不匹配,也缺少南北极顶点的预留空间,球体上下会出现孔洞。
  • 索引逻辑错误:直接将索引值设为对应顶点下标,无法正确组装三角形图元。
  • 顶点下标偏移错误:循环中先执行i++再赋值,会导致vertices[0]无有效数据,出现冗余空顶点。

实现逻辑说明

UV球体的三角形由三部分组成:

  • 北极点和最顶部一圈顶点组成的三角形带
  • 中间相邻两圈顶点组成的四边形,每个四边形拆分为2个三角形
  • 南极点和最底部一圈顶点组成的三角形带

修正后完整代码

Mesh ObjectFactory::createSphere() {

    // Texture loaded and binded to the shaderprogram
    Texture textures[]{
        Texture("resources/pop_cat.png", "diffuse", 0, GL_RGBA, GL_UNSIGNED_BYTE),
    };

    const int numHorizontalSegments = 20;
    const int numVerticalSegments = 20;
    // 总顶点数 = 南北极2个 + 横向环数量*每环顶点数
    const int totalVertices = 2 + numHorizontalSegments * numVerticalSegments;
    // 总索引数:北极三角形*3 + 中间四边形*2*3 + 南极三角形*3
    const int totalIndices = 3 * (numVerticalSegments * 2 + (numHorizontalSegments - 1) * numVerticalSegments * 2);

    std::vector<Vertex> vertices(totalVertices);
    std::vector<GLuint> indices(totalIndices);
    int vertexIdx = 0;
    int indexIdx = 0;

    // 1. 存入北极点
    vertices[vertexIdx++] = Vertex{glm::vec3(0.0f, 1.0f, 0.0f), glm::vec3(0.83f, 0.70f, 0.44f), glm::vec2(0.5f, 1.0f)};

    // 2. 存入中间所有环的顶点
    for (int h = 0; h < numHorizontalSegments; h++) {
        float angle1 = (h + 1) * M_PI / (numHorizontalSegments + 1);
        float sin1 = sinf(angle1);
        float cos1 = cosf(angle1);
        for (int v = 0; v < numVerticalSegments; v++) {
            float angle2 = v * 2 * M_PI / numVerticalSegments;
            float x = sin1 * cosf(angle2);
            float y = cos1;
            float z = sin1 * sinf(angle2);
            // uv坐标可以按需计算,这里给出参考值
            float u = (float)v / numVerticalSegments;
            float v_uv = 1.0f - (float)(h+1) / (numHorizontalSegments + 1);
            vertices[vertexIdx++] = Vertex{glm::vec3(x, y, z), glm::vec3(0.83f, 0.70f, 0.44f), glm::vec2(u, v_uv)};
        }
    }

    // 3. 存入南极点
    vertices[vertexIdx++] = Vertex{glm::vec3(0.0f, -1.0f, 0.0f), glm::vec3(0.83f, 0.70f, 0.44f), glm::vec2(0.5f, 0.0f)};

    // 4. 生成北极区域索引
    for (int v = 0; v < numVerticalSegments; v++) {
        indices[indexIdx++] = 0; // 北极点索引
        indices[indexIdx++] = 1 + v;
        indices[indexIdx++] = 1 + (v + 1) % numVerticalSegments;
    }

    // 5. 生成中间区域索引
    for (int h = 0; h < numHorizontalSegments - 1; h++) {
        for (int v = 0; v < numVerticalSegments; v++) {
            int currRingStart = 1 + h * numVerticalSegments;
            int nextRingStart = 1 + (h + 1) * numVerticalSegments;
            int nextV = (v + 1) % numVerticalSegments;
            // 第一个三角形
            indices[indexIdx++] = currRingStart + v;
            indices[indexIdx++] = nextRingStart + v;
            indices[indexIdx++] = nextRingStart + nextV;
            // 第二个三角形
            indices[indexIdx++] = currRingStart + v;
            indices[indexIdx++] = nextRingStart + nextV;
            indices[indexIdx++] = currRingStart + nextV;
        }
    }

    // 6. 生成南极区域索引
    int southPoleIdx = totalVertices - 1;
    int bottomRingStart = 1 + (numHorizontalSegments - 1) * numVerticalSegments;
    for (int v = 0; v < numVerticalSegments; v++) {
        indices[indexIdx++] = southPoleIdx;
        indices[indexIdx++] = bottomRingStart + (v + 1) % numVerticalSegments;
        indices[indexIdx++] = bottomRingStart + v;
    }

    std::vector<Texture> tex(textures, textures + sizeof(textures) / sizeof(Texture));
    // 创建球体网格
    return {vertices, indices, tex};
}

注意事项

  • 代码中已经补充了UV坐标的计算逻辑,你可以根据自己的纹理需求调整。
  • 所有顶点、索引改用std::vector动态分配,避免可变长度数组的编译器兼容问题。
  • 索引组装默认适配GL_TRIANGLES图元模式,如果你使用其他模式可以按需调整索引顺序。

内容的提问来源于stack exchange,提问作者Théo

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.29 08:36:03