利用arcball原理来是相机在某个半径旋转,而且相机的目标方向永远指向最初的目标方向,就是类似于相机在球面上旋转并且始终指向球心。
直接上程序流图和代码。
arcball原理还有相关的旋转矩阵在上一篇文章有介绍:这里
程序流图:
主函数:
/*
arcBallRotate 函数说明:
给定单位化后,鼠标在屏幕上的偏移量,计算在arcball原理下,相机实际位置的变换,并改变相机的实际位置
参数说明:
double x : x方向偏移量
double y : y方向便宜量
*/
void Camera::arcBallRotate(double x, double y)
{
//创建在相机坐标系下的旋转轴和旋转角度
arma::vec3 axisVec;
double theta;
arcball(x, y, axisVec, theta);
//计算世界坐标系下的旋转轴
arma::vec3 worldAxis = cameraToWorldCoord(axisVec);
//构建世界坐标系的坐标系
Axis axis;
axis.position = target;
axis.direction = worldAxis;
//在世界坐标系下旋转theta角,并得到新的相机位置
this->position = this->axisRotateTheta(axis, theta, position);
}
/*
arcball 函数说明:
在单位化下,计算屏幕中鼠标移动的偏移量,在单位球上产生的位置变换,并且用旋转轴和旋转角表示
参数说明:
double x 范围输入0.0 - 1.0;表示在相机坐标系下x方向偏移量
double y 范围输入0.0 - 1.0;表示在相机坐标系下y方向的偏移量
arma::vec3 &axisVec 经过计算后返回的旋转轴方向向量,如果计算失败返回的就是输入值
double &thete 经过计算后旋转的角度,若计算失败返回输入值
函数返回值说明:
如果返回true,则表示计算成功,否则表示计算失败
*/
bool Camera::arcball(double x, double y, arma::vec3& axisVec, double& theta)
{
//检查x的范围是否符合要求
if ((x > 1.0) || (x < -1.0))
{
return false;
}
//检查y的范围是否符合要求
if ((y > 1.0) || (y < -1.0))
{
return false;
}
double z = sqrt((1 - x * x - y * y));//arcball原理计算Z
arma::vec3 startPoint({
0.0,0.0,1.0 });//相机在相机坐标系下鼠标移动前的位置
arma::vec3 endPoint({
x,y,z});//相机在相机坐标系下鼠标移动后的位置
axisVec = arma::normalise(arma::cross(startPoint, endPoint));//得到归一化的叉乘向量,也即旋转轴向量
theta = acos(arma::norm_dot(startPoint, endPoint