I want to rotate a 2D object in OpenGL with this code:
mView = glm::translate(glm::mat4(1.0f), glm::vec3(0.0f));
mModel = glm::translate(glm::mat4(1.0f), mLocation);
mModel = glm::rotate(mModel, mRotation, glm::vec3(0.0f, 0.f, 1.f));
mModel = glm::scale(mModel, mScale);
mMVP = mProj * mView * mModel;
With this code, the object rotates around the global axis and not it's local axis.
If I don't include the view matrix(mMVP = mProj * mModel;) It works fine, but then I don't have a view projection. How is it possible for an object to rotate around it's own axis?
Related
I'm currently trying to rotate the camera around its local axis based on keyboard/mouse input and the code I currently have uses DirectXMath and works nicely, however it is using the world axis to rotate around rather than the cameras local axis. Because of this, some of the rotations are not as expected and causes issues as the camera rotates. For example, when we tilt our camera, the Y axis will change and we will want to rotate around another axis to get our expected results.
What am I doing wrong in the code or what do I need to change in order to rotate around its local axis?
vector.x, vector.y, vector.z (The vector to rotate around, i.e. (1.0f, 0.0f, 0.0f))
//define our camera matrix
XMFLOAT4X4 cameraMatrix;
//position, lookat, up values for the camera
XMFLOAT3 position;
XMFLOAT3 up;
XMFLOAT3 lookat;
void Camera::rotate(XMFLOAT3 vector, float theta) {
XMStoreFloat4x4(&cameraMatrix, XMMatrixIdentity());
//set our view quaternion to our current camera's lookat position
XMVECTOR viewQuaternion = XMQuaternionIdentity();
viewQuaternion = XMVectorSet(lookat.x, lookat.y, lookat.z, 0.0f);
//set the rotation vector based on our parameter, i.e (1.0f, 0.0f, 0.0f)
//to rotate around the x axis
XMVECTOR rotationVector = XMVectorSet(vector.x, vector.y, vector.z, 0.0f);
//create a rotation quaternion to rotate around our vector, with a specified angle, theta
XMVECTOR rotationQuaternion = XMVectorSet(
XMVectorGetX(rotationVector) * sin(theta / 2),
XMVectorGetY(rotationVector) * sin(theta / 2),
XMVectorGetZ(rotationVector) * sin(theta / 2),
cos(theta / 2));
//get our rotation quaternion inverse
XMVECTOR rotationInverse = XMQuaternionInverse(rotationQuaternion);
//new view quaternion = [ newView = ROTATION * VIEW * INVERSE ROTATION ]
//multiply our rotation quaternion with our view quaternion
XMVECTOR newViewQuaternion = XMQuaternionMultiply(rotationQuaternion, viewQuaternion);
//multiply the result of our calculation above with the inverse rotation
//to get our new view values
newViewQuaternion = XMQuaternionMultiply(newViewQuaternion, rotationInverse);
//take the new lookat values from our newViewQuaternion and put them into the camera
lookat = XMFLOAT3(XMVectorGetX(newViewQuaternion), XMVectorGetY(newViewQuaternion), XMVectorGetZ(newViewQuaternion));
//build our camera matrix using XMMatrixLookAtLH
XMStoreFloat4x4(&cameraMatrix, XMMatrixLookAtLH(
XMVectorSet(position.x, position.y, position.z, 0.0f),
XMVectorSet(lookat.x, lookat.y, lookat.z, 0.0f),
XMVectorSet(up.x, up.y, up.z, 0.0f)));
}
The view matrix is then set
//store our camera's matrix inside the view matrix
XMStoreFloat4x4(&_view, camera->getCameraMatrix() );
-
Edit:
I have tried an alternative solution without using quaternions, and it seems I can get the camera to rotate correctly around its own axis, however the camera's lookat values now never change and after I have stopped using the mouse/keyboard, it snaps back to its original position.
void Camera::update(float delta) {
XMStoreFloat4x4(&cameraMatrix, XMMatrixIdentity());
//do we have a rotation?
//this is set as we try to rotate, around a current axis such as
//(1.0f, 0.0f, 0.0f)
if (rotationVector.x != 0.0f || rotationVector.y != 0.0f || rotationVector.z != 0.0f) {
//yes, we have an axis to rotate around
//create our axis vector to rotate around
XMVECTOR axisVector = XMVectorSet(rotationVector.x, rotationVector.y, rotationVector.z, 0.0f);
//create our rotation matrix using XMMatrixRotationAxis, and rotate around this axis with a specified angle theta
XMMATRIX rotationMatrix = XMMatrixRotationAxis(axisVector, 2.0 * delta);
//create our camera's view matrix
XMMATRIX viewMatrix = XMMatrixLookAtLH(
XMVectorSet(position.x, position.y, position.z, 0.0f),
XMVectorSet(lookat.x, lookat.y, lookat.z, 0.0f),
XMVectorSet(up.x, up.y, up.z, 0.0f));
//multiply our camera's view matrix by the rotation matrix
//make sure the rotation is on the right to ensure local axis rotation
XMMATRIX finalCameraMatrix = viewMatrix * rotationMatrix;
/* this piece of code allows the camera to correctly rotate and it doesn't
snap back to its original position, as the lookat coordinates are being set
each time. However, this will make the camera rotate around the world axis
rather than the local axis. Which brings us to the same problem we had
with the quaternion rotation */
//XMVECTOR look = XMVectorSet(lookat.x, lookat.y, lookat.z, 0.0);
//XMVECTOR finalLook = XMVector3Transform(look, rotationMatrix);
//lookat.x = XMVectorGetX(finalLook);
//lookat.y = XMVectorGetY(finalLook);
//lookat.z = XMVectorGetZ(finalLook);
//finally store the finalCameraMatrix into our camera matrix
XMStoreFloat4x4(&cameraMatrix, finalCameraMatrix);
} else {
//no, there is no rotation, don't apply the roation matrix
//no rotation, don't apply the rotation matrix
XMStoreFloat4x4(&cameraMatrix, XMMatrixLookAtLH(
XMVectorSet(position.x, position.y, position.z, 0.0f),
XMVectorSet(lookat.x, lookat.y, lookat.z, 0.0f),
XMVectorSet(up.x, up.y, up.z, 0.0f)));
}
An example can be seen here: https://i.gyazo.com/f83204389551eff427446e06624b2cf9.mp4
I think I am missing setting the actual lookat value to the new lookat value, but I'm not sure how to calculate the new value, or extract it from the new view matrix (which I have already tried)
glm::mat4 Model = glm::mat4(1.0f);
float dir_x = 0.0f, dir_y = 1.0f, dir_z = 0.0f;
do {
// Clear the screen
glClear(GL_COLOR_BUFFER_BIT);
// Use our shader
glUseProgram(programID);
GLuint MatrixID = glGetUniformLocation(programID, "MVP");
glm::mat4 Projection = glm::perspective(glm::radians(45.0f), 4.0f / 3.0f, 0.1f, 100.0f);
//glm::mat4 Projection = glm::ortho(-1.0f,1.0f,-1.0f,1.0f,0.0f,100.0f); // In world coordinates
// Camera matrix
glm::mat4 View = glm::lookAt(
glm::vec3(0.5, 0.5, 3), // Camera is at (4,3,3), in World Space
glm::vec3(0.5, 0.5, 0), // and looks at the origin
glm::vec3(0, 1, 0) // Head is up (set to 0,-1,0 to look upside-down)
);
float rot_angle = 0.0f;
const float speed = 0.01f;
glm::vec3 dir = glm::vec3(dir_x, dir_y, dir-z);
if (glfwGetKey(window, GLFW_KEY_LEFT) == GLFW_PRESS)
{
rot_angle = -1.0f;
Model = glm::translate(tri_center)* glm::rotate(glm::mat4(), glm::radians(rot_angle), glm::vec3(0, 0, -1))*glm::translate(-tri_center)*Model;
//dir left
...
If I rotate the object(car), I want to move it to the head of the car. Now, regardless of the head of car, the car only moves upward.
How do codes make dir rotate?
Changing the center of rotation can be achieved with the following:
Remember, multiply matrices RIGHT TO LEFT, the first transform on the rightmost side, the last on the left
First, create a translation that brings the center of rotation to the origin of the scene (0, 0, 0), this is basically reversing each x,y, and z. So the translation for the example center vec3(1.0, 2.3, -5.2) is glm::mat4 origin = glm::translate(origin, glm::vec3(-1.0, -2.3, 5.2);
Store this vector, we are going to use this for ALL points in the mesh
Now apply the desired rotation(s) to this translate matrix and store them in a new mat4, so do:
glm::mat4 final = glm::rotate(..) * origin
Finally, bring the center (and the rest of the model) back to the original position by creating a translation identical to the vector3 with the following:
glm::mat4 relocate = glm::translate(relocate, center) and then
glm::mat4 final = relocate * glm::rotate(..) * origin
Essentially what we are doing here is bringing the center of the model to the origin, translating all points relative to that, then rotating them around the center (which is now the origin), then bringing them back the same distance they came.
Now apply this translation to ALL of the models points, do this in the vertex shader, obviously. If the model is really small, you could do it in your code but that will gobble memory for most meshes. This mat4 could be applied to the model matrix if you don't want to add another matrix. model = model * final //note, first do transformations, then scale for the model
Full code looks something like this: (you could also multiply the matricies manually, but GLM lets you pass a matrix into the args of translate() function, it then just applies the translation to the matrix in its current form)
glm::vec3 center = vec3(1.0, 2.3, -5.2);
glm::mat4 finalTransform = glm::translate(finalTransform, glm::vec3(-1.0, -2.3, 5.2)); //first bring everything to origin, notice this is a mat4
finalTransform = glm::rotate(finalTransform, ...); //rotate how you want
finalTransform = glm::translate(finalTransform, center); //return to center
model = model * finalTransform; //apply this transformation to be calculated by the vertex shader for the object
glUniformMatrix4fv(glGetUniformLocation(sp, "model"), 1, GL_FALSE, glm::value_ptr(model)); //pass model matrix into shader program
Also, in your current code it appears that you have the right idea, but you are using the translate function incorrectly. It should be called like this: glm::translate(mat4, vec3). At the very least, construct an empty mat4 to translate with the glm::mat4() constructor.
I'm trying to write the orbital camera (based on glm::quat) for my OpenGL application.
I have a few questions:
Сan I make ViewMatrix from RotationMatrix + position of camera?
camera_quat = glm::quat(glm::vec3(tmp_pitch, tmp_yaw, 0)) * camera_quat;
float pitch = camera_quat.pitch();
float yaw = camera_quat.yaw();
glm::mat4 rotate = glm::mat4_cast(camera_quat);
glm::vec3 view_direction(cos(yaw) * cos(pitch), sin(pitch), -sin(yaw) * cos(pitch));
camera_position = target - view_direction * radius;
glm::mat4 translate = glm::translate(camera_position);
glm::mat4 view_matrix = **???**;
Is this line correct?:
glm::vec3 view_direction(cos(yaw) * cos(pitch), sin(pitch), -sin(yaw) * cos(pitch));
P.S. Sorry if my english is bad. It is not my native language, I am russian.
I hope you can help me. Thank you in advance!
If you change the translate matrix to
glm::mat4 translate = glm::translate(-camera_position);
, it should be simply
glm::mat4 view_matrix = rotation * translation;
However, there is an easier way to go there. What you basically want to do is the following: Move the camera to the target, rotate the camera there, move it a bit back. This can be expressed in matrix form with (note that the view matrix is the inverse model transform for the camera):
view_matrix = glm::translate(0, 0, -radius) * rotate * glm::translate(-target);
How can I rotate a camera in a axis? What matrix I have to multiply?
I am using glm::lookAt to construct the viewMatrix, but I tried to multiply it by a rotation matrix and nothing happened.
glm::mat4 GetViewMatrix()
{
return glm::lookAt(this->Position, this->Position + this->Front, glm::vec3(0.0f, 5.0f, 0.0f));
}
glm::mat4 ProjectionMatrix = glm::perspective(actual_camera->Zoom, (float)g_nWidth / (float)g_nHeight, 0.1f, 1000.0f);
glm::mat4 ViewMatrix = actual_camera->GetViewMatrix();
glm::mat4 ModelMatrix = glm::mat4(1.0);
glm::mat4 MVP = ProjectionMatrix * ViewMatrix * ModelMatrix;
Rotate the front and up vectors of your camera using glm::rotate:
glm::mat4 GetViewMatrix()
{
auto front = glm::rotate(this->Front, angle, axis);
auto up = glm::rotate(glm::vec3(0, 1, 0), angle, axis);
return glm::lookAt(this->Position, this->Position + front, up);
}
Alternatively, you can add a multiplication with your rotation matrix to your MVP construction:
glm::mat4 MVP = ProjectionMatrix * glm::transpose(Rotation) * ViewMatrix * ModelMatrix;
It is important that the rotation happens after the view matrix, so all objects will be rotated relative to the camera's position. Furthermore, you have to use transpose(Rotation) (the inverse of a rotation matrix is its transpose), since rotating the camera clockwise for example, is equivalent to rotating all objects counter-clockwise.
I'm having difficulty figuring out how to translate an object across the screen given arrow key inputs. Currently I have no issue moving the camera around, but I can't seem to wrap my head around making the object move instead of the camera.
Here's what I'm doing to compute the View Matrix
ViewMatrix = glm::lookAt(
position, //camera position
position+direction, //look at origin
up //head up
);
where position and direction are glm::vec3
So to instead change the position of the object, would I modify the Model Matrix? or would I do something with mvp?
The model matrix currently remains at glm::mat4(1.0)
computeMatricesFromInputs(window,time); //function that handles input and computes viewMatrix
glm::mat4 projectionMatrix = glm::perspective(45.0f, 4.0f/3.0f, 0.1f, 100.0f);
glm::mat4 viewMatrix = getViewMatrix();
glm::mat4 modelMatrix = glm::mat4(1.0);
glm::mat4 MVP = projectionMatrix * viewMatrix * modelMatrix;
So I ended up figuring the problem with with help from #j-p. What I wanted to do was move the object, so I applied the glm function translate() to the model matrix. To do this I went over to my controls file and created a function called
glm::mat4 getModelMatrix();
which returned the variable glm::mat4 ModelMatrix that I had also declared in the header file. The actual portion of code and moved the object was like so:
//If the the corresponding key is pressed...
ModelMatrix = glm::translate(ModelMatrix, glm::vec3(0.0f, 1.0f, 0.0f); //move y 1.0f
//else If..etc..
Then passing back towards my main loop the final code would look like:
computeMatricesFromInputs(window,time); //function that handles input and computes viewMatrix
glm::mat4 projectionMatrix = glm::perspective(45.0f, 4.0f/3.0f, 0.1f, 100.0f);
glm::mat4 viewMatrix = getViewMatrix();
glm::mat4 modelMatrix = getModelMatrix();
glm::mat4 MVP = projectionMatrix * viewMatrix * modelMatrix;