I've been trying to emulate gluLookAt functionality, but with Quaternions. Each of my game object have a TranslationComponent. This component stores the object's position (glm::vec3), rotation (glm::quat) and scale (glm::vec3). The camera calculates its position each tick doing the following:
// UP = glm::vec3(0,1,0);
//FORWARD = glm::vec3(0,0,1);
cameraPosition = playerPosition - (UP * distanceUP) - (FORWARD * distanceAway);
This position code works as expexted, the camera is place 3 metres behind the player and 1 metre up. Now, the camera's Quaternion is set to the follow:
//Looking at the player's feet
cameraRotation = quatFromToRotation(FORWARD, playerPosition);
The rendering engine now takes these values and generates the ViewMatrix (camera) and the ModelMatrix (player) and then renders the scene. The code looks like this:
glm::mat4 viewTranslationMatrix =
glm::translate(glm::mat4(1.0f), cameraTransform->getPosition());
glm::mat4 viewScaleMatrix =
glm::scale(glm::mat4(1.0f), cameraTransform->getScale());
glm::mat4 viewRotationMatrix =
glm::mat4_cast(cameraTransform->getRotation());
viewMatrix = viewTranslationMatrix * viewRotationMatrix * viewScaleMatrix;
quatFromToRotation(glm::vec3 from, glm::vec3 to) is defined as the following:
glm::quat quatFromToRotation(glm::vec3 from, glm::vec3 to)
{
from = glm::normalize(from); to = glm::normalize(to);
float cosTheta = glm::dot(from, to);
glm::vec3 rotationAxis;
if (cosTheta < -1 + 0.001f)
{
rotationAxis = glm::cross(glm::vec3(0.0f, 0.0f, 1.0f), from);
if (glm::length2(rotationAxis) < 0.01f)
rotationAxis = glm::cross(glm::vec3(1.0f, 0.0f, 0.0f), from);
rotationAxis = glm::normalize(rotationAxis);
return glm::angleAxis(180.0f, rotationAxis);
}
rotationAxis = glm::cross(from, to);
float s = sqrt((1.0f + cosTheta) * 2.0f);
float invis = 1.0f / s;
return glm::quat(
s * 0.5f,
rotationAxis.x * invis,
rotationAxis.y * invis,
rotationAxis.z * invis
);
}
What I'm having troubles with is the fact the cameraRotation isn't being set correctly. No matter where the player is, the camera's forward is always (0,0,-1)
Your problem is in the line
//Looking at the player's feet
cameraRotation = quatToFromRotation(FORWARD, playerPosition);
You need to look from the camera position to the player's feet - not from "one meter above the player" (assuming the player is at (0,0,0) when you initially do this). Replace FORWARD with cameraPosition:
cameraRotation = quatToFromRotation(cameraPosition, playerPosition);
EDIT I believe you have an error in your quatToFromRotation function as well. See https://stackoverflow.com/a/11741520/1967396 for a very nice explanation (and some pseudo code) of quaternion rotation.
Related
(See image at bottom)
Most tutorials for OpenGL use a first person view and Y faces upwards.
However, I am trying to have the camera focus on a specific point and have z face upwards (like CAD software)
To achieve this, I have tried to swap Y and Z to calculate the position:
pos.x = cos(m_Yaw) * cos(m_Pitch);
pos.y = sin(m_Yaw) * cos(m_Pitch); // This was Z
pos.z = sin(m_Pitch); // This was Y
m_Position = (m_Zoom * pos) + m_Centre;
The Up vector is then calculated:
m_WorldUp = glm::vec3(0.0f, 0.0f, 1.0f);
m_Front = glm::normalize(pos);
m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp));
m_Up = glm::normalize(glm::cross(m_Right, m_Front));
The View Matrix is calculated with:
m_ViewMatrix = glm::lookAt(m_Position, m_Centre, m_Up);
The Projection Matrix is calculated with (although I don't think this is relavant):
m_ProjMatrix = glm::perspective(m_FOV, m_ScreenW / m_ScreenH, m_Near, m_Far);
When m_Centre is not at the origin, it can look distorted at certain angles:
See Image
If you rotate your head 90 degrees, this seems to look correct...
What can I do differently?
I'm trying to get the coordinates (x,y) of the grid (z = 0) using only the cursor coordinates. After a long search I found this way to do that using the glm::unproject.
First I'm getting the cursor coordinates using the callback:
void cursorCallback(GLFWwindow *window, double x, double y)
{
this->cursorCoordinate = glm::vec3(x, (this->windowHeight - y - 1.0f), 0.0f);
}
an then converting these coordinates:
glm::vec3 cursorCoordinatesToWorldCoordinates()
{
glm::vec3 pointInitial = glm::unProject(
glm::vec3(this->cursorCoordinate.x, this->cursorCoordinate.y, 0.0),
this->modelMatrix * this->viewMatrix,
this->projectionMatrix,
this->viewPort
);
glm::vec3 pointFinal = glm::unProject(
glm::vec3(this->cursorCoordinate.x, this->cursorCoordinate.y, 1.0),
this->modelMatrix * this->viewMatrix,
this->projectionMatrix,
this->viewPort
);
glm::vec3 vectorDirector = pointFinal - pointInitial;
double lambda = (-pointInitial.y) / vectorDirector.y;
double x = pointInitial.x + lambda * vectorDirector.x;
double y = pointInitial.z + lambda * vectorDirector.z;
return glm::vec3(x, y, 0.0f);
}
I use an ArcBall camera to rotate the world around specified axis, so that is how I generate the MVP matrixes:
this->position = glm::vec3(0.0f, 10.0f, 5.0f);
this->up = glm::vec3(0.0f, 1.0f, 0.0f);
this->lookAt = glm::vec3(0.0f, 0.0f, 0.0f);
this->fieldView = 99.0f;
this->farDistance = 100.0f;
this->nearDistance = 0.1f;
this->modelMatrix = glm::mat4(1.0f);
this->viewMatrix = glm::lookAt(this->position, this->lookAt, this->up) * glm::rotate(glm::degrees(this->rotationAngle) * this->dragSpeed, this->rotationAxis);
this->projectionMatrix = glm::perspective(glm::radians(this->fieldView), 1.0f, this->nearDistance, this->farDistance);
But something is going wrong because I'm not getting the right results. Look this print of the application:
each square is 1 unit, the cube is rendered at position (0, 0, 0). With rotationAngle = 0 when a put the cursor at (0,0), (1,1), (2,2), (3,3), (4,4), (5,5) I get (0, 5.7), (0.8, 6.4), (1.6, 6.9), (2.4, 7.6), (3.2, 8.2), (4.2, 8.8) respectivally. That's not expected.
Why y is delayed by 6 units?
It's necessary rotate the result cursorCoordinatesToWorldCoordinates based on rotationAngle isn't?
--
That I already did:
Checked if the viewport match with glViewport - OK
Checked the opengl coordinates (Y is up, not Z) - OK
You want to intersect the ray from glm::vec3(this->cursorCoordinate.x, this->cursorCoordinate.y, 0.0) to glm::vec3(this->cursorCoordinate.x, this->cursorCoordinate.y, 1.0) with the grid in world space, rather than model space (of the cuboid).
You've to skip this.modelMatrix:
glm::vec3 pointInitial = glm::unProject(
glm::vec3(this->cursorCoordinate.x, this->cursorCoordinate.y, 0.0),
this->viewMatrix,
this->projectionMatrix,
this->viewPort);
glm::vec3 pointFinal = glm::unProject(
glm::vec3(this->cursorCoordinate.x, this->cursorCoordinate.y, 1.0),
this->viewMatrix,
this->projectionMatrix,
this->viewPort);
In any case this->modelMatrix * this->viewMatrix is incorrect. If you eant to intersect the ray with an object in model space, then it has to be this->viewMatrix * this->modelMatrix. Matrix multiplication is not Commutative.
I'm trying to make a simple animation where an object rotates around the world origin in OpenGL using glm lib. My ideia is:
Send object to origin
Rotate it
Send back to original position
Make it look at what I want
Here's my implementation:
// Rotates object around point p
void rotate_about(float deltaTime, glm::vec3 p, bool ended) {
glm::vec3 axis = glm::vec3(0,1,0); //rotation axis
glm::mat4 scale_m = glm::scale(glm::mat4(1.0f), glm::vec3(scale, scale, scale)); //scale matrix
glm::mat4 rotation = getMatrix(Right, Up, Front, Position); //builds rotation matrix
rotation = glm::translate(rotation, p - Position );
rotation = glm::rotate(rotation, ROTATION_SPEED * deltaTime, axis);
rotation = glm::translate(rotation, Position - p );
Matrix = rotation * scale_m;
//look at point P
Front = glm::normalize(p - start_Position);
Right = glm::normalize(glm::cross(WorldUp, Front));
Up = glm::normalize(glm::cross(Right, Front));
if (ended == true) { //if last iteration of my animation: saves position
Position.x = Matrix[3][0];
Position.y = Matrix[3][1];
Position.z = Matrix[3][2];
}
}
getMatrix() simply returns a 4x4 matrix as:
| Right.x Right.y Right.z |
| Up.x Up.y Up.z |
| Front.x Front.y Front.z |
| Pos.x Pos.y Pos.z |
I'm using this image as reference:
As it is my model simply disappears when I start the animation. If I remove lines bellow "//look at point P" it rotates around the origin, but twitches every time my animation restarts. I'm guessing I'm losing or mixing informations I shouldn't somewhere.
How can I store my models Front/Right/Up information so I can rebuild its matrix from scratch?
First edit, this is the effect I'm having when I don't try to make my model look at the point P, in this case the origin. When I do try my model disappears. How can I make it look at where I want, and how can I get my models new Front/Right/Up vectors after I finish rotating it?
This is the code I ran in the gif above
Operations like glm::translate() or glm::roate() build a matrix by its parameters and multiply the input matrix by the new matrix
This means that
rotation = glm::translate(rotation, Position - p );
can be expressed as (pseudo code):
rotation = rotation * translation(Position - p);
Note, that the matrix multiplication has to be "read" from the left to the right. (See GLSL Programming/Vector and Matrix Operations)
The operation translate * rotate causes a rotation around the origin of the object:
The operation rotate * translate causes a rotation around the origin of the world:
The matrix glm::mat4 rotation (in the code of your question) is the current model matrix of your object.
It contains the position (translation) and the orientation of the object.
You want to rotate the object around the origin of the world.
To do so you have to create a matrix which contains the new rotation
glm::mat4 new_rot = glm::rotate(glm::mat4(1.0f), ROTATION_SPEED * deltaTime, axis);
Then you can calculate the final matrix as follows:
Matrix = new_rot * rotation * scale_m;
If you want to rotate an object around the a point p and the object should always face a point p, then all you need is the position of the object (start_position) and the rotation axis.
In your case the rotation axis is the up vector of the world.
glm::vec3 WorldUp( 0.0f, 1.0f, 0.0f );
glm::vec3 start_position = ...;
float scale = ...;
glm::vec3 p = ...;
Calculate the rotation matrix and the new (rotated) position
glm::mat4 rotate = glm::rotate(glm::mat4(1.0f), ROTATION_SPEED * deltaTime, WorldUp);
glm::vec4 pos_rot_h = rotate * glm::vec4( start_position - p, 1.0f );
glm::vec3 pos_rot = glm::vec3( pos_rot_h ) + p;
Calculate the direction in which the object should "look"
glm::vec3 Front = glm::normalize(p - pos_rot);
You can use your function getMatrix to setup the current orientation matrix of the object:
glm::vec3 Right = glm::normalize(glm::cross(WorldUp, Front));
glm::mat4 pos_look = getMatrix(Right, WorldUp, Front, pos_rot);
Calculate the model matrix:
glm::mat4 scale_m = glm::scale(glm::mat4(1.0f), glm::vec3(scale));
Matrix = pos_look * scale_m;
The final code may look like this:
glm::mat4 getMatrix(const glm::vec3 &X, const glm::vec3 &Y, const glm::vec3 &Z, const glm::vec3 &T)
{
return glm::mat4(
glm::vec4( X, 0.0f ),
glm::vec4( Y, 0.0f ),
glm::vec4( Z, 0.0f ),
glm::vec4( T, 1.0f ) );
}
void rotate_about(float deltaTime, glm::vec3 p, bool ended) {
glm::mat4 rotate = glm::rotate(glm::mat4(1.0f), ROTATION_SPEED * deltaTime, WorldUp);
glm::vec4 pos_rot_h = rotate * glm::vec4( start_position - p, 1.0f );
glm::vec3 pos_rot = glm::vec3( pos_rot_h ) + p;
glm::vec3 Front = glm::normalize(p - pos_rot);
glm::vec3 Right = glm::normalize(glm::cross(WorldUp, Front));
glm::mat4 pos_look = getMatrix(Right, WorldUp, Front, pos_rot);
glm::mat4 scale_m = glm::scale(glm::mat4(1.0f), glm::vec3(scale));
Matrix = pos_look * scale_m;
if ( ended == true )
Position = glm::vec3(Matrix[3]);
}
SOLUTION:
The problem was in this part:
rotation = glm::translate(rotation, p - Position );
rotation = glm::rotate(rotation, ROTATION_SPEED * deltaTime, axis);
rotation = glm::translate(rotation, Position - p );
if (ended == true) { //if last iteration of my animation: saves position
Position.x = Matrix[3][0];
Position.y = Matrix[3][1];
Position.z = Matrix[3][2];
}
Note that I was using the distance between world origin and model as the radius of the translation. However, after the animation ends I update the models Position, which changes the result of p - Position, i.e, the orbit radius. When this happen the model "twitches", because it lost rotation information.
I solved it by using a different variable for the orbit radius, and applying the translation on the z-axis of the model. When the translation is applied on the x-axis, the model - which faces the camera initially - will end up sideways to the origin. However, applying the translation on the z-axis will end up with the model either facing or backwards to the origin, depending on the signal.
How do i create a camera with a rotation on the z axis that can move in every direction? I have tried many things, this is my code:
glm::mat4 view = glm::mat4(1.0f);
glm::vec3 camRotInRadians = glm::vec3(toRadians(rot.x), toRadians(rot.y), toRadians(rot.z));
glm::vec3 direction = glm::vec3(
cos(camRotInRadians.x) * cos(camRotInRadians.y),
sin(camRotInRadians.x),
cos(camRotInRadians.x) * sin(camRotInRadians.y)
);
// Right vector
glm::vec3 right = glm::vec3(
cos(toRadians(rot.z)) * cos(toRadians(rot.y + 270.0f)),
sin(toRadians(rot.z)),
cos(toRadians(rot.z)) * sin(toRadians(rot.y + 270.0f))
);
glm::vec3 up = glm::cross(right, direction);
view = glm::lookAt(
pos - direction * glm::vec3(40.0f),
pos + direction,
up
);
I have an issue where my camera seems to orbit the origin. It makes me think that I have my matrix multiplication backwards. However it seems correct to me, and if I reverse it it sorts out the orbit issue, but I get an other issue where I think my translation is backwards.
glm::mat4 Camera::updateDelta(const float *positionVec3, const float *rotationVec3)
{
// Rotation Axis
const glm::vec3 xAxis(1.0f, 0.0f, 0.0f);
const glm::vec3 yAxis(0.0f, 1.0f, 0.0f);
const glm::vec3 zAxis(0.0f, 0.0f, 1.0f); // Should this be -1?
// Accumulate Rotations
m_rotation.x += rotationVec3[0]; // pitch
m_rotation.y += rotationVec3[1]; // yaw
m_rotation.z += rotationVec3[2]; // roll
// Calculate Rotation
glm::mat4 rotViewMat;
rotViewMat = glm::rotate(rotViewMat, m_rotation.x, xAxis);
rotViewMat = glm::rotate(rotViewMat, m_rotation.y, yAxis);
rotViewMat = glm::rotate(rotViewMat, m_rotation.z, zAxis);
// Updated direction vectors
m_forward = glm::vec3(rotViewMat[0][2], rotViewMat[1][2], rotViewMat[2][2]);
m_up = glm::vec3(rotViewMat[0][1], rotViewMat[1][1], rotViewMat[2][1]);
m_right = glm::vec3(rotViewMat[0][0], rotViewMat[1][0], rotViewMat[2][0]);
m_forward = glm::normalize(m_forward);
m_up = glm::normalize(m_up);
m_right = glm::normalize(m_right);
// Calculate Position
m_position += (m_forward * positionVec3[2]);
m_position += (m_up * positionVec3[1]);
m_position += (m_right * positionVec3[0]);
m_position += glm::vec3(positionVec3[0], positionVec3[1], positionVec3[2]);
glm::mat4 translateViewMat;
translateViewMat = glm::translate(translateViewMat, m_position);
// Calculate view matrix.
//m_viewMat = rotViewMat * translateViewMat;
m_viewMat = translateViewMat * rotViewMat;
// Return View Proj
return m_projMat * m_viewMat;
}
Everywhere else I do the matrix multiplication in reverse which gives me the correct answer, but this function seems to want the reverse.
When calculating an objects position in 3D space I do this
m_worldMat = transMat * rotMat * scale;
Which works as expected.
There seem to be a few wrong things with the code.
One: rotViewMat is used in the first rotate call before it is initialized. What value does it get initially? Is it a unit matrix?
Two: Rotation does not have the mathematical properties you seem to assume. Rotating around x, then around y, then around z (each at a constant velocity) is not the same as rotating around any axis ("orbiting"), it is a weird wobble. Because of subsequent rotations, your accumulated x rotation ("pitch") for example may actually be causing a movement in an entirely different direction (consider what happens to x when the accumulated y rotation is close to 90 deg). Another way of saying that is rotation is non-commutative, see:
https://physics.stackexchange.com/questions/48345/non-commutative-property-of-rotation and
https://physics.stackexchange.com/questions/10362/how-does-non-commutativity-lead-to-uncertainty/10368#10368.
See also: http://en.wikipedia.org/wiki/Rotation_matrix#Sequential_angles and http://en.wikipedia.org/wiki/Euler_angles. Since Euler angles (roll-pitch-yaw) are not vectors, it doesn't make sense to add to them a velocity vector.
What you probably want is this:
glm::mat4 Camera::updateDelta(const float *positionVec3, const float *axisVec3, const float angularVelocity)
{
...
glm::mat4 rotViewMat; // initialize to unit matrix
rotViewMat = glm::rotate(rotViewMat, angularVelocity, axisVec3);
...