第十三章 三维中Camera的控制的总结
13.1 环绕平滑浏览
这里说的环绕浏览,是指绕一个轴旋转浏览。这种方式就等于从一个球上的一点移动到球面的另外一点。而平滑浏览是指这两点之间的轨迹在球面上,从而构成一个圆弧。
假如存在起点P,终点为Q,中间插值点M,那么M的计算可以通过一个函数来获得,M=F(P,Q,t)。如果PMQ三点是直线的话,F就是直线的公式,也就是M=P*t+Q(1-t)。但是现在不是直线,而是曲线,所以这个公式自然跟直线的不同。
这时候我们引入四元数。四元数可以实现下面功能:
- 球面上的一点可以通过转换转换为四元数,也可以从四元数反转化为球面一点。
- 两个四元数可以进行插值运算,而且插值的结果是等差的。(也就是说|MP|/|PQ|=t/1,其中|MP|表示MP的模)
好了,至此环绕平滑浏览的基本思路应该明了了。下面的就是一些细节了。
第一个问题,球面一点如何转换为四元数。
转换的方法有两种:
欧拉角转四元数的公式是
第二个问题,如何进行插值
起点P和终点Q直接的角度差
θ,而等差的角度对应圆弧上相同的长度。所以使用的插值方式是
q(t)=q1 * sinθ(1-t)/sinθ + q2 * sinθt/sineθ
到此,基本操作方式以及列举出来了。至于为啥是这样的,需要对四元数的计算进行额外的学习。下面简单的列出插值的函数:
public static Quaternion4d Slerp(Quaternion4d q0, Quaternion4d q1, double t)
{
double cosom = q0.X * q1.X + q0.Y * q1.Y + q0.Z * q1.Z + q0.W * q1.W;
double tmp0, tmp1, tmp2, tmp3;
if (cosom < 0.0)
{
cosom = -cosom;
tmp0 = -q1.X;
tmp1 = -q1.Y;
tmp2 = -q1.Z;
tmp3 = -q1.W;
}
else
{
tmp0 = q1.X;
tmp1 = q1.Y;
tmp2 = q1.Z;
tmp3 = q1.W;
}
/* calc coeffs */
double scale0, scale1;
if ((1.0 - cosom) > double.Epsilon)
{
// standard case (slerp)
double omega = Math.Acos(cosom);
double t1 = omega * 180 / Math.PI;//可以从这看出起始点和终点直接的角度。
double sinom = Math.Sin(omega);
scale0 = Math.Sin((1.0 - t) * omega) / sinom;
scale1 = Math.Sin(t * omega) / sinom;
}
else
{
/* just lerp */
scale0 = 1.0 - t;
scale1 = t;
}
Quaternion4d q = new Quaternion4d();
q.X = scale0 * q0.X + scale1 * tmp0;
q.Y = scale0 * q0.Y + scale1 * tmp1;
q.Z = scale0 * q0.Z + scale1 * tmp2;
q.W = scale0 * q0.W + scale1 * tmp3;
return q;
}
另外,采用不同的球面一点如何转换为四元数方式会产生不同的效果。欧拉角转四元数的方式,如果把
ψ设为0,那么跟笛卡尔转欧拉角的效果差不多,生成的圆弧半径是球的半径。但是如果
ψ!=0,那么会导致斜的圆弧。下面配两幅图简单的展示下。
参考文档
[0]
kUANGtOBY的日记
http://www.douban.com/note/206638855/
[2] 四元数与欧拉角之间的转换 http://www.cnblogs.com/wqj1212/archive/2010/11/21/1883033.html