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

如何用OpenGL着色器生成圆柱底面及完整圆柱顶点坐标

OpenGL圆柱底面绘制问题解决方法

问题根源分析

你的代码存在三个核心问题导致只显示两个大三角形:

  1. 重复顶点冗余:顶点数组中大量重复定义了相同坐标的顶点(比如vert3与vert4、vert5/6/7都是同一个点),索引引用的是重复顶点而非圆周上的不同点。
  2. 索引绘制数量不足:draw()函数中固定只绘制6个索引(对应2个三角形),循环4次也只是重复绘制这两个三角形,没有覆盖完整的底面。
  3. 圆周顶点数量不足:仅使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.18 19:20:47