How to properly rotate and scale an object in OpenGL? - c++

I'm trying to build the model matrix every frame, for that I'm creating a translation, rotation, and scale matrix and multiplying it. But I can't seem to figure it how to build the rotation matrix and scale it properly.
Here's what I'm doing:
glm::mat4 scale = glm::scale(mat4(1.0f), my_models[i].myscale);
glm::mat4 rotateM(1.0);
glm:mat4 translate = glm::translate(mat4(1.0f), my_models[i].initialPos);
rotateM = mat4_cast(my_models[i].Quat);
rotateM = glm::rotate(rotateM, (float) my_models[i].angle * t, my_models[i].animation_axis[0]);
my_models[i].modelMatrix = translate * rotateM *scale;
my_models[i].Quat = quat_cast(my_models[i].modelMatrix);
In the Constructor I'm using:
quat Quat = glm::angleAxis(glm::radians(90.f), glm::vec3(0.f, 1.f, 0.f));
If my_models[i].myscale exactly 1.0f it rotates just fine, but if it is bigger the object keeps growing and rotates weirdly. Quaternions are very new to me, so I'm assuming I'm messing up there.
What am I missing? Are there simpler ways to construct the models rotation matrix? if so, what information should I be saving?
Edit:
As jparima suggested, the following fixed my problem.
glm::mat4 scale = glm::scale(mat4(1.0f), my_models[i].myscale);
glm::mat4 rotateM(1.0);
glm::mat4 translate = glm::translate(mat4(1.0f), my_models[i].initialPos);
my_models[i].Quat = rotate(my_models[i].Quat, my_models[i].angle * t, my_models[i].animation_axis[0]);
rotateM = mat4_cast(my_models[i].Quat);
my_models[i].modelMatrix = translate * rotateM * scale;

From the GLM quaternion.hpp for quat_cast it says
Converts a pure rotation 4 * 4 matrix to a quaternion.
You are setting the whole model matrix there which has also scale and translation. In fact, I don't know why you are converting a matrix to quaternion at all. Therefore you could remove the last line (quat_cast) and update the quaternion directly if you want to apply rotations.

Related

Is there any way to extract a transform matrix from a view matrix in glm?

I need to extract the transform matrix from my camera to assign it to a mesh.
I'm working in a computational graphics project in school, the objective is to simulate the arms of a character in first person perspective.
My camera implementation includes a vector3 for the camera position, so i can assign that to my mesh, the problem is that i can't extract the rotation of the camera from my view matrix yet.
I calculate my final pitch and yaw in the rotation function this way, x and y are the current mouse position in the screen
m_yaw += (x - m_mouseLastPosition.x) * m_rotateSpeed;
m_pitch -= (y - m_mouseLastPosition.y) * m_rotateSpeed;
This is how i update the view matrix when it changes
glm::vec3 newFront;
newFront.x = -cos(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
newFront.y = sin(glm::radians(m_yaw)) * cos(glm::radians(m_pitch));
newFront.z = sin(glm::radians(m_pitch));
m_front = glm::normalize(newFront);
m_right = glm::normalize(glm::cross(m_front, m_worldUp));
m_up = glm::normalize(glm::cross(m_right, m_front));
m_viewMatrix = glm::lookAt(m_position, (m_position + m_front), m_up);
Right now I can assign the position of the camera to my mesh, like this
m_mesh.m_transform = glm::translate(glm::mat4(1.0f), m_camera.m_position);
I can assign the camera position successfully, but not rotation.
What i expect is to assign the full camera transform to my mesh, or to extract the rotation independently and assign it to the mesh after.
The steps to setting up the model view projection matrix, that I have always followed (which doesn't mean it's 100% right), which appears to be what you are having problems with is:
// Eye position is in world coordinate system, as is scene_center. up_vector is normalized.
glm::dmat4 view = glm::lookat(eye_position, scene_center, up_vector);
glm::dmat4 proj = glm::perspective(field_of_view, aspect, near_x, far_x);
// This converts the model from it's units, to the units of the world coordinate system
glm::dmat4 model = glm::scale(glm::dmat4(1.0), glm::dvec3(1.0, 1.0, 1.0));
// Add model level rotations here utilizing glm::rotate
// offset is where the objects 0,0 should be mapped to in the world coordinate system
model = glm::translate(model, offset);
// Order of course matters here.
glm::dvec3 mvp = proj * view * model;
Hope that helps.
Thank you so much for your answers, they helped a lot.
I managed to solve my problem in a very simple way, i just had to directly assign the final transform to my mesh using the separate properties of my camera.
glm is so new to me, i wasn't familiar with the way it handles matrix multiplications.
The final code takes a translation matrix with the camera position, then i rotated the resulting matrix in the Y axis with my pitch and finaly the resulting one rotates in the X axis with my yaw.
m_mesh.m_transform = glm::rotate(glm::rotate(glm::translate(glm::mat4(1.0f), camera.m_position), glm::radians(-camera.m_yaw), glm::vec3(0.0f, 1.0f, 0.0f)), glm::radians(-camera.m_pitch), glm::vec3(1.0f, 0.0f, 0.0f));

object keep his distance around camera c++ opengl

I want to keep the object permanently at a certain distance from the camera. How i can made this? I tried this:
vec3 obj_pos = -cam->Get_CameraPos() ;
obj_pos .z -= 10.0f ;
...
o_modelMatrix = glm::translate(o_modelMatrix, obj_pos);
but it's not working; The object simply stands on the determined position and not moving
Full code of render:
void MasterRenderer::renderPlane() {
PlaneShader->useShaderProgram();
glm::mat4 o_modelMatrix;
glm::mat4 o_view = cam->Get_ViewMatrix();
glm::mat4 o_projection = glm::perspective(static_cast<GLfloat>(glm::radians(cam->Get_fov())),
static_cast<GLfloat>(WIDTH) / static_cast<GLfloat>(HEIGHT), 0.1f, 1000.0f);
glUniformMatrix4fv(glGetUniformLocation(PlaneShader->ShaderProgramID, "projection"), 1, GL_FALSE, glm::value_ptr(o_projection ));
glUniformMatrix4fv(glGetUniformLocation(PlaneShader->ShaderProgramID, "view"), 1, GL_FALSE, glm::value_ptr(o_view ));
vec3 eye_pos = vec3(o_view [3][0], o_view [3][1], o_view [3][2]); //or cam->getCameraPosition();
glm::vec3 losDirection = glm::normalize(vec3(0.0f, 0.0f, -1.0f) - eye_pos);
vec3 obj_pos = eye_pos + losDirection * 1.0f;
b_modelMatrix = scale(o_modelMatrix, vec3(20.0f));
b_modelMatrix = glm::translate(b_modelMatrix, obj_pos );
glUniformMatrix4fv(glGetUniformLocation(PlaneShader->ShaderProgramID,
"model"), 1, GL_FALSE, glm::value_ptr(o_modelMatrix));
...
/// draw
Maybe this is a shot from the hip, but I suppose that you set up a lookat matrix and that you the position of your object is defined in world coordinates.
Commonly a camera is defined by a eye position, at target (center) position and an up vector.
The direction in which the camera looks is the line of sight, which is the unit vector from the eye position to the target position.
Calcualte the line of sight:
glm::vec3 cameraPosition ...; // the eye position
glm::vec3 cameraTarget ...; // the traget (center) posiiton
glm::vec3 losDirection = glm::normalize( cameraTarget - cameraPosition );
Possibly the camera class knows the direction of view (line of sight), then you can skip this calculation.
If the object is always to be placed a certain distance in front of the camera, the position of the object is the position of the camera plus a distance in the direction of the line of sight:
float distance = ...;
float objectPosition = cameraPosition + losDirection * distance;
glm::mat4 modelPosMat = glm::translate( glm::mat4(1.0f) , objectPosition );
glm::mat4 objectModelMat = ...; // initial model matrix of the object
o_modelMatrix = modelPosMat * objectModelMat;
Note the objectModelMat is the identity matrix if the object has no further transformations glm::mat4(1.0f).
so you want to move the object with camera (instead of moving camera with object like camera follow). If this is just for some GUI stuff you can use different static view matrices for it. But if you want to do this in way you suggested then this is the way:
definitions
First we need few 3D 4x4 homogenuous transform matrices (read the link to see how to disect/construct what you need). So lets define some matrices we need for this:
C - inverse camera matrix (no projection)
M - direct object matrix
R - direct object rotation
Each matrix has 4 vectors X,Y,Z are the axises of the coordinate system represented by it and O is the origin. Direct matrix means the matrix directly represents the coordinate system and inverse means that it is the inverse of such matrix.
Math
so we want to construct M so it is placed at some distance d directly in front of C and has rotation R. I assume you are using perspective projection and C viewing direction is -Z axis. So what you need to do is compute position of M. That is easy you just do this:
iC = inverse(C); // get the direct matrix of camera
M = R; // set rotation of object
M.O = iC.O - d*iC.Z; // set position of object
The M.O = (M[12],M[13],M[14]) and iC.Z = (iC.Z[8],iC.Z[9],iC.Z[10]) so if you got direct access to your matrix you can do this on your own in case GLM does not provide element access.
Beware that all this is for standard OpenGL matrix convention and multiplication order. If you use DirectX convention instead then M,R are inverse and C is direct matrix so you would need to change the equations accordingly. Sorry I do not use GLM so I am not confident to generate any code for you.
In case you want to apply camera rotations on object rotations too then you need to change M = R to M = R*iC or M = iC*R which depends on what effect you want to achieve.
It's work fine with not multiplication, but addition
obj_pos = glm::normalize(glm::cross(vec3(0.0f, 0.0f, -1.0f), vec3(0.0f, 1.0f, 0.0f)));
o_modelMatrix[3][0] = camera_pos.x;
o_modelMatrix[3][1] = camera_pos.y;
o_modelMatrix[3][2] = camera_pos.z + distance;
o_modelMatrix = glm::translate(o_modelMatrix, obj_pos);

How do you set the Bounds of glm::ortho based on scene max and min coordinates?

I have a triangle and have 3 vertices anywhere in space.
I attempted to get the max and min coordinates for it.
void findBoundingBox(glm::vec3 & minBB, glm::vec3 & maxBB)
{
minBB.x = std::min(minBB.x, mCoordinate.x);
minBB.y = std::min(minBB.y, mCoordinate.y);
minBB.z = std::min(minBB.z, mCoordinate.z);
maxBB.x = std::max(maxBB.x, mCoordinate.x);
maxBB.y = std::max(maxBB.y, mCoordinate.y);
maxBB.z = std::max(maxBB.z, mCoordinate.z);
}
}
Now I tried to set
:
glm::vec3 InverseViewDirection(50.0f, 200, 200); //Inverse View Direction
glm::vec3 LookAtPosition(0.0,0,0); // I can make it anywhere with barycentric coord, but this is the simple case
glm::vec3 setupVector(0.0, 1, 0);
I tried to set the orthographic view to wrap the triangle by:
myCamera.setProjectionMatrix(min.x, max.x, max.y,min.y, 0.0001f, 10000.0f);
But its not neatly bounding the triangle in my view.
I've been stumped on this for a day, any pointers?
Bad: output : (I want the view to neatly bound the triangle)
Edit:
Based on a comment ( I have tried to update the bounds with the view matrix (model is identity, so ignoring that for now)
still no luck :(
glm::vec4 minSS = ((myCamera.getViewMatrix()) * glm::vec4(minWS, 0.0));
glm::vec4 maxSS = ((myCamera.getViewMatrix()) * glm::vec4(maxWS, 0.0));
myCamera.setProjectionMatrix(minSS.x, maxSS.x, maxSS.y, minSS.y, -200.0001f, 14900.0f);
You will need to apply all transformations that come before the perspective transformation to your input points when you calculate the bounding box.
In your code fragments, it looks like you're applying a viewing transform with an arbitrary viewpoint (50, 200, 200) as part of your rendering. You need to apply this same transformation to your input points before you feed them into your findBoundingBox() function.
In more mathematical terms, you typically have something like this in your vertex shader, with InputPosition being the original vertex coordinates:
gl_Position = ProjectionMatrix * ViewMatrix * ModelMatrix * InputPosition;
To determine a projection matrix that will map all your points to a given range, you need to look at all points that the projection matrix is applied to. With the notation above, those points are ViewMatrix * ModelMatrix * InputPosition. So when you calculate the bounding box, the model and view matrices (or the modelview matrix if you combine them) needs to be applied to the input points.

Issue with GLM Camera X,Y Rotation introducing Z Rotation

So I've been having trouble with a camera I've implemented in OpenGL and C++ using the GLM library. The type of camera I'm aiming for is a fly around camera which will allow easy exploration of a 3D world. I have managed to get the camera pretty much working, it's nice and smooth, looks around and the movement seems to be nice and correct.
The only problem I seem to have is that the rotation along the camera's X and Y axis (looking up and down) introduces some rotation about it's Z axis. This has the result of causing the world to slightly roll whilst travelling about.
As an example... if I have a square quad in front of the camera and move the camera in a circular motion, so as if looking around in a circle with your head, once the motion is complete the quad will have rolled slightly as if you've tilted your head.
My camera is currently a component which I can attach to an object/entity in my scene. Each entity has a "Frame" which is basically the model matrix for that entity. The Frame contains the following attributes:
glm::mat4 m_Matrix;
glm::vec3 m_Position;
glm::vec3 m_Up;
glm::vec3 m_Forward;
These are then used by the camera to create the appropriate viewMatrix like this:
const glm::mat4& CameraComponent::GetViewMatrix()
{
//Get the transform of the object
const Frame& transform = GetOwnerGO()->GetTransform();
//Update the viewMatrix
m_ViewMatrix = glm::lookAt(transform.GetPosition(), //position of camera
transform.GetPosition() + transform.GetForward(), //position to look at
transform.GetUp()); //up vector
//return reference to the view matrix
return m_ViewMatrix;
}
And now... here are my rotate X and Y methods within the Frame object, which I'm guessing is the place of the problem:
void Frame::RotateX( float delta )
{
glm::vec3 cross = glm::normalize(glm::cross(m_Up, m_Forward)); //calculate x axis
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, cross);
m_Forward = glm::normalize(glm::vec3(Rotation * glm::vec4(m_Forward, 0.0f))); //Rotate forward vector by new rotation
m_Up = glm::normalize(glm::vec3(Rotation * glm::vec4(m_Up, 0.0f))); //Rotate up vector by new rotation
}
void Frame::RotateY( float delta )
{
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, m_Up);
//Rotate forward vector by new rotation
m_Forward = glm::normalize(glm::vec3(Rotation * glm::vec4(m_Forward, 0.0f)));
}
So somewhere in there, there's a problem which I've been searching around trying to fix. I've been messing with it for a few days now, trying random things but I either get the same result, or the z axis rotation is fixed but other bugs appear such as incorrect X, Y rotation and camera movement.
I had a look at gimbal lock but from what I understood of it, this problem didn't seem quite like gimbal lock to me. But I may be wrong.
Store the current pitch/yaw angles and generate the camera matrix on-the-fly instead of trying to accumulate small changes on the intermediate vectors.
In your RotateY function, change it from this:
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, m_Up);
to this:
glm::mat4 Rotation = glm::rotate(glm::mat4(1.0f), delta, glm::vec3(0,1,0));

Quaternions -> Euler Angles -> Rotation Matrix trouble (GLM)

I'm writing a program that loads a file containing a scene description and then displays it using OpenGL. I'm using GLM for all of my math operations. Rotations in the scene file are stored in quaternion format. My scene management systems takes rotations for objects in the form of euler angles, and these angles are later converted to a rotation matrix when drawing.
My loading process thus takes the quaternion rotations, converts them to euler angles for storage in my object class, then converts these euler angles to rotation matrices for drawing. I'm using the glm::eulerAngles and glm::eulerAngleYXZ functions (respectively) to perform these two operations.
However, I am getting incorrect results. For example, if I understand correctly the quaternion {0.500 -0.500 0.500 0.500} (that's W X Y Z) should describe the rotation taking an arrow from the +Z axis to the +Y axis. When I run the program, however, I get the arrow pointing along the +X axis.
I would assume that there is some flaw in my understanding of the quaternions, but I am able to get my expected results by skipping the intermediary euler angle form. By converting the quaternion directly to a rotation matrix using glm::toMat4, I get a rotation that points my +Z arrow towards +Y.
I'm having trouble reconciling these two different outputs, considering that both methods seem both simple and correct. To simplify my question, why is it that these two seemingly equivalent methods produce different results:
glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f; // eulerAngleYXZ takes radians but eulerAngles returns degrees
glm::mat4 transform1 = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);
// transform1 rotates a +Z arrow so that it points at +X
glm::quat q(.5, -.5, .5, .5);
glm::mat4 transform2 = glm::toMat4(q);
// transform2 rotates a +Z arrow so that it points at +Y
You have probably figured this out by now... but
What eulerAngle sequence does the function:
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f;
return? If it does not return explicitly an 'YXZ' sequence, you will not be able to use the next function properly:
glm::mat4 transform1 = glm::eulerAngleYXZ(euler.y, euler.x, euler.z);
Your variable 'euler' must be the same sequence type as the function you specify to transform it into a rotation matrix.
After looking here it looks like the function 'glm::eulerAngles' returns 'XYZ' as pitch, yaw, and roll. Thus, assuming they are 'YXZ', or yaw, pitch, roll is incorrect.
As said before, with Euler angles and rotation matrices, order matters!
The order of multiplication is important when dealing with Euler angles. YXZ and XYZ produce very different rotations.
You could calculate separate matrices for each axis, and then multiply them together in the order your need.
glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q) * 3.14159f / 180.f;
glm::mat4 transformX = glm::eulerAngleX(euler.x);
glm::mat4 transformY = glm::eulerAngleY(euler.y);
glm::mat4 transformZ = glm::eulerAngleZ(euler.z);
glm::mat4 transform1 =
transformX * transformY * transformZ; // or some other order
I think the result is radian already, no need to convert.
glm::quat q(.5, -.5, .5, .5);
glm::vec3 euler = glm::eulerAngles(q); // * 3.14159f / 180.f;
glm::mat4 transformX = glm::eulerAngleX(euler.x);
glm::mat4 transformY = glm::eulerAngleY(euler.y);
glm::mat4 transformZ = glm::eulerAngleZ(euler.z);
glm::mat4 transform1 =
transformX * transformY * transformZ; // or some other order