如何为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
相关产品推荐
相关产品推荐

