1. 前言
这是我最近遇到的一个问题,如题所示,我需要通过鼠标拖拽实现相机绕点的球面旋转,原本的想法很简单,类似笔记八、摄像机中提到的那样,从聚焦中心点的视角出发,将鼠标移动的距离xoffset和yoffset转换为yaw和pitch角,然后计算出该视角的向量,与球面求交从而得到相机的当前位置,但是实际过程中出现了一个比较严重的问题,就是欧拉角的致命缺陷——万向锁,如果pitch角超过±90°,我们就会遇到各种问题,在设计第一视角时我们可以控制pitch角在90°到-90°之间,这不影响我们的体验,但是如果我们要控制相机进行球面旋转的话,这样偷懒可能就不对了,那么能不能有别的方法来实现这种效果呢?当然是有的,一种是四元数,它可以实现球面旋转的平滑插值过渡,但是今天我要介绍另一种更为直观的实现方法
2. 相机的球面旋转
(代码基于笔记九、相机类修改)
同样的,我们需要记录每次鼠标移动的距离xoffset和yoffset,这两个值该怎么去运用呢?
可以画出示意图:
其中A和B是相机移动前后的点,O点是我们相机的聚焦点,那么现在要做的事就很明显了,原本相机的视角是平行的圆面,我们把它看作投影平面,而移动相机时,我们是在OAB形成的圆面上移动,那么我们的任务就是利用OA求得OB
首先我们计算出OA向量,求出当前的旋转半径
void updateCameraVectors()
{
if (xoffset == 0 && yoffset == 0)
return;
glm::vec3 target, offset;
target = Position - Front;
double radius = glm::length(target);
target = glm::normalize(target);
之后我们将当前相机向上方向与OA向量叉乘得到投影面的右向量
Right = glm::normalize(glm::cross(Up, target));
其次再用OA向量与求得的叉乘得到投影面的上向量
Up = glm::normalize(glm::cross(target, Right));
然后我们求屏幕位移在投影面上对应的向量AB(将投影面的右向量与上向量分别乘以xy位移值并相加)
offset = Up * yoffset + Right * xoffset ;
之后我们将位移的的弧长转换为OAB圆的弧度并取负,保证物体的相对旋转方向与鼠标推拽方向一致(相机旋转方向与鼠标位移方向相反)
double len = glm::length(offset);
double angle = -len / radius;
offset = glm::normalize(offset);
最后我们计算新的target向量OB = (OAcos(OAB)+ABsin(OAB)),并将它乘以半径的长度加在聚焦点,从而得到我们的相机位置文章来源:https://www.toymoban.com/news/detail-431091.html
double cr = cos(angle) * radius, sr = sin(angle) * radius;
Position = Front + glm::vec3(target.x * cr + offset.x * sr, target.y * cr + offset.y * sr, target.z * cr + offset.z * sr);
}
3.效果
文章来源地址https://www.toymoban.com/news/detail-431091.html
到了这里,关于【OpenGL】杂谈一、通过鼠标拖拽实现相机绕空间中的某点进行球面旋转查看的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!