限制相机间距

时间:2010-08-09 15:55:05

标签: math 3d camera quaternions

当我只有相机四元数时,如何才能有效地限制相机间距?我是否必须转换为欧拉角然后再转换为四元数或者还有其他方法吗?

4 个答案:

答案 0 :(得分:2)

如果相机从未有任何滚动(这在许多游戏中很常见,例如第一人称射击游戏),那么解决方案很简单。如果有滚动,那么还有一个额外的步骤。如果没有滚动,我将从做什么开始,并将解决方案概括为如果存在该怎么做。

设qc为相机旋转。让qy以与qc相同的偏航旋转,但是零间距。如果没有滚动,则摄像机旋转是偏航旋转,然后是俯仰旋转:

qc = qp * qy

我们可以将音高旋转qp恢复为从qy到qc的旋转:

qp = qc * qy^-1

然后,诀窍是构造qy,所以我们可以将它插入上面的等式来解决qp。设vc是指向摄像机镜头外的单位矢量,或“前向矢量”。设vy是相同的向量,但投影到水平面并进行归一化。最后,当摄像机旋转qc是身份旋转时,让v0成为前向矢量。将v0旋转到vy的旋转是偏航旋转。角度可以表示为:

yaw = asin(Norm(cross(v0, vy)))

相应的偏航旋转是:

qy = { cos(yaw/2), up * sin(yaw/2) }

其中“向上”是向上方向的单位向量,也就是偏航旋转的轴。将其插入上面的qp = qy ^ -1 * qc以获得音高四元数qp。最后,从qp获得俯仰角:

pitch = 2*asin(Dot(right, [qp[1], qp[2], qp[3]]))

其中“right”是右方向的单位向量,也就是俯仰旋转的轴。

就像我说的,如果相机也有滚动,事情会变得更复杂,但总的策略是一样的。您将相机旋转表示为旋转组件的乘积,然后隔离所需的组件(在本例中为音高)。例如,如果用于定义“pitch”的euler序列是常见的偏航 - 俯仰 - 滚动序列,则将qc定义为:

qc = qr * qp * qy

我们可以将变量qx定义为组合的俯仰和滚转:

qx = qr * qp

我们现在可以将qc写成:

qc = qx * qy

我们已经知道如何通过回溯上面用于解决qp的步骤来解决这种形式的qx问题。重新排列qx的定义,我们得到:

qp = qr^-1 * qx

我们刚刚解决了qx,所以为了求解音高旋转qp,我们只需要滚动qr。我们可以像以前一样使用向量构建它。让vc再次成为前向向量。滚动将围绕此向量旋转。让vu成为摄像机的向上矢量(在世界坐标中),让vu0成为摄像机的向上矢量,零滚动。我们可以通过将全局向上矢量投影到垂直于vc的平面来构造vu0,然后进行归一化。然后,滚动旋转qr是从vu0到vu的旋转。该旋转的轴是前向矢量vc。滚动角度

roll = asin(Dot(vc, cross(vu0, vu)))

相应的四元数是:

qr = { cos(roll/2), forward * sin(roll/2) }

“向前”是滚转轴。

答案 1 :(得分:1)

音高只是完整旋转的一个组成部分,所以如果你想要考虑你的旋转,你最好分开存储音高,可能使用欧拉角。

当你需要限制运动时,转换到欧拉角度并返回可能不会很好,因为你需要记住最后一帧(如果你有的话),看你是否超过限制,以及方向。

正如我所看到的,四元数的主要观点是你不需要为这样的限制而烦恼。一切正常,没有任何奇点。为什么要限制音高?

答案 2 :(得分:0)

相机旋转四元数可以定义为:

vector A = [x, y, z] 
Q.x = A.x * sin(theta/2)
Q.y = A.y * sin(theta/2)
Q.z = A.z * sin(theta/2)
Q.w = cos(theta/2)

其中A是位置,θ是您想要旋转相机的角度(调整音高)。

因此,如果您有四元数,则可以通过每次验证旋转角度加/减实际角度是否正确来限制俯仰角度。

如果您将限制设置为

,我认为您可以避免转换
+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)

因为余弦是一个连续的函数,所以它应该起作用。

如果您可以发布您实际使用的代码,我们可以提供更多帮助。

答案 3 :(得分:0)

我可能会晚一点,但这就是我解决它的方式:

        // "Up" = local vector -> rotation * Vector3.UnitY
        // "Forward" = local vector -> rotation * Vector3.UnitZ
        // "Right" = local vector -> rotation * Vector3.UnitX

    public void Rotate(Vector3 axis, float angle)
    {
        if (LerpRotation)
        {
            RotationTarget *= Quaternion.FromAxisAngle(axis, angle);
        }
        else
        {
            Rotation *= Quaternion.FromAxisAngle(axis, angle);
        }
        //Locking the Pitch in 180°
        float a = Vector3.CalculateAngle(Vector3.UnitY, Up);
        float sign = Math.Sign(Forward.Y);
        float delta = (float)Math.PI / 2 - a;
        if(delta < 0)
            Rotation *= Quaternion.FromAxisAngle(Right, delta * sign);
    }