Как рассчитать поворот при использовании камеры кватерниона? - PullRequest
2 голосов
/ 03 января 2012

Учитывая кватернионную камеру, как я могу рассчитать ее вращение, когда игрок может ходить по разным нормам поверхности (стенам).

Я работаю над игрой, которая позволяет игроку ходить по потолкам и стенам в трехмерном пространстве. Я решил использовать систему камер кватернионов, чтобы избежать Gimbal Lock. При заданном наборе векторов up (0,1,0), правого (1,0,0) и прямого (0,0,1) я строю кватернион. Игрок вращается вокруг вектора вверх для направления и вокруг правого вектора для шага.

Без изменения векторов гравитации камера работает нормально и позволяет игроку перемещаться по среде, как если бы это была стандартная игра FPS.

Для простоты, скажем, игрок может нажать клавишу, которая захватывает нормаль с ближайшей поверхности столкновения, которая отличается от их текущей нормали, и назначает ее в качестве нового вектора гравитации.

К сожалению, я столкнулся с мозговым блоком и не могу понять, как правильно получить новые векторы вверх, вправо и вперед из этого нового вектора гравитации и применить их к текущему кватерниону вращения, или даже если это правильный способ решения проблемы. Если это поможет, код движения и поворота для моей камеры ниже.

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

1 Ответ

2 голосов
/ 04 января 2012

Хорошо, я нашел решение, которое хорошо работает, и оказалось, что я думал об этом неправильно. Единственный вектор, который нужно изменить, это вектор повышения, остальные два вектора, прямой и правый, должны оставаться неизменными. Следующий код выровняет текущий кватернион с новым вектором вверх. Хитрость заключалась в том, что для создания нового кватерниона вы используете квадрат точечного произведения между текущим вектором роста и новым вектором роста и вектором кросса между ними Затем вы умножаете это на ваш текущий курс и переходите между ними. Не забудьте установить свой новый вектор вверх потом. Вы можете использовать любое значение, которое вы хотите в slerp, чтобы сделать его более плавным, 1.0 делает переход мгновенно.

        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
...