如何用OpenGL着色器生成圆柱底面及完整圆柱顶点坐标
OpenGL圆柱底面绘制问题解决方法
问题根源分析
你的代码存在三个核心问题导致只显示两个大三角形:
- 重复顶点冗余:顶点数组中大量重复定义了相同坐标的顶点(比如vert3与vert4、vert5/6/7都是同一个点),索引引用的是重复顶点而非圆周上的不同点。
- 索引绘制数量不足:
draw()函数中固定只绘制6个索引(对应2个三角形),循环4次也只是重复绘制这两个三角形,没有覆盖完整的底面。 - 圆周顶点数量不足:仅使用60度的三角函数值生成顶点,没有均匀生成圆周上的多个点来组成完整的扇形面。
圆柱底面坐标推导
圆柱底面是XY平面上的圆,采用三角扇形绘制方式:
- 中心顶点:
(0, 0, 0)(Z轴为0,对应底面所在平面) - 圆周顶点:设底面半径为
r,将圆周等分为numSegments段,第i个顶点的角度为θ = 2π * i / numSegments,坐标为:x = r * cos(θ) y = r * sin(θ) z = 0
每个相邻的圆周顶点与中心顶点组成一个三角形,最终拼接成完整的圆形底面。
修改后的完整代码
以下是修复后的核心代码部分及完整程序:
#include <GL\glew.h> #include <GLFW\glfw3.h> #include <iostream> #include <vector> #include <cmath> // GLM library #include <glm/glm.hpp> #include <glm/gtc/matrix_transform.hpp> #include <glm/gtc/type_ptr.hpp> using namespace std; int width, height; const double PI = 3.1415926535; const float toRadians = PI / 180.0f; // 绘制参数 const int numSegments = 16; // 圆周分段数,数值越大底面越平滑 const float radius = 1.0f; // 底面半径 // Draw Primitive(s) void draw() { GLenum mode = GL_TRIANGLES; // 每个分段对应一个三角形,共numSegments个三角形,每个三角形3个索引 GLsizei indicesCount = numSegments * 3; glDrawElements(mode, indicesCount, GL_UNSIGNED_INT, nullptr); } // Create and Compile Shaders static GLuint CompileShader(const string& source, GLuint shaderType) { GLuint shaderID = glCreateShader(shaderType); const char* src = source.c_str(); glShaderSource(shaderID, 1, &src, nullptr); glCompileShader(shaderID); // 检查编译错误 GLint success; glGetShaderiv(shaderID, GL_COMPILE_STATUS, &success); if (!success) { char infoLog[512]; glGetShaderInfoLog(shaderID, 512, nullptr, infoLog); cout << "Shader compilation error:\n" << infoLog << endl; } return shaderID; } // Create Program Object static GLuint CreateShaderProgram(const string& vertexShader, const string& fragmentShader) { GLuint vertexShaderComp = CompileShader(vertexShader, GL_VERTEX_SHADER); GLuint fragmentShaderComp = CompileShader(fragmentShader, GL_FRAGMENT_SHADER); GLuint shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShaderComp); glAttachShader(shaderProgram, fragmentShaderComp); glLinkProgram(shaderProgram); // 检查链接错误 GLint success; glGetProgramiv(shaderProgram, GL_LINK_STATUS, &success); if (!success) { char infoLog[512]; glGetProgramInfoLog(shaderProgram, 512, nullptr, infoLog); cout << "Program link error:\n" << infoLog << endl; } glDeleteShader(vertexShaderComp); glDeleteShader(fragmentShaderComp); return shaderProgram; } int main(void) { width = 640; height = 480; GLFWwindow* window; if (!glfwInit()) return -1; window = glfwCreateWindow(width, height, "Cylinder Base", NULL, NULL); if (!window) { glfwTerminate(); return -1; } glfwMakeContextCurrent(window); if (glewInit() != GLEW_OK) cout << "GLEW initialization failed!" << endl; // 生成顶点和索引数据 vector<GLfloat> vertices; vector<GLuint> indices; // 添加中心顶点(带颜色) vertices.push_back(0.0f); vertices.push_back(0.0f); vertices.push_back(0.0f); vertices.push_back(1.0f); vertices.push_back(0.0f); vertices.push_back(0.0f); // 红色 // 添加圆周顶点 for (int i = 0; i < numSegments; ++i) { float angle = 2 * PI * i / numSegments; float x = radius * cos(angle); float y = radius * sin(angle); // 顶点坐标 vertices.push_back(x); vertices.push_back(y); vertices.push_back(0.0f); // 顶点颜色(循环RGB) float r = (i % 3 == 0) ? 1.0f : 0.0f; float g = (i % 3 == 1) ? 1.0f : 0.0f; float b = (i % 3 == 2) ? 1.0f : 0.0f; vertices.push_back(r); vertices.push_back(g); vertices.push_back(b); } // 生成索引:每个三角形由中心顶点 + 当前圆周顶点 + 下一个圆周顶点组成 for (int i = 0; i < numSegments; ++i) { indices.push_back(0); // 中心顶点索引 indices.push_back(i + 1); // 当前圆周顶点 indices.push_back((i + 1) % numSegments + 1); // 下一个圆周顶点,循环回到第一个 } glEnable(GL_DEPTH_TEST); glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); GLuint VBO, EBO, VAO; glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glGenVertexArrays(1, &VAO); glBindVertexArray(VAO); glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(GLfloat), vertices.data(), GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, indices.size() * sizeof(GLuint), indices.data(), GL_STATIC_DRAW); // 位置属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)0); glEnableVertexAttribArray(0); // 颜色属性 glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 6 * sizeof(GLfloat), (GLvoid*)(3 * sizeof(GLfloat))); glEnableVertexAttribArray(1); glBindVertexArray(0); // Vertex shader source code string vertexShaderSource = "#version 330 core\n" "layout(location = 0) in vec4 vPosition;" "layout(location = 1) in vec4 aColor;" "out vec4 oColor;" "uniform mat4 model;" "uniform mat4 view;" "uniform mat4 projection;" "void main()\n" "{\n" "gl_Position = projection * view * model * vPosition;" "oColor = aColor;" "}\n"; // Fragment shader source code string fragmentShaderSource = "#version 330 core\n" "in vec4 oColor;" "out vec4 fragColor;" "void main()\n" "{\n" "fragColor = oColor;" "}\n"; GLuint shaderProgram = CreateShaderProgram(vertexShaderSource, fragmentShaderSource); while (!glfwWindowShouldClose(window)) { glfwGetFramebufferSize(window, &width, &height); glViewport(0, 0, width, height); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glUseProgram(shaderProgram); glm::mat4 viewMatrix(1.0f); glm::mat4 projectionMatrix(1.0f); glm::mat4 modelMatrix(1.0f); viewMatrix = glm::translate(viewMatrix, glm::vec3(0.0f, 0.0f, -3.0f)); viewMatrix = glm::rotate(viewMatrix, 45.0f * toRadians, glm::vec3(1.0f, 0.0f, 0.0f)); projectionMatrix = glm::perspective(45.0f, (GLfloat)width / (GLfloat)height, 0.1f, 100.0f); GLuint modelLoc = glGetUniformLocation(shaderProgram, "model"); GLuint viewLoc = glGetUniformLocation(shaderProgram, "view"); GLuint projectionLoc = glGetUniformLocation(shaderProgram, "projection"); glUniformMatrix4fv(modelLoc, 1, GL_FALSE, glm::value_ptr(modelMatrix)); glUniformMatrix4fv(viewLoc, 1, GL_FALSE, glm::value_ptr(viewMatrix)); glUniformMatrix4fv(projectionLoc, 1, GL_FALSE, glm::value_ptr(projectionMatrix)); glBindVertexArray(VAO); draw(); glBindVertexArray(0); glUseProgram(0); glfwSwapBuffers(window); glfwPollEvents(); } glfwTerminate(); return 0; }
关键修改点
- 使用
vector动态生成顶点和索引,避免手动重复定义。 - 均匀生成
numSegments个圆周顶点,确保底面平滑。 - 索引数组正确关联中心顶点与相邻圆周顶点,生成完整的扇形三角面。
- 修复
draw()函数的索引绘制数量,匹配实际的三角形数量。 - 添加了 shader 编译和链接错误检查,方便排查问题。
内容的提问来源于stack exchange,提问作者MachoMan
相关产品推荐
相关产品推荐

