Reputation: 175
I'm trying to implement a camera all by myself in OpenGL (I use glfw and gml). As for now, I don't have any class for it. I will create it later. So here is my try on coding the camera movements; it works fine with simple mouse movements, but otherwise, the camera tilts sideways. I'm still new to OpenGL so I don't have a lot to show but here is illustrated my problem: https://i.sstatic.net/7YK5t.jpg
I have a few (global as for now) variables :
float lastX = 0.0f, lastY = 0.0f, yaw = 0.0f, pitch = 0.0f;
glm::vec3 cameraPos(0.0f, 0.0f, 3.0f);
glm::vec3 cameraUp(0.0f, 1.0f, 0.0f); // As a reminder, x points to the right, y points upwards and z points towards you
glm::vec3 cameraFront(0.0f, 0.0f, -1.0f);
With these, I can create a view matrix this way :
glm::mat4 view;
view = glm::lookAt(cameraPos, cameraPos + cameraFront, cameraUp);
I want to be able to move my camera perpendicularly (yaw) and laterally (pitch), i.e. up, down, right, left on my screen. For this, it is enough to rotate the cameraFront vector and the cameraUp vector appropriately and then update the view matrix with the updated vectors.
My Cursor Position Callback looks like this :
glm::vec3 rotateAroundAxis(glm::vec3 toRotate, float angle, glm::vec3 axisDirection, glm::vec3 axisPoint) { // angle in radians
toRotate -= axisPoint;
glm::mat4 rotationMatrix(1.0f);
rotationMatrix = glm::rotate(rotationMatrix, angle, axisDirection);
glm::vec4 result = rotationMatrix*glm::vec4(toRotate, 1.0f);
toRotate = glm::vec3(result.x, result.y, result.z);
toRotate += axisPoint;
return toRotate;
}
void mouseCallback(GLFWwindow* window, double xpos, double ypos) {
const float maxPitch = float(M_PI) - float(M_PI) / 180.0f;
glm::vec3 cameraRight = -glm::cross(cameraUp, cameraFront);
float xOffset = xpos - lastX;
float yOffset = ypos - lastY;
lastX = xpos;
lastY = ypos;
float sensitivity = 0.0005f;
xOffset *= sensitivity;
yOffset *= sensitivity;
yaw += xOffset; // useless here
pitch += yOffset;
if (pitch > maxPitch) {
yOffset = 0.0f;
}
if (pitch < -maxPitch) {
yOffset = 0.0f;
}
cameraFront = rotateAroundAxis(cameraFront, -xOffset, cameraUp, cameraPos);
cameraFront = rotateAroundAxis(cameraFront, -yOffset, cameraRight, cameraPos);
cameraUp = rotateAroundAxis(cameraUp, -yOffset, cameraRight, cameraPos);
}
As I said, it works fine for simple up-down, left-right camera movements, but when I start to move my mouse in circles or like a madman, the camera starts to rotate longitudinally (roll).
I've tried to force cameraRight.y = cameraPos.y so that the cameraRight vector doesn't tilt upwards/downwards due to numerical errors but it doesn't solve the problem. I've also tried to add a (global) cameraRight vector to keep track of it instead of computing it every time so the end of the function looks like this :
cameraFront = rotateAroundAxis(cameraFront, -xOffset, cameraUp, cameraPos);
cameraRight = rotateAroundAxis(cameraRight, -xOffset, cameraUp, cameraPos);
cameraFront = rotateAroundAxis(cameraFront, -yOffset, cameraRight, cameraPos);
cameraUp = rotateAroundAxis(cameraUp, -yOffset, cameraRight, cameraPos);
but it doesn't solve the problem. Any pieces of advice ?
Upvotes: 0
Views: 1203
Reputation: 7198
It seems you have global X-axis to the right, Y-axis going deep in the screen and Z-axis going up. And you local camera axis system is similar.
The desired behaviour is rotate the camera over its current position, left-right mouse movement is rotation around global Z, and up-dowm mouse movement is rotation around local X. Think a bit around these rotations until you understand them well, and why one is around global but the other around local directions. Imagine a security camera and its movements to visualize the axis systems and rotations.
The goal is getting the parameters used to define the View transformation by lookAt
function.
First rotate around local X. We convert this local vector into global axis system by inverting the current View-matrix, you call view
glm::vec3 currGlobalX = glm::normalize((glm::inverse(view) * glm::vec4(1.0, 0.0, 0.0, 0.0)).xyz);
We need to rotate not only the cameraUp
vector, but also the current target
defined in global coordinates, what you call cameraPos + cameraFront
:
cameraUp = rotateAroundAxis(cameraUp, -yOffset, currGlobalX, glm::vec3(0.0f, 0.0f, 0.0f)); //vector, not needed to translate
cameraUp = glm::normalize(cameraUp);
currenTarget = rotateAroundAxis(currenTarget, -yOffset, currGlobalX, cameraPos); //point, need translation
Now rotate around global Z-axis
cameraUp = rotateAroundAxis(cameraUp, -xOffset, glm::vec3(0.0f, 0.0f, 1.0f), glm::vec3(0.0f, 0.0f, 0.0f)); //vector, not needed to translate
cameraUp = glm::normalize(cameraUp);
currenTarget = rotateAroundAxis(currenTarget, -xOffset, glm::vec3(0.0f, 0.0f, 1.0f), cameraPos); //point, need translation
Finally, update view
:
view = glm::lookAt(cameraPos, currenTarget, cameraUp);
Upvotes: 3