OpenGL中基于GLM实现相机球面插值的问题咨询
相机球面弧线插值问题解答
关于glm::rotation的正确用法
你当前代码里的glm::rotation(camera_start_pos, vec3(1,1,1))完全误解了这个函数的作用——glm::rotation(a, b)是生成一个将向量a旋转到向量b的四元数。要实现球面位置插值,首先要明确:球面上的点可看作从球面中心(默认原点)出发的向量,第一步需把起始、结束位置向量归一化得到单位方向向量:
glm::vec3 start_dir = glm::normalize(camera_start_pos); glm::vec3 end_dir = glm::normalize(camera_end_pos);
若想用四元数实现旋转插值,应生成从基准单位向量到这两个方向向量的旋转四元数。更合理的方式是生成能将start_dir旋转到end_dir的旋转四元数,再对旋转进度插值:
// 生成从起始方向到结束方向的旋转四元数 glm::quat rot_q = glm::rotation(start_dir, end_dir); // 插值旋转进度:cur_time从0到1,0对应无旋转(起始方向),1对应完全转到结束方向 glm::quat interpolated_rot = glm::slerp(glm::quat(1,0,0,0), rot_q, cur_time);
插值后如何得到正确位置
得到插值后的旋转四元数后,将其作用到归一化的起始位置向量上,再乘以原球面半径(即起始位置向量的长度,camera_start_pos到原点的距离):
float radius = glm::length(camera_start_pos); glm::vec3 interpolated_dir = interpolated_rot * start_dir; camera_pos = interpolated_dir * radius;
这样就能保证相机始终在以原点为中心、半径固定的球面上,沿弧线从start_pos移动到end_pos。
无需四元数的球面插值方法
完全可以不用四元数,直接对向量做球面线性插值(Vector SLERP),逻辑更直观:
- 计算归一化后的起始、结束方向向量的点积,得到夹角余弦值:
glm::vec3 start_dir = glm::normalize(camera_start_pos); glm::vec3 end_dir = glm::normalize(camera_end_pos); float dot = glm::dot(start_dir, end_dir);
- 限制dot范围避免数值误差:
dot = glm::clamp(dot, -1.0f, 1.0f);
- 计算两向量夹角θ,再用三角函数计算插值权重:
float theta = glm::acos(dot); float sin_theta = glm::sin(theta); // cur_time为0到1的插值因子 float w0 = glm::sin((1 - cur_time) * theta) / sin_theta; float w1 = glm::sin(cur_time * theta) / sin_theta;
- 加权得到插值方向,再乘以半径得到最终位置:
float radius = glm::length(camera_start_pos); glm::vec3 interpolated_dir = w0 * start_dir + w1 * end_dir; camera_pos = interpolated_dir * radius;
内容的提问来源于stack exchange,提问作者Andriy Sultanov
相关产品推荐
相关产品推荐

