如何在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
相关产品推荐
相关产品推荐

