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

如何在C++中将球面几何转换为椭球几何并生成顶点

问题描述

我有一段生成球面顶点的C++代码,但需要修改为生成满足椭球方程 ((x²)/(a²)) + ((y²)/(b²)) + ((z²)/(c²)) = 1 的顶点,其中a、b、c是椭球三个轴的长度。现有球面生成代码如下:

Sphere::Sphere(const LengthT radius, const LengthT a, const LengthT b, const LengthT c, std::uint16_t sectorCount, std::uint16_t stackCount) :
    m_radius(radius),
    m_sectorCount(sectorCount),
    m_stackCount(stackCount) {
    const double sectorStep{2 * std::numbers::pi / m_sectorCount};
    const double stackStep{std::numbers::pi / m_stackCount};
    constexpr double half_pi{std::numbers::pi / 2};
    double xy, sectorAngle, stackAngle;
    const auto lengthInv{double(1) / m_radius};
    for(std::uint16_t i{0}; i <= stackCount; i++) {
        Vertex vertex;
        stackAngle = (half_pi) - (i * stackStep);
        xy = m_radius * cos(stackAngle);
        vertex.pos.z = m_radius * sin(stackAngle);
        for(std::uint16_t j{0}; j <= sectorCount; j++) {
            sectorAngle = j * sectorStep;
            vertex.pos.x = xy * cos(sectorAngle);
            vertex.pos.y = xy * sin(sectorAngle);
            vertex.normals.x = vertex.pos.x * lengthInv;
            vertex.normals.y = vertex.pos.y * lengthInv;
            vertex.normals.z = vertex.pos.z * lengthInv;
            vertex.texCords.x = double(j) / sectorCount;
            vertex.texCords.y = double(i) / stackCount;
            verticies.push_back(vertex);
        }
    }
}
解决方案

核心思路是将球面坐标按椭球三个轴的长度进行缩放,同时修正法线计算(椭球法线与球面法线逻辑不同,需基于椭球方程推导)。修改后的代码如下:

Sphere::Sphere(const LengthT radius, const LengthT a, const LengthT b, const LengthT c, std::uint16_t sectorCount, std::uint16_t stackCount) :
    m_radius(radius),
    m_sectorCount(sectorCount),
    m_stackCount(stackCount) {
    const double sectorStep{2 * std::numbers::pi / m_sectorCount};
    const double stackStep{std::numbers::pi / m_stackCount};
    constexpr double half_pi{std::numbers::pi / 2};
    double sectorAngle, stackAngle;
    // 预计算轴长的倒数平方,用于法线计算
    const double invA2 = 1.0 / (static_cast<double>(a) * static_cast<double>(a));
    const double invB2 = 1.0 / (static_cast<double>(b) * static_cast<double>(b));
    const double invC2 = 1.0 / (static_cast<double>(c) * static_cast<double>(c));

    for(std::uint16_t i{0}; i <= stackCount; i++) {
        Vertex vertex;
        stackAngle = half_pi - (i * stackStep);
        // 先计算单位球面的坐标分量
        const double sphereXy = cos(stackAngle);
        const double sphereZ = sin(stackAngle);

        for(std::uint16_t j{0}; j <= sectorCount; j++) {
            sectorAngle = j * sectorStep;
            // 缩放球面坐标到椭球尺寸(若需保留radius整体缩放,可乘以m_radius)
            vertex.pos.x = static_cast<double>(a) * sphereXy * cos(sectorAngle);
            vertex.pos.y = static_cast<double>(b) * sphereXy * sin(sectorAngle);
            vertex.pos.z = static_cast<double>(c) * sphereZ;

            // 计算椭球法线:基于椭球方程的梯度(x/a², y/b², z/c²),归一化后得到单位法线
            glm::vec3 normal{
                vertex.pos.x * invA2,
                vertex.pos.y * invB2,
                vertex.pos.z * invC2
            };
            normal = glm::normalize(normal);
            vertex.normals.x = normal.x;
            vertex.normals.y = normal.y;
            vertex.normals.z = normal.z;

            vertex.texCords.x = static_cast<double>(j) / sectorCount;
            vertex.texCords.y = static_cast<double>(i) / stackCount;
            verticies.push_back(vertex);
        }
    }
}

关键改动说明

  • 顶点坐标生成:不再用原radius统一缩放,而是将单位球面坐标分别乘以a、b、c三个轴长,直接生成满足椭球方程的顶点。若需要保留radius作为整体缩放因子,可在每个坐标计算时额外乘以m_radius。
  • 法线修正:椭球的法线不能复用球面的归一化坐标,正确逻辑是取椭球方程的梯度方向(x/a², y/b², z/c²),归一化后得到单位法线向量。
  • 简化冗余逻辑:移除原代码中lengthInv、xy等冗余变量,改用更直观的单位球面分量,提升代码可读性。

更新:此方案已开发并测试通过,已被选为有效答案。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.23 21:13:27