Skip to content

Commit

Permalink
renderer: Construct frustum with parameters.
Browse files Browse the repository at this point in the history
  • Loading branch information
heinezen committed Jul 14, 2024
1 parent 8d5fd3e commit 6522a7d
Show file tree
Hide file tree
Showing 8 changed files with 155 additions and 77 deletions.
49 changes: 21 additions & 28 deletions libopenage/renderer/camera/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,18 @@ Camera::Camera(const std::shared_ptr<Renderer> &renderer,
moved{true},
zoom_changed{true},
view{Eigen::Matrix4f::Identity()},
proj{Eigen::Matrix4f::Identity()} {
proj{Eigen::Matrix4f::Identity()},
frustum{this->viewport_size,
DEFAULT_NEAR_DISTANCE,
DEFAULT_FAR_DISTANCE,
this->scene_pos,
CAM_DIRECTION,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
this->get_zoom_factor()} {
this->look_at_scene(Eigen::Vector3f(0.0f, 0.0f, 0.0f));

this->init_uniform_buffer(renderer);

// Make the frustum slightly bigger than the camera's view to ensure objects on the boundary get rendered
float real_zoom = get_zoom_factor();
frustum.update(this->viewport_size,
DEFAULT_NEAR_DISTANCE,
DEFAULT_FAR_DISTANCE,
this->scene_pos,
CAM_DIRECTION,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
real_zoom);

log::log(INFO << "Created new camera at position "
<< "(" << this->scene_pos[0]
<< ", " << this->scene_pos[1]
Expand All @@ -61,19 +58,16 @@ Camera::Camera(const std::shared_ptr<Renderer> &renderer,
zoom_changed{true},
viewport_changed{true},
view{Eigen::Matrix4f::Identity()},
proj{Eigen::Matrix4f::Identity()} {
proj{Eigen::Matrix4f::Identity()},
frustum{this->viewport_size,
DEFAULT_NEAR_DISTANCE,
DEFAULT_FAR_DISTANCE,
this->scene_pos,
CAM_DIRECTION,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
this->get_zoom_factor()} {
this->init_uniform_buffer(renderer);

// Make the frustum slightly bigger than the camera's view to ensure objects on the boundary get rendered
float real_zoom = this->get_zoom_factor();
frustum.update(this->viewport_size,
DEFAULT_NEAR_DISTANCE,
DEFAULT_FAR_DISTANCE,
this->scene_pos,
CAM_DIRECTION,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
real_zoom);

log::log(INFO << "Created new camera at position "
<< "(" << this->scene_pos[0]
<< ", " << this->scene_pos[1]
Expand Down Expand Up @@ -128,15 +122,14 @@ void Camera::move_to(Eigen::Vector3f scene_pos) {
this->scene_pos = scene_pos;
this->moved = true;

// Make the frustum slightly bigger than the camera's view to ensure objects on the boundary get rendered
float real_zoom = this->get_zoom_factor();
// Update frustum
frustum.update(viewport_size,
DEFAULT_NEAR_DISTANCE,
DEFAULT_FAR_DISTANCE,
scene_pos,
CAM_DIRECTION,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
real_zoom);
this->get_zoom_factor());
}

void Camera::move_rel(Eigen::Vector3f direction, float delta) {
Expand Down Expand Up @@ -183,7 +176,7 @@ const Eigen::Matrix4f &Camera::get_view_matrix() {

Eigen::Vector3f eye = this->scene_pos;
Eigen::Vector3f center = this->scene_pos + direction; // look in the direction of the camera
Eigen::Vector3f up = Eigen::Vector3f(0.0f, 1.0f, 0.0f);
Eigen::Vector3f up = CAM_UP;

Eigen::Vector3f f = center - eye;
f.normalize();
Expand Down Expand Up @@ -265,7 +258,7 @@ Eigen::Vector3f Camera::get_input_pos(const coord::input &coord) const {
auto direction = CAM_DIRECTION.normalized();
Eigen::Vector3f eye = this->scene_pos;
Eigen::Vector3f center = this->scene_pos + direction;
Eigen::Vector3f up = Eigen::Vector3f(0.0f, 1.0f, 0.0f);
Eigen::Vector3f up = CAM_UP;

Eigen::Vector3f f = center - eye;
f.normalize();
Expand Down Expand Up @@ -294,7 +287,7 @@ const std::shared_ptr<renderer::UniformBuffer> &Camera::get_uniform_buffer() con
return this->uniform_buffer;
}

Frustum Camera::get_frustum() const {
const Frustum &Camera::get_frustum() const {
return this->frustum;
}

Expand Down
8 changes: 5 additions & 3 deletions libopenage/renderer/camera/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ namespace camera {
/**
* Camera for selecting what part of the ingame world is displayed.
*
* The camera uses orthographic projection as it is primarily used for
* 2D rendering.
*
* TODO: Vulkan version.
*/
class Camera {
Expand Down Expand Up @@ -184,7 +187,7 @@ class Camera {
*
* @return Frustum object.
*/
Frustum get_frustum() const;
const Frustum &get_frustum() const;

private:
/**
Expand Down Expand Up @@ -292,8 +295,7 @@ class Camera {
std::shared_ptr<renderer::UniformBuffer> uniform_buffer;

/**
* The frustum used to cull objects
* Will be recalculated regardless of whether frustum culling is enabled
* Frustum (viewing volume) for culling rendering objects.
*/
Frustum frustum;
};
Expand Down
5 changes: 5 additions & 0 deletions libopenage/renderer/camera/definitions.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@ static const Eigen::Vector3f CAM_DIRECTION{
-1 * (std::sqrt(6.f) / 4),
};

/**
* Camera up vector.
*/
static const Eigen::Vector3f CAM_UP{0.0f, 1.0f, 0.0f};

/**
* Default near distance of the camera.
*
Expand Down
60 changes: 33 additions & 27 deletions libopenage/renderer/camera/frustum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,39 +14,40 @@

namespace openage::renderer::camera {

Frustum::Frustum() :
top_face_distance{0.0f},
bottom_face_distance{0.0f},
right_face_distance{0.0f},
left_face_distance{0.0f},
far_face_distance{0.0f},
near_face_distance{0.0f},
top_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
bottom_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
right_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
left_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
far_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
near_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)} {
Frustum::Frustum(const util::Vector2s &viewport_size,
const float near_distance,
const float far_distance,
const Eigen::Vector3f &camera_pos,
const Eigen::Vector3f &cam_direction,
const Eigen::Vector3f &up_direction,
const float zoom_factor) {
this->update(viewport_size,
near_distance,
far_distance,
camera_pos,
cam_direction,
up_direction,
zoom_factor);
}

void Frustum::update(util::Vector2s &viewport_size,
float near_distance,
float far_distance,
Eigen::Vector3f &scene_pos,
Eigen::Vector3f cam_direction,
Eigen::Vector3f up_direction,
float real_zoom) {
void Frustum::update(const util::Vector2s &viewport_size,
const float near_distance,
const float far_distance,
const Eigen::Vector3f &camera_pos,
const Eigen::Vector3f &cam_direction,
const Eigen::Vector3f &up_direction,
const float zoom_factor) {
// offsets are adjusted by zoom
// this is the same calculation as for the projection matrix
float halfscreenwidth = viewport_size[0] / 2;
float halfscreenheight = viewport_size[1] / 2;

float halfwidth = halfscreenwidth * real_zoom;
float halfheight = halfscreenheight * real_zoom;
float halfwidth = halfscreenwidth * zoom_factor;
float halfheight = halfscreenheight * zoom_factor;

Eigen::Vector3f direction = cam_direction.normalized();
Eigen::Vector3f eye = scene_pos;
Eigen::Vector3f center = scene_pos + direction;
Eigen::Vector3f eye = camera_pos;
Eigen::Vector3f center = camera_pos + direction;

// calculate up (u) and right (s) vectors for camera
// these define the camera plane in 3D space that the input
Expand All @@ -57,8 +58,9 @@ void Frustum::update(util::Vector2s &viewport_size,
Eigen::Vector3f u = s.cross(f);
u.normalize();

Eigen::Vector3f near_position = scene_pos + direction * near_distance;
Eigen::Vector3f far_position = scene_pos + direction * far_distance;
// calculate distance of the camera position to the near and far plane
Eigen::Vector3f near_position = camera_pos + direction * near_distance;
Eigen::Vector3f far_position = camera_pos + direction * far_distance;

// The frustum is a cuboid box with 8 points defining it (4 on the near plane, 4 on the far plane)
Eigen::Vector3f near_top_left = near_position - s * halfwidth + u * halfheight;
Expand All @@ -67,14 +69,15 @@ void Frustum::update(util::Vector2s &viewport_size,
Eigen::Vector3f near_bottom_right = near_position + s * halfwidth - u * halfheight;

Eigen::Vector3f far_top_left = far_position - s * halfwidth + u * halfheight;
Eigen::Vector3f far_top_right = far_position + s * halfwidth + u * halfheight;
// Eigen::Vector3f far_top_right = far_position + s * halfwidth + u * halfheight; // unused
Eigen::Vector3f far_bottom_left = far_position - s * halfwidth - u * halfheight;
Eigen::Vector3f far_bottom_right = far_position + s * halfwidth - u * halfheight;

// The near and far planes are easiest to compute, as they should be in the direction of the camera
this->near_face_normal = cam_direction.normalized();
this->far_face_normal = -1.0f * cam_direction.normalized();

// The distance of the plane from the origin is the dot product of the normal and a point on the plane
this->near_face_distance = this->near_face_normal.dot(near_position);
this->far_face_distance = this->far_face_normal.dot(far_position);

Expand All @@ -84,11 +87,14 @@ void Frustum::update(util::Vector2s &viewport_size,
this->top_face_normal = (near_top_right - near_top_left).cross(near_top_left - far_top_left);
this->bottom_face_normal = (near_bottom_left - far_bottom_left).cross(near_bottom_right - near_bottom_left);

// for orthographic projection, the normal of left/right should equal -s/s
// and the normal of top/bottom should equal u/-u
this->left_face_normal.normalize();
this->right_face_normal.normalize();
this->top_face_normal.normalize();
this->bottom_face_normal.normalize();

// calculate the distance of the planes to the origin
this->left_face_distance = this->left_face_normal.dot(near_bottom_left);
this->right_face_distance = this->right_face_normal.dot(far_bottom_right);
this->top_face_distance = this->top_face_normal.dot(near_top_right);
Expand Down
86 changes: 75 additions & 11 deletions libopenage/renderer/camera/frustum.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,36 +15,54 @@
namespace openage::renderer::camera {

/**
* Frustum for a camera.
* Frustum for a camera. The frustum is defined by 6 planes (top, bottom, right, left, far, near) that
* define the viewing volume of the camera.
*
* Used for frustum culling (https://learnopengl.com/Guest-Articles/2021/Scene/Frustum-Culling)
* in the renderer.
*
* As the openage camera uses orthographic projection, the frustum is a box, i.e. plane opposite
* to each other are parallel.
*/
class Frustum {
public:
/**
* Create a new frustum.
*
* @param viewport_size Size of the viewport (width x height).
* @param near_distance Near distance of the frustum.
* @param far_distance Far distance of the frustum.
* @param camera_pos Scene position of the camera.
* @param cam_direction Direction the camera is looking at.
* @param up_direction Up direction of the camera.
* @param zoom_factor Zoom factor of the camera.
*/
Frustum();
Frustum(const util::Vector2s &viewport_size,
const float near_distance,
const float far_distance,
const Eigen::Vector3f &camera_pos,
const Eigen::Vector3f &cam_direction,
const Eigen::Vector3f &up_direction,
const float zoom_factor);

/**
* Update this frustum with the new camera parameters.
*
* @param viewport_size Size of the viewport (width x height).
* @param near_distance Near distance of the frustum.
* @param far_distance Far distance of the frustum.
* @param scene_pos Scene position of the camera.
* @param camera_pos Scene position of the camera.
* @param cam_direction Direction the camera is looking at.
* @param up_direction Up direction of the camera.
* @param real_zoom Zoom factor of the camera.
* @param zoom_factor Zoom factor of the camera.
*/
void update(util::Vector2s &viewport_size,
float near_distance,
float far_distance,
Eigen::Vector3f &scene_pos,
Eigen::Vector3f cam_direction,
Eigen::Vector3f up_direction,
float real_zoom);
void update(const util::Vector2s &viewport_size,
const float near_distance,
const float far_distance,
const Eigen::Vector3f &camera_pos,
const Eigen::Vector3f &cam_direction,
const Eigen::Vector3f &up_direction,
const float zoom_factor);

/**
* Check whether a point in the scene is inside the frustum.
Expand All @@ -56,18 +74,64 @@ class Frustum {
bool in_frustum(Eigen::Vector3f &scene_pos) const;

private:
/**
* Normal vector of the top face.
*/
Eigen::Vector3f top_face_normal;

/**
* Normal vector of the bottom face.
*/
Eigen::Vector3f bottom_face_normal;

/**
* Normal vector of the right face.
*/
Eigen::Vector3f right_face_normal;

/**
* Normal vector of the left face.
*/
Eigen::Vector3f left_face_normal;

/**
* Normal vector of the far face.
*/
Eigen::Vector3f far_face_normal;

/**
* Normal vector of the near face.
*/
Eigen::Vector3f near_face_normal;

/**
* Shortest distance from the top face to the scene origin.
*/
float top_face_distance;

/**
* Shortest distance from the bottom face to the scene origin.
*/
float bottom_face_distance;

/**
* Shortest distance from the right face to the scene origin.
*/
float right_face_distance;

/**
* Shortest distance from the left face to the scene origin.
*/
float left_face_distance;

/**
* Shortest distance from the far face to the scene origin.
*/
float far_face_distance;

/**
* Shortest distance from the near face to the scene origin.
*/
float near_face_distance;
};
} // namespace openage::renderer::camera
Loading

0 comments on commit 6522a7d

Please sign in to comment.