限制相机间距

发布于 2024-09-13 17:32:32 字数 62 浏览 1 评论 0原文

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

How can I efficiently limit camera pitch when I have only camera quaternion? Do I have to convert to euler angles and then back to quaternion or is there any other way?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

睫毛溺水了 2024-09-20 17:32:32

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

令 qc 为相机旋转。让 qy 进行与 qc 相同偏航但零螺距的旋转。如果没有滚动,相机旋转是偏航旋转,然后是俯仰旋转:

qc = qp * qy

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

qp = qc * qy^-1

那么,技巧就是构造 qy,因此我们可以将其插入上面的方程求解 qp。令 vc 为指向相机镜头的单位矢量,或“前向矢量”。令 vy 为相同的向量,但投影到水平面并进行归一化。最后,令v0为当相机旋转qc为恒等旋转时的前向向量。将 v0 旋转为 vy 的旋转就是偏航旋转。该角度可以给出为:

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

相应的偏航旋转为:

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

其中“up”是向上方向的单位矢量,也称为偏航旋转的轴。将其代入上面的 qp = qy^-1 * qc 即可得到音高四元数 qp。最后,从 qp 获取俯仰角,如下所示:

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

其中“right”是正确方向的单位向量,也称为俯仰旋转轴。

就像我说的,如果相机也有滚动,事情会变得更加复杂,但总体策略是相同的。您将相机旋转表示为旋转分量的乘积,然后隔离所需的分量(在本例中为俯仰)。例如,如果用于定义“俯仰”的欧拉序列是常见的偏航-俯仰-横滚序列,则将 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) }

其中“向前”是横滚旋转轴。

If the camera never has any roll (as is common in lots of games, such as first person shooters), then the solution is simple. If there is roll, then there's an additional step involved. I'll start with what to do if there is no roll, and generalize the solution to what to do if there is.

Let qc be the camera rotation. Let qy a rotation with the same yaw as qc, but with zero pitch. If there is no roll, the camera rotation is a yaw rotation followed by a pitch rotation:

qc = qp * qy

We can recover the pitch rotation qp as the rotation from qy to qc:

qp = qc * qy^-1

The trick, then, is to construct qy, so we can plug it into the above equation to solve for qp. Let vc be the unit vector pointing out of the lens of the camera, or the "forward vector". Let vy be the same vector, but projected to the horizontal plane and normalized. Finally, let v0 be the forward vector when the camera rotation qc is the identity rotation. The rotation that rotates v0 into vy is the yaw rotation. The angle can be given as:

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

The corresponding yaw rotation is:

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

Where "up" is the unit vector in the up direction, aka the axis for yaw rotations. Plug this into qp = qy^-1 * qc above to get pitch quaternion qp. Finally, get the pitch angle from qp as:

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

Where "right" is the unit vector in the right direction, aka the axis for pitch rotations.

Like I said, things get more complicated if the camera also has roll, but the general strategy is the same. You formulate the camera rotation as a product of rotation components, then isolate the component you want (in this case, pitch). For example, if the euler sequence you use to define "pitch" is the common yaw-pitch-roll sequence, you define qc as:

qc = qr * qp * qy

We can define a variable qx to be the combined pitch and roll rotations:

qx = qr * qp

We can now write qc as:

qc = qx * qy

We already know how to solve for qx in this form, by retracing the steps we used above to solve for qp. Rearranging the definition for qx, we get:

qp = qr^-1 * qx

We just solved for qx, so to solve for the pitch rotation qp, we only need the roll qr. We can construct it using vectors as we did previously. Let vc be the forward vector again. The roll will be a rotation around this vector. Let vu be the camera's up vector (in world coordinates), and let vu0 be the camera's up vector with zero roll. We can construct vu0 by projecting the global up vector to the plane perpendicular to vc, then normalizing. The roll rotation qr is then the rotation from vu0 to vu. The axis of this rotation is the forward vector vc. The roll angle is

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

The corresponding quaternion is:

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

Where "forward" is the axis of roll rotations.

帅哥哥的热头脑 2024-09-20 17:32:32

俯仰只是完整旋转的一个组成部分,因此如果您想像这样考虑旋转,最好单独存储俯仰,可能使用欧拉角。

当您需要限制运动时,仅转换为欧拉角并返回可能效果不太好,因为您需要记住最后一帧(如果有的话)以查看是否超过了限制以及在哪个方向上。

在我看来,四元数的要点是你不需要担心这样的限制。一切都正常进行,没有任何奇点。你到底为什么要限制音高?

The pitch is just one component of the full rotation, so if you want to think about your rotation like that, you'd better store the pitch separately, possibly using Euler angles.

Just converting to Euler angles and back when you need to limit the movement might not work too well, since you'l need to remember the last frame (if you have any) to see if you passed the limit, and in what direction.

As I see it, the main point of quaternions is that you don't need to bother with limits like that. Everything just works without any singularities. Why exactly do you want to limit the pitch?

街角卖回忆 2024-09-20 17:32:32

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

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是位置,theta是你想要旋转相机的角度(调整俯仰)。

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

我认为如果您设置限制,则可以避免转换,因为

+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)

余弦是一个连续函数,这应该有效。

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

Camera rotation quaternions can be defined as:

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)

Where A is the position and theta is the angle you want to rotate the camera (adjust the pitch).

So if you have the quaternion available you can limit the angle of pitch by verifying each time if the rotation angle plus/minus the actual angle is ok.

I think you can avoid conversion if you set your limits as

+cos(supLim/2) < (Q.w + P.w) < -cos(infLim/2)

As cosine is an continuous function this is supposed to work.

If you could post the code you're actually using maybe we can help a little more.

万劫不复 2024-09-20 17:32:32

我可能来晚了一点,但这就是我解决它的方法:

        // "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);
    }

I may be a little late to the party, but this is how I solved it:

        // "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);
    }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文