C++ OpenGl Camera rotation using Quaternion issue
I am having a problem implementing quaternions for my camera. Im creating a 3D space shooter in OpenGL and I need to avoid gimbal lock. If I rotate in only one axis its fine but once I apply both the pitch and yaw they have a weird behavior. My rotation angle is using the input of my mouse.
Here I calculate my ViewMatrix and its updated every frame
Matrix4x4 camera::GetViewMat()
{
m_rotation.AddX((oInput.MousePosition.GetY() - oInput.PrevMousePosition.GetY()) * m_sensitivity * dt);
m_rotation.AddY((oInput.MousePosition.GetX() - oInput.PrevMousePosition.GetX()) * -m_sensitivity * dt);
Matrix4x4 oTranslateOrigin, oRotate, oView;
oView.SetIdentity();
//constructor creates a quaternion from an AxisAngle, the constructor will be shown below
Quaternions pitch = Quaternions(m_rotation.GetX() * Utility::DegToRad(), Vector3(1, 0, 0));
Quaternions yaw = Quaternions(m_rotation.GetY() * Utility::DegToRad(), Vector3(0, 1, 0));
//update orientation with the new quaternion times its current orientation
ori = ori * yaw * pitch;
//convert quaternion to matrix, also shown below
oRotate = ori.ToMatrix();
oTranslateOrigin.BuildTranslate(-m_camPosition.GetX(), -m_camPosition.GetY(), -m_camPosition.GetZ());
oView = oRotate * oTranslateOrigin;
return oView;
}
Initialize ProjectionMatrix
Matrix4x4 camera::GetProjMat(float fFieldOfViewY, float fAspectRatio, float fNearZ, float fFarZ)
{
// Transposed version of D3DXMatrixPerspectiveFovRH
float fYScale = 1 / tanf( fFieldOfViewY / 2.0f );
float fXScale = fYScale / fAspectRatio;
memset( &m_projMat, 0, sizeof( Matrix4x4 ) );
m_projMat.Set(0, 0, fXScale);
m_projMat.Set(1, 1, fYScale);
m_projMat.Set(2, 2, fFarZ / ( fNearZ - fFarZ ));
m_projMat.Set(2, 3, ( fNearZ * fFarZ ) / ( fNearZ - fFarZ ));
m_projMat.Set(3, 2, -1.0f);
return m_projMat;
}
this is one of the contructors that creates a quaternion to AxisAngle
Quaternions::Quaternions(float angle, Vector3& axis)
{
FromAxisAngle(angle, axis);
}
void Quaternions::FromAxisAngle(float angle, Vector3& axis)
{
float halfAngle = angle * ((float)PI/360.0f);
float sin = sinf(halfAngle);
this->w = cosf(halfAngle);
this->x = axis.GetX() * sin;
this->y = axis.GetY() * sin;
this->z = axis.GetZ() * sin;
}
Matrix4x4 Quaternions::ToMatrix()
{
Normalize();
Matrix4x4 mat;
mat.SetIdentity();
mat.Set(0, 0, 1.0f - 2*(this->y * this->y) - 2*(this->z * this->z));
mat.Set(0, 1, 2*(this->x*this->y) - 2*(this->w*this->z));
mat.Set(0, 2, 2*(this->x * this->z) + 2*(this->w * this->y));
mat.Set(1, 0, 2*(this->x * this->y) + 2*(this->w * this->z));
mat.Set(1, 1, 1.0f - 2*(this->x * this->x) - 2*(this->z * this->z));
mat.Set(1, 2, 2*(this->y * this->z) - 2*(this->w * this->x));
mat.Set(2, 0, 2*(this->x * this->z) - 2*(this->w * this->y));
mat.Set(2, 1, 2*(this->y * this->z) + 2*(this->w * this->x));
mat.Set(2, 2, 1.0f - 2*(this->x * this->x) - 2*(this->y * this->y));
return mat;
}
and that is about it that I am doing.
Can you guys lead me in the right direction?
Is it because this line:
ori = ori * yaw * pitch;
Is in the wrong order?
You can always rotate the camera matrix with openGL functions. Just make sure you have the camer matrix on the stack.
glRotate(angle, 1, 0, 0);
glRotate(angle, 0, 1, 0);
You can look at a point with this function:
void gluLookAt( GLdouble eyeX , GLdouble eyeY , GLdouble eyeZ ,
GLdouble centerX , GLdouble centerY , GLdouble centerZ ,
GLdouble upX , GLdouble upY , GLdouble upZ );
Where you need the eye position, target (center) position, and the up-vector of the camera.
Ok this is what you want to do to avoid gimble lock with quaternions. Plus ive included some Opengl specific code to hopeful head you in the right direction. Ok first things first I'm going to map keys to rotations.
W - Pitch up(x rot)
S - Pitch down(x rot)
A - Yaw left(y rot)
D - Yaw Right(y rot)
Q - Roll left(z rot)
E - Roll right(z rot)
All you want to is when these are pressed call the correct rotation. So for example the user presses W all we need to do is create a quaternion for with a rotation around the x with 1degrees. Do that using your quaternion class:
a = EulerToQuat(angle,0,0);
cameraOri = cameraOri * a;
Obviously do the same for the other controls! Ok final step you need to do is using the rotate commands in Opengl to rotate the world correctly. The way we do this which is nice and easy to read is we convert our quaternion to a angle and axis.(Hopefully your quaternion class can do this!) And then proceed to do a glRotate using this!
m_frustumCamera->getQuat().GetAxisAngle(axis,angle);
angle = (float)(angle * radToDeg);
glRotatef(angle,axis.x,axis.y,axis.z);
glTranslatef(-1*m_frustumCamera->getPos().x,-1*m_frustumCamera->getPos().y,-1*m_frustumCamera->getPos().z);
If you dont have a working quaternion class I recommend: http://www.wolframalpha.com/ to make sure all your functions give the correct response!
链接地址: http://www.djcxy.com/p/81758.html上一篇: 相机与四元数的轨道点