Camera
Previously, I described a simple view matrix that is fixed in one position. But I would like to make this a bit more interesting by introducing the Camera struct.
The Camera is an entity that moves around the screen and can rotate in 3 axes to view the scene. For example, how far objects look like. I have taken this camera out of LearnOpenGL
struct Camera {
// camera Attributes
Vec3 position;
Vec3 front;
Vec3 up;
Vec3 right;
Vec3 worldUp;
// euler Angles
float yaw;
float pitch;
// camera options
float movementSpeed;
float mouseSensitivity;
float zoom;
};
I should be able to control this camera with the keyboard and mouse. Finally, I want to be able to get the view matrix of the camera at any point. These are the functions of this camera:
Camera Camera_Create(Vec3 position, Vec3 up, float yaw, float pitch);
Mat4 Camera_GetViewMatrix(Camera *camera);
// processes input received from any keyboard-like input system. Accepts input
// parameter in the form of camera defined ENUM (to abstract it from windowing
// systems)
void Camera_ProcessKeyboard(Camera *camera, CameraMovement direction,
float deltaTime);
// processes input received from a mouse input system. Expects the offset
// value in both the x and y direction.
void Camera_ProcessMouseMovement(Camera *camera, float offsetX, float offsetY,
bool constrainPitch);
void Camera_ProcessMouseScroll(Camera *camera, float offsetY);
void Camera_UpdateVectors(Camera *camera);
I have also added some constants for some of the camera values:
// Defines several possible options for camera movement. Used as abstraction to
// stay away from window-system specific input methods
enum CameraMovement { FORWARD, BACKWARD, LEFT, RIGHT, UP, DOWN };
// Default camera values
const float YAW = -90.0f;
const float PITCH = 0.0f;
const float SPEED = 2.5f;
const float SENSITIVITY = 0.1f;
const float ZOOM = 45.0f;
I need to add the camera to the renderer entity to be able to access it inside the renderer functions. In the future I could create a different struct called Scene that holds information of all the entities that will be renderer, the lights, and the camera.
struct Renderer {
...
Camera camera;
};
...
Mat4 view = Camera_GetViewMatrix(&r->camera);
float aspect = (float)r->width / r->height;
Mat4 projection = Mat4_Perspective(DegToRadians(r->camera.zoom), aspect, 0.1f, 100.0f);
Now I can use the Camera_GetViewMatrix function to get the view matrix and transform the object.
Here is the entire implementation of the camera:
#include "camera.h"
#include "math.h"
#include <cmath>
Camera Camera_Create(Vec3 position, Vec3 up, float yaw, float pitch) {
Camera camera = {};
camera.front = Vec3{0.0f, 0.0f, -1.0f};
camera.movementSpeed = SPEED;
camera.mouseSensitivity = SENSITIVITY;
camera.zoom = ZOOM;
camera.position = position;
camera.worldUp = up;
camera.yaw = yaw;
camera.pitch = pitch;
Camera_UpdateVectors(&camera);
return camera;
}
Mat4 Camera_GetViewMatrix(Camera *camera) {
return Mat4_LookAt(camera->position,
Vec3_Add(camera->position, camera->front), camera->up);
}
void Camera_ProcessKeyboard(Camera *camera, CameraMovement direction,
float deltaTime) {
float velocity = camera->movementSpeed * deltaTime;
if (direction == FORWARD) {
camera->position = Vec3_Add(camera->position,
Vec3_ScalarMult(camera->front, velocity));
}
if (direction == BACKWARD) {
camera->position = Vec3_Subtract(
camera->position, Vec3_ScalarMult(camera->front, velocity));
}
if (direction == LEFT) {
camera->position = Vec3_Subtract(
camera->position, Vec3_ScalarMult(camera->right, velocity));
}
if (direction == RIGHT) {
camera->position = Vec3_Add(camera->position,
Vec3_ScalarMult(camera->right, velocity));
}
if (direction == UP) {
camera->position =
Vec3_Add(camera->position, Vec3_ScalarMult(camera->up, velocity));
}
if (direction == DOWN) {
camera->position = Vec3_Subtract(camera->position,
Vec3_ScalarMult(camera->up, velocity));
}
}
void Camera_ProcessMouseMovement(Camera *camera, float offsetX, float offsetY,
bool constrainPitch) {
offsetX *= camera->mouseSensitivity;
offsetY *= camera->mouseSensitivity;
camera->yaw += offsetX;
camera->pitch += offsetY;
// make sure that when pitch is out of bounds, screen doesn't get flipped
if (constrainPitch) {
if (camera->pitch > 89.0f) {
camera->pitch = 89.0f;
}
if (camera->pitch < -89.0f) {
camera->pitch = -89.0f;
}
}
Camera_UpdateVectors(camera);
}
void Camera_ProcessMouseScroll(Camera *camera, float offsetY) {
camera->zoom -= (float)offsetY;
if (camera->zoom < 1.0f) {
camera->zoom = 1.0f;
}
if (camera->zoom > 45.0f) {
camera->zoom = 45.0f;
}
}
void Camera_UpdateVectors(Camera *camera) {
// calculate the new Front vector
Vec3 front;
front.x = cos(DegToRadians(camera->yaw)) * cos(DegToRadians(camera->pitch));
front.y = sin(DegToRadians(camera->pitch));
front.z = sin(DegToRadians(camera->yaw)) * cos(DegToRadians(camera->pitch));
camera->front = Vec3_Normalize(front);
// also re-calculate the Right and Up vector
// normalize the vectors, because their length gets
// closer to 0 the more you look up or down which
// results in slower movement.
camera->right = Vec3_Normalize(Vec3_Cross(camera->front, camera->worldUp));
camera->up = Vec3_Normalize(Vec3_Cross(camera->right, camera->front));
}