Working on an opengl project in visual studio.Im trying to rotate the camera around the X and Y axis.
Thats the math i should use
Im having trouble because im using glm::lookAt for camera position and it takes glm::vec3 as arguments.
Can someone explain how can i implement this in opengl?
PS:i cant use quaternions
The lookAt function should take three inputs:
vec3 cameraPosition
vec3 cameraLookAt
vec3 cameraUp
For my past experience, if you want to move the camera, first find the transform matrix of the movement, then apply the matrix onto these three vectors, and the result will be three new vec3, which are your new input into the lookAt function.
vec3 newCameraPosition = movementMat4 * cameraPosition
//Same for other two
Another approach could be finding the inverse movement of the one you want the camera to do and applying it to the whole scene. Since moving the camera is kind of equals to move the object onto inverse movement while keep the camera not move :)
Below the camera is rotated around the z-axis.
const float radius = 10.0f;
float camX = sin(time) * radius;
float camZ = cos(time) * radius;
glm::vec3 cameraPos = glm::vec3(camX, 0.0, camZ);
glm::vec3 objectPos = glm::vec3(0.0, 0.0, 0.0);
glm::vec3 up = glm::vec3(0.0, 1.0, 0.0);
glm::mat4 view = glm::lookAt(cameraPos, objectPos, up);
Check out https://learnopengl.com/, its a great site to learn!
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)
I've been stuck on this for two days now, I'm unsure where else to look. I'm rendering two 3d cubes using OpenGL, and trying to apply a local rotation to each cube in these scene in response to me pressing a button.
I've got to the point where my cubes rotate in 3d space, but their both rotating about the world-space origin, instead of their own local origins.
(couple second video)
https://www.youtube.com/watch?v=3mrK4_cCvUw
After scouring the internet, the appropriate formula for calculating the MVP is as follow:
auto const model = TranslationMatrix * RotationMatrix * ScaleMatrix;
auto const modelview = projection * view * model;
Each of my cube's has it's own "model", which is defined as follows:
struct model
{
glm::vec3 translation;
glm::quat rotation;
glm::vec3 scale = glm::vec3{1.0f};
};
When I press a button on my keyboard, I create a quaternion representing the new angle and multiply it with the previous rotation quaternion, updating it in place.
The function looks like this:
template<typename TData>
void rotate_entity(TData &data, ecst::entity_id const eid, float const angle,
glm::vec3 const& axis) const
{
auto &m = data.get(ct::model, eid);
auto const q = glm::angleAxis(glm::degrees(angle), axis);
m.rotation = q * m.rotation;
// I'm a bit unsure on this last line above, I've also tried the following without fully understanding the difference
// m.rotation = m.rotation * q;
}
The axis is provided by the user like so:
// inside user-input handling function
float constexpr ANGLE = 0.2f;
...
// y-rotation
case SDLK_u: {
auto constexpr ROTATION_VECTOR = glm::vec3{0.0f, 1.0f, 0.0f};
rotate_entities(data, ANGLE, ROTATION_VECTOR);
break;
}
case SDLK_i: {
auto constexpr ROTATION_VECTOR = glm::vec3{0.0f, -1.0f, 0.0f};
rotate_entities(data, ANGLE, ROTATION_VECTOR);
break;
}
My GLSL vertex shader is pretty straight forward from what I've found in the example code out there:
// attributes input to the vertex shader
in vec4 a_position; // position value
// output of the vertex shader - input to fragment
// shader
out vec3 v_uv;
uniform mat4 u_mvmatrix;
void main()
{
gl_Position = u_mvmatrix * a_position;
v_uv = vec3(a_position.x, a_position.y, a_position.z);
}
Inside my draw code, the exact code I'm using to calculate the MVP for each cube is:
...
auto const& model = shape.model();
auto const tmatrix = glm::translate(glm::mat4{}, model.translation);
auto const rmatrix = glm::toMat4(model.rotation);
auto const smatrix = glm::scale(glm::mat4{}, model.scale);
auto const mmatrix = tmatrix * rmatrix * smatrix;
auto const mvmatrix = projection * view * mmatrix;
// simple wrapper that does logging and forwards to glUniformMatrix4fv()
p.set_uniform_matrix_4fv(logger, "u_mvmatrix", mvmatrix);
Earlier in my program, I calculate my view/projection matrices like so:
auto const windowheight = static_cast<GLfloat>(hw.h);
auto const windowwidth = static_cast<GLfloat>(hw.w);
auto projection = glm::perspective(60.0f, (windowwidth / windowheight), 0.1f, 100.0f);
auto view = glm::lookAt(
glm::vec3(0.0f, 0.0f, 1.0f), // camera position
glm::vec3(0.0f, 0.0f, -1.0f), // look at origin
glm::vec3(0.0f, 1.0f, 0.0f)); // "up" vector
The positions of my cube's in world-space are on the Z axis, so they should be visible:
cube0.set_world_position(0.0f, 0.0f, 0.0f, 1.0f);
cube1.set_world_position(-0.7f, 0.7f, 0.0f, 1.0f);
// I call set_world_position() exactly once before my game enter's it's main loop.
// I never call this again, it just modifies the vertex used as the center of the shape.
// It doesn't modify the model matrix at all.
// I call it once before my game enter's it's game loop, and I never modify it after that.
So, my question is, is the appropriate way to update a rotation for an object?
Should I be storing a quaternion directly in my object's "model"?
Should I be storing my translation and scaling as separate vec3's?
Is there an easier way to do this? I've been reading and re-reading anything I can find, but I don't see anyone doing this in the same way.
This tutorial is a bit short on details, specifically how to apply a rotation to an existing rotation (I believe this is just multiplying the quaternions together, which is what I'm doing inside rotate_entity(...) above).
http://www.opengl-tutorial.org/intermediate-tutorials/tutorial-17-quaternions/
https://github.com/opengl-tutorials/ogl/blob/master/tutorial17_rotations/tutorial17.cpp#L306-L311
Does it make more sense to store the resulting "MVP" matrix myself as my "model" and apply glm::transform/glm::scale/glm::rotate operations on the MVP matrix directly? (I tried this last option earlier, but I couldn't figure out how to get that to work too).
Thanks!
edit: better link
Generally, you don't want to modify the position of your model's individual vertices on the CPU. That's the entire purpose of the vertex program. The purpose of the model matrix is to position the model in the world in the vertex program.
To rotate a model around its center, you need to first move the center to the origin, then rotate it, then move the center to its final position. So let's say you have a cube that stretches from (0,0,0) to (1,1,1). You need to:
Translate the cube by (-0.5, -0.5, -0.5)
Rotate by the angle
Translate the cube by (0.5, 0.5, 0.5)
Translate the cube to wherever it belongs in the scene
You can combine the last 2 translations into a single one, and of course, you can collapse all of these transformations into a single matrix that is your model matrix.
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);
I'm using glm::quaternions to rotate a vector around a certain axis. But the vector inverts the axis everytime it is called.
Here is my code to rotate my object around the side axis:
void PlayerController::rotateForward(float angle) {
angle = (angle*M_PI) / 180.0f;
fquat rot = fquat(angle, playerObject->getVecSide());
normalize(rot);
vec4 newUpVec = rot * vec4(playerObject->getVecUp(), 1.0f);
normalize(newUpVec);
vec3 upVec3 = normalize(vec3(newUpVec.x ,newUpVec.y, newUpVec.z));
playerObject->setVecUp(upVec3);
playerObject->setVecForward(-normalize(cross(playerObject->getVecUp(), playerObject->getVecSide())));
vec3 newPlayerPos = currentPlanet->getPosition() + (playerObject->getVecUp() * radius);
playerObject->setPosition(newPlayerPos);
}
everytime I call this method, my up vector is turned around the axis, but also inverted. I can work around this by using:
vec4 newUpVec = rot * -vec4(playerObject->getVecUp(), 1.0f);
but this is more like treating symptoms instead of finding the cause. Maybe someone around here can help me in understanding what the quaternion does here.
well here is the answer, the quaternion initialization was wrong...
these lines were missing:
rotateAround = rotateAround * sinf(angle/2.0f);
angle = cosf(angle/2.0f);
so the correct version of the method looks like this:
vec3 GameObjectRotator::rotateVector(float angle, vec3 toRotate, vec3 rotateAround) {
angle = (angle*M_PI) / 180.0f;
rotateAround = rotateAround * sinf(angle/2.0f);
angle = cosf(angle/2.0f);
fquat rot = fquat(angle, rotateAround);
normalize(rot);
vec4 rotated = rot * vec4(toRotate, 1.0f);
normalize(rotated);
return normalize(vec3(rotated));
}
(the class has gone through quite a bit refactoring since the first version, but the idea should be clear anyways)