I am trying to do a class to walk through the world in OpenGL, but I am having problems with the mathematics. My idea here is to use the function lookAt from glm to set the observer in the position I wanted, and then just operate with the points that I pass to the function.
I think the functions to do rotations that I made are correct, but the translation part in the walk method seems to be wrong, and when I try to walk in the world if I just translate, or just rotated, things go right, but when I do both things just get messed.
here is the class so far:
#ifndef OBSERVER_H
#define OBSERVER_H
#include <GL/gl.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
class Observer {
private:
glm::vec3 eye, center, upp;
public:
glm::mat4 view;
Observer() {}
~Observer() {}
void initialize(glm::vec3 eye, glm::vec3 center, glm::vec3 upp);
void walk(GLfloat distance);
void pitch(GLfloat pitch);
void yaw(GLfloat yaw);
void roll(GLfloat roll);
void setView();
};
void Observer::initialize(glm::vec3 eye, glm::vec3 center, glm::vec3 upp)
{
this->eye = eye;
this->center = center;
this->upp = upp;
}
void Observer::walk(GLfloat distance)
{
glm::vec3 vector = glm::normalize(center - eye);
glm::vec3 translate = vector*distance - vector;
eye += translate;
center += translate;
upp += translate;
}
void Observer::roll(GLfloat roll) {
glm::mat4 rotate(1.0f);
rotate = glm::rotate(rotate, roll, glm::vec3(center - eye));
center = glm::vec3(rotate * glm::vec4(center, 1.0f));
upp = glm::vec3(rotate * glm::vec4(upp, 1.0f));
}
void Observer::yaw(GLfloat yaw) {
glm::mat4 rotate(1.0f);
rotate = glm::rotate(rotate, yaw, glm::vec3(upp - eye));
center = glm::vec3(rotate * glm::vec4(center, 1.0f));
upp = glm::vec3(rotate * glm::vec4(upp, 1.0f));
}
void Observer::pitch(GLfloat pitch) {
glm::mat4 rotate(1.0f);
glm::vec3 cross = glm::cross(center - eye, upp - eye);
rotate = glm::rotate(rotate, pitch, cross);
center = glm::vec3(rotate * glm::vec4(center, 1.0f));
upp = glm::vec3(rotate * glm::vec4(upp, 1.0f));
}
void Observer::setView()
{
view = glm::lookAt(eye, center, upp);
}
#endif
So right before I starting draw things I set the view matrix with this class in other part in the program. Can someone tell me if my maths are right?
When you walk, you only want to transform the eye and center position, not the upp vector. Just remove the upp += translate; line.
Related
I'm trying to make a "6 degrees of freedom" camera like the ones used in space games. I would like to learn how to store camera rotation as a quaternion but I can't exactly find anything on the internet to help me (or maybe I don't know what keywords I should be using).
What I want to do is use a glm::quat to store the orientation of the camera but the parts that I'm having issues with is changing the pitch, yaw, roll and finding out how to get the new up, right and direction vectors needed for the glm::lookAt function to create a view matrix.
So far this is my code with the functions I need help with marked:
#include "camera.hpp"
#include <glad/glad.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
#include <glm/gtc/quaternion.hpp>
Camera::Camera(glm::vec3 position, glm::quat orientation, float zNear, float zFar)
{
this->position = position;
this->orientation = orientation;
right = glm::vec3( 1, 0, 0);
up = glm::vec3( 0, 1, 0);
direction = glm::vec3( 0, 0, -1);
this->zNear = zNear;
this->zFar = zFar;
updateViewMatrix();
}
Camera::Camera(glm::vec3 position, glm::vec3 orientation, float zNear, float zFar)
{
this->position = position;
this->orientation = glm::quat(glm::vec3(glm::radians(orientation.x), glm::radians(orientation.y), glm::radians(orientation.z)));
right = glm::vec3( 1, 0, 0);
up = glm::vec3( 0, 1, 0);
direction = glm::vec3( 0, 0, -1);
this->zNear = zNear;
this->zFar = zFar;
updateViewMatrix();
}
void Camera::setPosition(glm::vec3 position)
{
this->position = position;
}
void Camera::setOrientation(glm::quat orientation)
{
this->orientation = orientation;
}
void Camera::setOrientation(glm::vec3 orientation)
{
glm::quat orientationQuat = glm::quat(orientation);
this->orientation = orientationQuat;
}
void Camera::setPitch(float amount)
{
// NEED HELP
updateViewMatrix();
}
void Camera::setYaw(float amount)
{
// NEED HELP
updateViewMatrix();
}
void Camera::setRoll(float amount)
{
// NEED HELP
updateViewMatrix();
}
void Camera::setZNear(float zNear)
{
this->zNear = zNear;
updateProjectionMatrix();
}
void Camera::setZFar(float zFar)
{
this->zFar = zFar;
updateProjectionMatrix();
}
void Camera::move(glm::vec3 movement)
{
position += right * movement.x + up * movement.y + direction * movement.z;
updateViewMatrix();
}
void Camera::moveAxis(glm::vec3 translation)
{
position += translation;
updateViewMatrix();
}
void Camera::rotate(glm::quat rotation)
{
// NEED HELP
}
void Camera::rotate(glm::vec3 rotation)
{
glm::quat rotationQuat = glm::quat(rotation);
// NEED HELP
}
void Camera::pitch(float amount)
{
// NEED HELP
updateViewMatrix();
}
void Camera::yaw(float amount)
{
// NEED HELP
updateViewMatrix();
}
void Camera::roll(float amount)
{
// NEED HELP
updateViewMatrix();
}
// Excluded a few get___() functions here
glm::mat4 Camera::getViewMatrix() const
{
return viewMatrix;
}
glm::mat4 Camera::getProjectionMatrix() const
{
return projectionMatrix;
}
glm::mat4 Camera::getViewProjectionMatrix() const
{
return viewProjectionMatrix;
}
void Camera::updateViewMatrix()
{
viewMatrix = glm::lookAt(position, position + direction, up/*glm::cross(right, direction)*/);
viewProjectionMatrix = projectionMatrix * viewMatrix;
}
These are the relevant variables I create in my header file:
glm::vec3 position;
glm::quat orientation;
glm::vec3 direction; // Camera Direction / Camera View Facing
glm::vec3 right;
glm::vec3 up;
I found the answer to my problem of "How to find the right, up and direction vectors for the glm::lookat function"
All I need to do now is change the world up, right and direction vectors by the orientation of the quaternion like so:
right = glm::normalize(orientation * glm::vec3(1, 0, 0));
up = glm::normalize(orientation * glm::vec3(0, 1, 0));
direction = glm::normalize(orientation * glm::vec3(0, 0, -1));
I'm trying to implement a simple paint program and now I have a problem with zoom, I can't understand how to do it? I tried to adapt the code from here to myself, but it did not work, I just get a black screen. What my problem?
Not using glut or glew!
Here my camera code:
.h
class Camera2d
{
public:
Camera2d(const glm::vec3& pos = glm::vec3(0.f, 0.f, 0.f),
const glm::vec3& up = glm::vec3(0.f, 1.f, 0.f));
//~Camera2d();
void setZoom(const float& zoom);
float getZoom() const noexcept;
glm::mat4 getViewMatrix() const noexcept;
void mouseScrollCallback(const float& yOffset);
protected:
void update();
private:
// Camera zoom
float m_zoom;
// Euler Angles
float m_yaw;
float m_pitch;
public:
// Camera Attributes
glm::vec3 position;
glm::vec3 worldUp;
glm::vec3 front;
glm::vec3 up;
glm::vec3 right;
};
.cpp
Camera2d::Camera2d(
const glm::vec3& pos /* = glm::vec3(0.f, 0.f, 0.f) */,
const glm::vec3& up /* = glm::vec3(0.f, 1.f, 0.f) */
)
: m_zoom(45.f)
, m_yaw(-90.f)
, m_pitch(0.f)
, position(pos)
, worldUp(up)
, front(glm::vec3(0.f, 0.f, -1.f))
{
this->update();
}
void Camera2d::setZoom(const float& zoom)
{
this->m_zoom = zoom;
}
float Camera2d::getZoom() const noexcept
{
return this->m_zoom;
}
glm::mat4 Camera2d::getViewMatrix() const noexcept
{
return glm::lookAt(this->position, this->position + this->front, this->up);
}
void Camera2d::mouseScrollCallback(const float& yOffset)
{
if (m_zoom >= 1.f && m_zoom <= 45.f)
m_zoom -= yOffset;
else if (m_zoom <= 1.f)
m_zoom = 1.f;
else if (m_zoom >= 45.f)
m_zoom = 45.f;
}
void Camera2d::update()
{
// Calculate the new Front vector
glm::vec3 _front;
_front.x = cos(glm::radians(this->m_yaw)) * cos(glm::radians(this->m_pitch));
_front.y = sin(glm::radians(this->m_pitch));
_front.z = cos(glm::radians(this->m_pitch)) * sin(glm::radians(this->m_yaw));
this->front = glm::normalize(_front);
// Also re-calculate the Right and Up vector
this->right = glm::normalize(glm::cross(this->front, this->worldUp)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
this->up = glm::normalize(glm::cross(this->right, this->front));
}
and in main i try smth like this in render loop
// pass projection matrix to shader
glm::mat4 projection = glm::perspective(glm::radians(camera.getZoom()),
static_cast<float>(WIDTH) / static_cast<float>(HEIGHT),
0.1f,
10000.f);
shaderProg.setMat4("projecton", projection);
// camera view transformation
glm::mat4 view = camera.getViewMatrix();
shaderProg.setMat4("view", view);
here i have just 1 model its my white bg-texture
glm::mat4 model = glm::translate(model, glm::vec3(0.f, 0.f, 0.f));
model = glm::rotate(model, glm::radians(0.f), glm::vec3(1.0f, 0.3f, 0.5f));
shaderProg.setMat4("model", model);
All code on github: here
You're working in 2D, forget about the camera, forget about projection, forget about following OpenGL tutorials, they're aimed at 3D graphics.
What you need is just a rectangle to fill your screen. Start with the vertices at the the corners of the screen, starting from the top-left corner and moving counterclockwise: (-1,1,0) (-1,-1,0) (1,-1,0) (1,1,0). Forget about Z, you're working in 2D.
You draw on a texture, and the texture coordinates are (0,1) (0,0) (1,0) (1,1), same order as above. Zooming is now just a matter of scaling the rectangle. You have a matrix to determine the scale and one to determine the position. Forget about rotations, front vectors and all that stuff. In the vertex shader you scale and then translate the vertices as usual, in this order. Done.
To interact for example you can have mouse wheel up increasing the scale factor and mouse wheel down decreasing it. Hold click and move the mouse to change the (x,y) position. Again forget about Z. Throw those values into the vertex shader and do the usual transformations.
I'm trying to move camera around the player.
Right now I'm using camera class like this:
Camera::Camera(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch)
{
this->position = position;
this->m_WorldUp = up;
this->up = up;
this->m_Yaw = yaw;
this->m_Pitch = pitch;
this->UpdateCameraVectors();
}
glm::mat4 Camera::getViewMatrix()
{
return glm::lookAt(position, position + m_Front, up);
}
void Camera::ProcessKeyboard(Camera_Movement direction, GLfloat deltaTime)
{
float velocity = moveSpeed * deltaTime;
switch (direction) {
case FORWARD: position += m_Front * velocity; break;
case BACKWARD: position -= m_Front * velocity; break;
case LEFT: position -= m_Right * velocity; break;
case RIGHT: position += m_Right * velocity; break;
case UPWARDS: position += m_WorldUp * velocity; break;
case DOWNWARDS: position -= m_WorldUp * velocity; break;
}
}
void Camera::ProcessMouseMovement(GLfloat xOffset, GLfloat yOffset, GLboolean constrainPitch)
{
xOffset *= sensitivity;
yOffset *= sensitivity;
m_Yaw += xOffset;
m_Pitch += yOffset;
if (constrainPitch) {
if (m_Pitch > 89.0f) {
m_Pitch = 89.0f;
} else if (m_Pitch < -89.0f) {
m_Pitch = -89.0f;
}
}
UpdateCameraVectors();
}
void Camera::UpdateCameraVectors()
{
glm::vec3 front;
front.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
front.y = -sin(glm::radians(m_Pitch));
front.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
m_Front = glm::normalize(front);
m_Right = glm::normalize(glm::cross(m_Front, m_WorldUp));
up = glm::normalize(glm::cross(m_Right, m_Front));
}
It allows me to free look and move around the world.
Player's update method at the moment:
glm::mat4 projection = glm::mat4(1.0f);
projection = glm::perspective(glm::radians(45.0f), 16.0f / 9.0f, 0.1f, 1000.0f);
glm::mat4 view = glm::mat4(1.0f);
view = camera->getViewMatrix();
glm::mat4 model = glm::mat4(1.0f); {
glm::mat4 translate = glm::translate(model, position);
glm::mat4 rotate = glm::rotate(model, glm::radians(180.0f), glm::vec3(0.0f, 1.0f, 0.0f));
glm::mat4 scale = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));
model = translate * rotate * scale;
}
glm::mat4 mvp = projection * view * model;
GLint u_mvp = shader.GetUniformLocation("u_mvp");
glUniformMatrix4fv(u_mvp, 1, GL_FALSE, glm::value_ptr(mvp));
I know that I have to change something with view matrix, but I have not got enough knowledge.
How can I upgrade my camera class that it can look, rotate, around a player, like in a circle, an MMO RPG style?
The camera class itself should not be receiving keyboard updates at all - that should be done in the player class. Every time the player moves, update the camera class with its new position. See comments in below code for more details.
Camera::Camera(glm::vec3 position, glm::vec3 up, GLfloat yaw, GLfloat pitch, GLfloat dist)
{
m_WorldUp = up;
// this->up = up; <- delete this variable; lookAt computes it for us
m_Pos = position; // this is the *player* position
m_Yaw = yaw;
m_Pitch = pitch;
m_Dist = dist; // distance from the player
UpdateViewMatrix(true);
}
// private method
void Camera::UpdateViewMatrix(bool computeDir = false)
{
// compute the new direction
if (computeDir)
{
glm::vec3 radial;
radial.x = cos(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
radial.y = sin(glm::radians(m_Pitch)); // there was a sign error here
radial.z = sin(glm::radians(m_Yaw)) * cos(glm::radians(m_Pitch));
m_Dir = -radial;
}
glm::vec3 pos = m_Pos - m_Dist * m_Dir; // *camera* position
// additional view matrix member variable
// you were using lookAt in the wrong way
m_View = glm::lookAt(m_Pos, pos, m_WorldUp);
}
// public method - call this everytime the player moves
void Camera::UpdateTargetPosition(glm::vec3 const & pos)
{
m_Pos = pos;
UpdateViewMatrix();
}
void Camera::UpdateAngles(GLfloat yaw, GLfloat pitch, GLboolean constrainPitch)
{
if (constrainPitch) {
if (pitch > 89.0f) {
pitch = 89.0f;
} else if (pitch < -89.0f) {
pitch = -89.0f;
}
}
// if yaw is outside the conventional range (-180.0, 180.0], shift it
if (yaw < -180.0f || yaw > 180.0f) {
yaw -= floor((yaw + 180.0f) / 360.0f) * 360.0f;
}
m_Yaw = yaw;
m_Pitch = pitch;
UpdateViewMatrix(true);
}
void Camera::ProcessMouseMovement(GLfloat xOffset, GLfloat yOffset, GLboolean constrainPitch)
{
UpdateAngles(m_Yaw + xOffset * sensitivity,
m_Pitch + yOffset * sensitivity,
constrainPitch);
}
for mouse follow you need:
camera = inverse(player * camera_view_and_offset)
where player is your player direct matrix, camera_view_and_offset is the view offset and turn around matrix relative to your player coordinate system and camera is your camera inverse matrix you should use as part of modelview ....
for more info see:
Understanding 4x4 homogenous transform matrices
For fun, I've made a 3d camera in opengl. It works well, except for the fact that I cannot figure out how to limit rotation about the x-axis. If you scroll up too much, the up and down controls will invert. I've tried limiting the camFront.y variable to an arbitrary value, but the camera will still flip over the x-axis.
Here is my code:
#ifndef CAMERA_H
#define CAMERA_H
#include <GL/glew.h>
#include <GLFW/glfw3.h>
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/quaternion.hpp>
#include <glm/gtx/quaternion.hpp>
#define WORLD_UP glm::vec3(0.0f, 1.0f, 0.0f)
#include <iostream>
enum CamDirection {
CAM_FORWARD,
CAM_BACKWARD,
CAM_LEFT,
CAM_RIGHT
};
class Camera {
public:
void cameraUpdate();
glm::mat4 getViewMatrix();
Camera();
Camera(glm::vec3 startPosition);
void move(CamDirection dir, GLfloat deltaTime);
void look(double xOffset, double yOffset);
void update();
private:
glm::vec3 camPos;
glm::vec3 camFront;
glm::vec3 camUp;
glm::vec3 camRight;
const GLfloat camSpeed = 5.05f;
};
glm::mat4 Camera::getViewMatrix() {
return glm::lookAt(camPos, camPos + camFront, camUp);
}
Camera::Camera():
camPos (glm::vec3(0.0f, 0.0f, 0.0f)),
camFront(glm::vec3(0.0f, 0.0f, -1.0f)),
camUp (WORLD_UP)
{}
Camera::Camera(glm::vec3 startPos):
camPos (startPos),
camFront (glm::vec3(0.0f, 0.0f, -1.0f)),
camUp (WORLD_UP)
{}
void Camera::move(CamDirection dir, GLfloat deltaTime) {
const GLfloat v = camSpeed * deltaTime;
if (dir == CAM_FORWARD)
camPos += v * camFront;
else if (dir == CAM_BACKWARD)
camPos -= v * camFront;
else if (dir == CAM_RIGHT)
camPos += v * camRight;
else
camPos -= v * camRight;
}
void Camera::look(double xOffset, double yOffset) {
glm::quat startQuat = {0, camFront.x, camFront.y, camFront.z};
glm::quat rotation = glm::angleAxis((GLfloat)xOffset, glm::vec3(0.0f, 1.0f, 0.0f));
glm::quat view = startQuat * rotation;
rotation = glm::angleAxis((GLfloat)yOffset, glm::vec3(-1.0f, 0.0f, 0.0f));
view = view * rotation;
camFront = glm::vec3(view.x, view.y, view.z);
std::cerr << camFront.x << ' ' << camFront.y << ' ' << camFront.z << std::endl;
}
void Camera::update() {
// Also re-calculate the Right and Up vector
camRight = glm::normalize(glm::cross(camFront, WORLD_UP)); // Normalize the vectors, because their length gets closer to 0 the more you look up or down which results in slower movement.
camUp = glm::normalize(glm::cross(camRight, camFront));
}
#endif // CAMERA_H
How can I fix this?
You need to limit view.y and view.z values before assigning them to camFront, to be max 89 degrees and minimum -89 degrees. At 90 and -90 degrees it starts to invert. So a very simple approach could be,
if(view.y > 89)
{
view.y = 89;
}
if(view.y < -89)
{
view.y = -89;
}
if(view.z > 89)
{
view.z = 89;
}
if(view.z < -89)
{
view.z = -89;
}
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.