XNA 3.0 Game Programming Recipes翻译2-4

2-4.创建自由式相机:使用四元数的全3D旋转

问题

        你想要创建一个可以任意旋转的相机,例如飞行游戏。你需要绕三个轴旋转来做到,由于 万向节锁h的限制,也不是不可能,但很难。

 

方案

       由于万向节锁,结合多个轴的几次旋转,会导致不正确的结果。用四元数储存相机的旋转能帮你解决这个问题。

 

如何运作

当你结合绕不同轴的旋转,万向节锁就会发生。因为第一次的旋转把第二次旋转的轴也改变了,第三次旋转的轴被前两次旋转改变了。很难想象最后会发生什么。

旋转的特性是始终存在一个单一的旋转,它与多次旋转组合的结果相同。因此,这里需要的技巧是定义你的相机只会绕着单轴旋转。这可以是任何轴:不必是XYZ之一。这个轴和旋转角度将储存在一个叫四元数的变量。

问题是你如何计算这个轴?不用。它会在处理过程中自动创建。你只需指定起始轴,每次用户移动鼠标,这个轴将更新。

你把向上向量作为起始轴,旋转0度,所以你看着正前方。接下来,有用户输入,你想相机绕向右的向量旋转。向右向量首先获取当前相机的旋转来转动,而因为这角度是0,所以保持不变。向右向量称为当前旋转轴。相机围着这个轴转,并有点仰视。

接下来用户输入要相机沿其前进轴旋转。这个轴首先沿着当前相机的旋转来旋转,现在不是0而包含绕向右轴的旋转。每个旋转结合,单轴旋转的结果和旋转的角度存储在相机的四元数。这个旋转称为相机的当前旋转。

每次用户输入,相同的程序发生:前///下向量依据相机当前旋转轴旋转,组合新旋转,当前相机存储新相机旋转到四元数,之后相机绕着新旋转轴旋转。

幸运的是,整个过程变成很简单的代码。刚开始,你要允许只绕一个单一的轴旋转(向右的轴)

 

float updownRotation = 0.0f;

KeyBoardState keys = Keyboard.GetState();

if(key.IsKeyDown(Keys.Up))

    updownRotation = 1.5f;

if(key.IsKeyDown(Keys.Down))

    updownRotation = -1.5f;

Quaternion additionalRotation = Quaternion.CreateFromAxisAngle(new Vector3(1,0,0),updownRotation);

cameraRotation = cameraRotation * additionalRotation;

 

首先,用户输入决定是否上下旋转。接下来,新的四元数被创建来持有这个轴和绕这个轴旋转的数量。最后,新旋转和当前旋转结合,结果存储为新旋转。

 

注意 四元数这字眼常使程序员害怕。我遇到了许多文章和网站把四元数比作黑暗魔法,是无法理解或抽象化的。但这不完全正确。四元数用来存储绕一个轴的旋转,所以他应该可以储存这个轴和绕着它旋转的角度。一个轴由3个数字定义,角度可以由一个数字定义。所以你要存储这样一个旋转,最简单的就是存储4个数字。猜一下四元数实际上是什么?很简单,4个数字。没什么神奇。尽管背后的数学原理比较神奇。

 

        你想任意旋转相机,你需要可以沿着第二个轴旋转。这个旋转存储在additionalRotation变量,绕第一个轴的旋转乘以绕第二个轴的旋转:

 

        float updownRotation = 0.0f;

        float leftrightRotation = 0.0f;

        KeyboardState keys = Keyboard.GetState();

if(key.IsKeyDown(Keys.Up))

    updownRotation = 0.05f;

if(key.IsKeyDown(Keys.Down))

    updownRotation = -0.05f;

if(key.IsKeyDown(Keys.Right))

    leftrightRotation = -0.05f;

if(key.IsKeyDown(Keys.Left))

leftrightRotation = 0.05f;

 

Quaternin additionalRotatin =

Quaternion.CreateFromAxisAngle(new Vector3(1,0,0),updownRotation) *

Quaternion.CreateFromAxisAngle(new Vector3(0,1,0),leftrightRotation);

cameraRotation = cameraRotation * additionalRotation;

 

注意 四元数乘法像矩阵乘法一样重要。见4-2。你在用四元数乘法时要有这个想法。四元数的规则和矩阵乘法类似,但当两个旋转轴是互相垂直时例外,当你计算additionalRotation变量时。这意味着无论你先绕那个轴,结果都一样。但通常轴没有互相垂直。因此,你写cameraRotation * additionalRotation很重要

 

注意 矩阵乘法中,*意味着‘先*后’。事情本来可以很容易,在四元数中,*意味着 *cameraRotation = cameraRotation * additionalRotation 应该是先additionalRotationcameraRotation,表示轴储存在additionalRotation变量的轴要先绕着cameraRotatin变量中存储的轴旋转。

 

        一旦旋转矩阵被找到,视图矩阵可以被构造:

 

       private void UpdateViewMatrix()

        {

            Vector3 cameraOriginalTarget = new Vector3(0, 0, -1);

            Vector3 cameraOriginalUpVector = new Vector3(0, 1, 0);

 

            Vector3 cameraRotatedTarget = Vector3.Transform(cameraOriginalTarget, cameraRotation);

            Vector3 cameraFinalTarget = cameraPosition + cameraRotatedTarget;

 

            Vector3 cameraRotatedUpVector = Vector3.Transform(cameraOriginalUpVector, cameraRotation);

 

            viewMatrix = Matrix.CreateLookAt(cameraPosition, cameraFinalTarget, cameraRotatedUpVector);

        }

        

         移动相机,当用户按下方向键,先旋转移动向量,把变化后的向量加上当前相机位置:

 

float moveSpeed = 0.05f;

Vector3 rotatedVector = Vector3.Transform(vectorToAdd, cameraRotation);

cameraPosition += moveSpeed * rotatedVector;

UpdateViewMatrix();

 

扩展阅读

    相机跟随物体

你可以用本章代码很容易创建一个太空游戏,相机定位在航天器的驾驶舱内。但是,如果你想把相机定位在航天器后面,你需要其他代码。现在,位置和旋转对应航天器,而不是相机。相机的位置基于航天器的位置。航天器可以任意旋转。如果你把相机定位在航天器后面,相机会看着航天器。所以你已经知道视图矩阵的目标:航天器的位置。向上向量和航天器相同。航天器转,则相机也转。你要定位相机在航天器后,通常比航天器的向量后一点,比如航天器的位置加上(0,0,1)。这样就可以了。

转载于:https://www.cnblogs.com/XNAconglele/archive/2009/08/26/1554038.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值