如何使用四元数相机计算旋转? [英] How can I calculate the rotation when using a quaternion camera?

查看:396
本文介绍了如何使用四元数相机计算旋转?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

给定一个四元数相机,当玩家可以在不同的表面法线(墙壁)上行走时,如何计算它的旋转。

Given a quaternion camera, how can I calculate its rotation when the player can walk on different surface normals (walls).

允许玩家在3D空间中的天花板和墙壁上行走。我选择使用四元数相机系统,以避免云台锁。给定一组up(0,1,0),right(1,0,0)和forward(0,0,1)向量,我构造一个四元数。

I'm working on a game which allows the player to walk on ceilings and walls in 3D space. I've opted to use a quaternion camera system to avoid Gimbal Lock. Given a set of up(0,1,0), right(1,0,0), and forward(0,0,1) vectors, I construct a quaternion. The player rotates around the up vector for heading and around the right vector for pitch.

在不改变重力矢量的情况下,相机工作正常,允许玩家围绕环境移动就像是一个标准的FPS游戏。

Without changing gravity vectors, the camera works fine and allows the player to move about the environment as if it were a standard FPS game.

为了简单起见,玩家可以按一个键,从最近的碰撞面抓取法线, ,并将其指定为新的重力矢量。

For simplicity, let's say the player can press a key which grabs the normal from the nearest collision surface that differs from their current normal, and assigns that as the new gravity vector.

不幸的是,我遇到了一个脑块,不知道如何正确地获得新的,从这个新的重力矢量向前向量,并将它们应用到当前旋转四元数,或者即使这是解决问题的正确方法。

Unfortunately, I've run into a brainblock, and cannot figure out how to properly get the new up, right, and forward vectors from this new gravity vector and apply them to the current rotation quaternion, or even if that is the correct way to tackle the problem. If it helps, the movement and rotation code for my camera is below.

Field forwardVector:TVector = TVector.Create(0,0,1)
Field rightVector:TVector = TVector.Create(1,0,0)
Field upVector:TVector = TVector.Create(0,1,0)

Field pos:TVector = New TVector
Field headingQuaternion:TQuaternion = TQuaternion.Create()
Field pitchQuaternion:TQuaternion = TQuaternion.Create()
Field combinedRotation:TQuaternion = TQuaternion.Create()
Field gravityVector:TVector = TVector.Create(0,1,0)

'---------
'ChangeGravityVector
'---------
Method ChangeGravityVector( newGravityVector:TVector )

    gravityVector = newGravityVector

End Method

'---------
'MoveForward
'---------
Method MoveForward( moveAmount:Float, noGravity:Byte = False )

    Local vecRot:TVector

    If( noGravity = True )

        headingQuaternion.MultiplyByVector( forwardVector )
        vecRot = combinedRotation.MultiplyByVector( forwardVector )

    Else

        vecRot = headingQuaternion.MultiplyByVector( forwardVector )

    EndIf

    vecRot.ScaleVector( moveAmount )
    pos.AddVector( vecRot )

End Method

'---------
'MoveUp
'---------
Method MoveUp( moveAmount:Float, noGravity:Byte = False  )

    Local vecRot:TVector

    If( noGravity = True )

        headingQuaternion.MultiplyByVector( gravityVector )
        vecRot = combinedRotation.MultiplyByVector( gravityVector )

    Else

        vecRot = headingQuaternion.MultiplyByVector( gravityVector )

    EndIf

    vecRot.ScaleVector( moveAmount )
    pos.AddVector( vecRot )

End Method

'---------
'MoveRight
'---------
Method MoveRight( moveAmount:Float, noGravity:Byte = False  )

    Local vecRot:TVector

    If( noGravity = True )

        headingQuaternion.MultiplyByVector( rightVector )
        vecRot = combinedRotation.MultiplyByVector( rightVector )

    Else

        vecRot = headingQuaternion.MultiplyByVector( rightVector )

    EndIf

    vecRot.ScaleVector( moveAmount )
    pos.AddVector( vecRot )

End Method

'---------
'RotateX
'---------
Method RotateX( rotateAmount:Float )

    Local xRotQuat:TQuaternion = TQuaternion.Create()
    xRotQuat.ConvertFromAxisAngle( rightVector, rotateAmount )
    pitchQuaternion = pitchQuaternion.MultiplyByQuaternion( xRotQuat )

End Method

'---------
'RotateY
'---------
Method RotateY( rotateAmount:Float )

    Local yRotQuat:TQuaternion = TQuaternion.Create()
    yRotQuat.ConvertFromAxisAngle( gravityVector, rotateAmount )
    headingQuaternion = yRotQuat.MultiplyByQuaternion( headingQuaternion )

End Method

'---------
'GetCameraMatrix
'---------
Method GetCameraMatrix:TMatrix4x4()

    combinedRotation = headingQuaternion.MultiplyByQuaternion( pitchQuaternion )
    Return combinedRotation.GetMatrix()

End Method


推荐答案

好的,所以我找到了一个解决方案,事实证明,我在想它是错的。唯一需要改变的向量是向上向量,其他两个向量,向前和向右,应该保持不变。以下代码将使当前四元数与新的向上向量对齐。诀窍是你使用当前上行向量和新上行向量之间的点积的平方和这两个向量之间的交叉向量来创建一个新的四元数。然后你乘以你的当前标题和slerp之间的两个。记住要设置新的向上向量。

Ok, so I found a solution that works well, and it turns out, I was thinking about it wrong. The only vector that needs to change is the up vector, the others two vectors, forward and right, should stay the same. The following code will align the current quaternion with a new up vector. The trick was that you use the square of the dot product between the current up vector and new up vector and the cross vector between those two to create a new quaternion. You then multiply that by your current heading and slerp between the two. Remember to set your new up vector afterwards. You can use any value you want in the slerp to make it smoother, 1.0 makes the transition instantly.

        Local newQuat:TQuaternion = New TQuaternion
        Local cross:TVector = upVector.GetCrossProduct( newGravityVector )
        Local dot:Float = upVector.GetDotProduct( newGravityVector )
        Local dotSquare:Float = Sqr( ( 1.0 + dot ) * 2.0 )
        Local scale:Float = 1.0/dotSquare
        newQuat.x = cross.x*scale
        newQuat.y = cross.y*scale
        newQuat.z = cross.z*scale
        newQuat.w = dotSquare*0.5

        newQuat = newQuat.MultiplyByQuaternion( headingQuaternion )

        headingQuaternion = headingQuaternion.Slerp( headingQuaternion, newQuat, 1.0 )

        gravityVector = newGravityVector
        upVector = newGravityVector

这篇关于如何使用四元数相机计算旋转?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆