OpenGL通过三角形绘制平整平面时索引错误如何修复
OpenGL平面生成问题修复方案
参考图
- 期望的索引绘制顺序:

- 实际错误渲染结果:

现存问题梳理
- 顶点生成不全:每行顶点的循环上限设为了
terrain_width-1,导致每行少生成1个顶点,和索引计算的预期顶点数不匹配 - 索引越界+计算错误:
- 行循环上限设为了
terrain_height,处理最后一行时访问row+1的顶点会越界 - 三角形索引的计算逻辑完全不符合网格四边形拆分规则,导致索引指向错误的顶点
- 行循环上限设为了
- 容器未清空:每次调用绘制函数都会向顶点、索引、纹理坐标容器追加数据,多次调用后数据冗余错乱
- 法向量未初始化:绑定了法向量属性指针,但没有往法向量容器中写入任何有效数据
- 属性禁用错误:最后禁用了从未启用的
color属性,反而没有禁用启用过的texCoord属性
修改后的代码
void Terrain::draw_terrain(const Input& in) { // 每次绘制前先清空原有数据,避免重复追加 terrain_verts.clear(); terrain_indices.clear(); terrain_texture_coordinates.clear(); terrain_norms.clear(); this->P = glm::mat4(1.0f); this->V = glm::mat4(1.0f); this->M = glm::mat4(1.0f); P = in.P; V = in.V; M = glm::scale(M, glm::vec3(1.0f, 1.0f, 1.0f)); // 先生成所有顶点、法向量、纹理坐标 for (int row = 0; row < terrain_height; row++) { int col; // 修改:循环到terrain_width,生成每行所有顶点 for (col = 0; col < terrain_width; col++) { // x, y, z, 1 terrain_verts.emplace_back(col, row, 0.0f, 1); // 补充平面法向量,全部朝向z轴正方向 terrain_norms.emplace_back(0.0f, 0.0f, 1.0f, 0.0f); } // 纹理坐标生成逻辑保留,按需调整即可 if (row % 2 == 0) { for (col = 0; col < terrain_width; col += 2) { terrain_texture_coordinates.emplace_back(0, 0); terrain_texture_coordinates.emplace_back(1, 0); } } else { for (col = 0; col < terrain_width; col += 2) { terrain_texture_coordinates.emplace_back(0, 1); terrain_texture_coordinates.emplace_back(1, 1); } } } // 单独生成索引,行循环只到terrain_height-1,避免越界 for (int row = 0; row < terrain_height - 1; row++) { int col; for (col = 0; col < terrain_width - 1; col++) { // 第一个三角形(对应你说的蓝色方向) terrain_indices.emplace_back(col + row * terrain_width); // 左上 terrain_indices.emplace_back(col + row * terrain_width + 1); // 右上 terrain_indices.emplace_back(col + (row + 1) * terrain_width); // 左下 // 第二个三角形(对应你说的绿色方向) terrain_indices.emplace_back(col + row * terrain_width + 1); // 右上 terrain_indices.emplace_back(col + (row + 1) * terrain_width + 1); // 右下 terrain_indices.emplace_back(col + (row + 1) * terrain_width); // 左下 } } spLambertTextured->use(); glUniformMatrix4fv(spLambertTextured->u("P"), 1, false, glm::value_ptr(P)); glUniformMatrix4fv(spLambertTextured->u("V"), 1, false, glm::value_ptr(V)); glEnableVertexAttribArray(spLambertTextured->a("vertex")); glEnableVertexAttribArray(spLambertTextured->a("texCoord")); glEnableVertexAttribArray(spLambertTextured->a("normal")); glUniformMatrix4fv(spLambertTextured->u("M"), 1, false, glm::value_ptr(M)); glVertexAttribPointer(spLambertTextured->a("vertex"), 4, GL_FLOAT, false, 0, terrain_verts.data()); glVertexAttribPointer(spLambertTextured->a("texCoord"), 2, GL_FLOAT, false, 0, terrain_texture_coordinates.data()); glVertexAttribPointer(spLambertTextured->a("normal"), 4, GL_FLOAT, false, 0, terrain_norms.data()); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, tex); glUniform1i(spLambertTextured->u("tex"), 0); glDrawElements(GL_TRIANGLES, terrain_indices_count(), GL_UNSIGNED_INT, terrain_indices.data()); // 修改:禁用正确的属性,移除不存在的color属性禁用 glDisableVertexAttribArray(spLambertTextured->a("vertex")); glDisableVertexAttribArray(spLambertTextured->a("texCoord")); glDisableVertexAttribArray(spLambertTextured->a("normal")); }
额外优化建议
如果你的地形尺寸是固定的,不需要每次绘制都重新生成顶点、索引和纹理坐标,完全可以把这部分逻辑移到初始化函数里只执行一次,绘制时直接复用数据即可,能大幅提升渲染性能。
内容的提问来源于stack exchange,提问作者code_rushh
相关产品推荐
相关产品推荐

