2012-01-03 9 views
2

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

Я работаю над игрой, которая позволяет игроку ходить по потолкам и стенам в трехмерном пространстве. Я решил использовать систему кватернионов, чтобы избежать Gimbal Lock. Учитывая набор векторов up (0,1,0), right (1,0,0) и forward (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 

ответ

2

Хорошо, поэтому я нашел решение, которое хорошо работает, и, оказывается, я думал об этом неправильно. Единственным вектором, который нужно изменить, является вектор вверх, остальные два вектора, вперед и вправо, должны оставаться неизменными. Следующий код выравнивает текущий кватернион с новым вектором вверх. Фокус в том, что вы используете квадрат точечного произведения между текущим вектором вверх и новым вектором вверх и поперечным вектором между этими двумя, чтобы создать новый кватернион. Затем вы умножаете это на свой текущий заголовок и slerp между ними. Не забудьте впоследствии установить новый вектор. Вы можете использовать любое значение, которое вы хотите в 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 
Смежные вопросы