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

OpenGL中C++实现的圆柱如何添加上下底面?

给圆柱添加上下底面的解决方案

你的现有代码已经实现了圆柱的侧面,但上下底面是独立的圆形平面,需要单独构造顶点并绘制。下面一步步帮你完善:

1. 先优化现有侧面生成代码

你原来的GetUV函数里有多余的cv和sv变量(v是高度方向,不需要三角函数),可以先简化:

glm::vec3 CMyApp::GetUV(float u, float v) {
    float r = 0.5; // 半径
    float h = 2;   // 高度
    u *= 2 * 3.1415f; // 把[0,1]的u映射到0~2π
    v *= h; // 把[0,1]的v映射到0~h
    float cu = cosf(u), su = sinf(u);
    return glm::vec3(r * cu, v, -r * su);
}

同时补全Init函数里的u和v赋值,改用动态数组避免栈溢出:

// 侧面顶点数:(圆周分段数N+1) × (高度分段数M+1)
int sideVertCount = (N + 1) * (M + 1);
// 总顶点数:侧面 + 两个底面(每个底面N+1个顶点:圆心+N个圆周点)
std::vector<Vertex> verts(sideVertCount + 2 * (N + 1));
int idx = 0;

// 生成侧面顶点
for (int i = 0; i <= N; ++i) {
    float u = static_cast<float>(i) / N; // 圆周方向参数,范围[0,1]
    for (int j = 0; j <= M; ++j) {
        float v = static_cast<float>(j) / M; // 高度方向参数,范围[0,1]
        glm::vec3 pos = GetUV(u, v);
        verts[idx].position = pos;
        // 侧面UV直接用u和v,方便贴图
        verts[idx].uv = glm::vec2(u, v);
        // 侧面法线:径向向外(忽略y分量,归一化)
        verts[idx].normal = glm::normalize(glm::vec3(pos.x, 0.0f, pos.z));
        idx++;
    }
}

2. 添加上下底面的顶点生成

上下底面是圆心在(0, 0, 0)和(0, h, 0)的圆形,用**三角扇(GL_TRIANGLE_FAN)**绘制最高效(圆心+一圈点就能生成整个圆面):

float r = 0.5f;
float h = 2.0f;

// 生成下底面(y=0,法线向下)
// 先添加圆心
verts[idx].position = glm::vec3(0.0f, 0.0f, 0.0f);
verts[idx].uv = glm::vec2(0.5f, 0.5f); // 圆心对应UV纹理中心
verts[idx].normal = glm::vec3(0.0f, -1.0f, 0.0f);
idx++;
// 添加圆周上的点
for (int i = 0; i <= N; ++i) {
    float u = static_cast<float>(i) / N * 2 * 3.1415f;
    float cu = cosf(u), su = sinf(u);
    verts[idx].position = glm::vec3(r * cu, 0.0f, -r * su);
    // 把圆周点映射到UV纹理的[0,1]范围(圆心在0.5,0.5)
    verts[idx].uv = glm::vec2(0.5f + 0.5f * cu, 0.5f + 0.5f * su);
    verts[idx].normal = glm::vec3(0.0f, -1.0f, 0.0f);
    idx++;
}

// 生成上底面(y=h,法线向上)
verts[idx].position = glm::vec3(0.0f, h, 0.0f);
verts[idx].uv = glm::vec2(0.5f, 0.5f);
verts[idx].normal = glm::vec3(0.0f, 1.0f, 0.0f);
idx++;
for (int i = 0; i <= N; ++i) {
    float u = static_cast<float>(i) / N * 2 * 3.1415f;
    float cu = cosf(u), su = sinf(u);
    verts[idx].position = glm::vec3(r * cu, h, -r * su);
    verts[idx].uv = glm::vec2(0.5f + 0.5f * cu, 0.5f + 0.5f * su);
    verts[idx].normal = glm::vec3(0.0f, 1.0f, 0.0f);
    idx++;
}

3. 分别绘制侧面和底面

在渲染函数中,需要用不同的图元类型绘制不同部分:

// 绘制侧面(用三角带或三角形,根据你的原始逻辑选择)
glDrawArrays(GL_TRIANGLE_STRIP, 0, sideVertCount);

// 绘制下底面(三角扇:从第sideVertCount个顶点开始,共N+1个顶点)
glDrawArrays(GL_TRIANGLE_FAN, sideVertCount, N + 1);

// 绘制上底面(从sideVertCount + N+1个顶点开始,共N+1个顶点)
glDrawArrays(GL_TRIANGLE_FAN, sideVertCount + N + 1, N + 1);

关键细节说明

  • 法线设置:侧面法线径向向外,上下底面法线分别指向y轴负方向和正方向,这样光照计算才会正确显示面的朝向。
  • UV映射:底面的UV把圆形映射到正方形纹理的中心区域,避免贴图拉伸变形。
  • 动态数组:用std::vector<Vertex>替代固定大小的数组,避免栈溢出,也更灵活。

内容的提问来源于stack exchange,提问作者chuck

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 03:59:06