Skip to content

Commit

Permalink
renderer: Get frustum from camera.
Browse files Browse the repository at this point in the history
  • Loading branch information
heinezen committed Jul 13, 2024
1 parent 36cd4d5 commit d3c347e
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 155 deletions.
45 changes: 26 additions & 19 deletions libopenage/renderer/camera/camera.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,15 +25,20 @@ Camera::Camera(const std::shared_ptr<Renderer> &renderer,
moved{true},
zoom_changed{true},
view{Eigen::Matrix4f::Identity()},
proj{Eigen::Matrix4f::Identity()},
frustum_culling{false} {
proj{Eigen::Matrix4f::Identity()} {
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 = 0.7f * this->default_zoom_ratio * this->zoom;
frustum.Recalculate(this->viewport_size, near_distance, far_distance, this->scene_pos, cam_direction, Eigen::Vector3f(0.0f, 1.0f, 0.0f), real_zoom);
frustum.Recalculate(this->viewport_size,
near_distance,
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]
Expand All @@ -46,8 +51,7 @@ Camera::Camera(const std::shared_ptr<Renderer> &renderer,
Eigen::Vector3f scene_pos,
float zoom,
float max_zoom_out,
float default_zoom_ratio,
bool frustum_culling) :
float default_zoom_ratio) :
scene_pos{scene_pos},
viewport_size{viewport_size},
zoom{zoom},
Expand All @@ -57,13 +61,18 @@ Camera::Camera(const std::shared_ptr<Renderer> &renderer,
zoom_changed{true},
viewport_changed{true},
view{Eigen::Matrix4f::Identity()},
proj{Eigen::Matrix4f::Identity()},
frustum_culling{frustum_culling} {
proj{Eigen::Matrix4f::Identity()} {
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 = 0.7f * this->default_zoom_ratio * this->zoom;
frustum.Recalculate(this->viewport_size, near_distance, far_distance, this->scene_pos, cam_direction, Eigen::Vector3f(0.0f, 1.0f, 0.0f), real_zoom);
frustum.Recalculate(this->viewport_size,
near_distance,
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]
Expand Down Expand Up @@ -121,7 +130,13 @@ void Camera::move_to(Eigen::Vector3f scene_pos) {

// Make the frustum slightly bigger than the camera's view to ensure objects on the boundary get rendered
float real_zoom = 0.7f * this->default_zoom_ratio * this->zoom;
frustum.Recalculate(viewport_size, near_distance, far_distance, scene_pos, cam_direction, Eigen::Vector3f(0.0f, 1.0f, 0.0f), real_zoom);
frustum.Recalculate(viewport_size,
near_distance,
far_distance,
scene_pos,
cam_direction,
Eigen::Vector3f(0.0f, 1.0f, 0.0f),
real_zoom);
}

void Camera::move_rel(Eigen::Vector3f direction, float delta) {
Expand Down Expand Up @@ -289,16 +304,8 @@ void Camera::init_uniform_buffer(const std::shared_ptr<Renderer> &renderer) {
this->uniform_buffer = renderer->add_uniform_buffer(ubo_info);
}

bool Camera::using_frustum_culling() const {
return this->frustum_culling;
}

bool Camera::is_in_frustum(Eigen::Vector3f pos) const{
if (!this->frustum_culling) {
return true;
}

return frustum.is_in_frustum(pos);
Frustum Camera::get_frustum() const {
return this->frustum;
}

} // namespace openage::renderer::camera
21 changes: 8 additions & 13 deletions libopenage/renderer/camera/camera.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
#include "coord/scene.h"
#include "util/vector.h"

#include "frustum.h"
#include "renderer/camera/frustum.h"

namespace openage::renderer {
class Renderer;
Expand Down Expand Up @@ -63,15 +63,13 @@ class Camera {
* @param zoom Zoom level of the camera (defaults to 1.0f).
* @param max_zoom_out Maximum zoom out level (defaults to 64.0f).
* @param default_zoom_ratio Default zoom level calibration (defaults to (1.0f / 49)).
* @param frustum_culling Is frustum culling enables (defaults to false).
*/
Camera(const std::shared_ptr<Renderer> &renderer,
util::Vector2s viewport_size,
Eigen::Vector3f scene_pos,
float zoom = 1.0f,
float max_zoom_out = 64.0f,
float default_zoom_ratio = 1.0f / 49,
bool frustum_culling = false);
float default_zoom_ratio = 1.0f / 49);
~Camera() = default;

/**
Expand Down Expand Up @@ -195,9 +193,12 @@ class Camera {
*/
const std::shared_ptr<renderer::UniformBuffer> &get_uniform_buffer() const;

bool using_frustum_culling() const;

bool is_in_frustum(Eigen::Vector3f pos) const;
/**
* Get a frustum object for this camera.
*
* @return Frustum object.
*/
Frustum get_frustum() const;

private:
/**
Expand Down Expand Up @@ -293,12 +294,6 @@ class Camera {
*/
std::shared_ptr<renderer::UniformBuffer> uniform_buffer;

/**
* Is frustum culling enabled? If true, perform frustum culling.
* If false, all frustum checks will return true
*/
bool frustum_culling;

/**
* The frustum used to cull objects
* Will be recalculated regardless of whether frustum culling is enabled
Expand Down
189 changes: 97 additions & 92 deletions libopenage/renderer/camera/frustum.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,116 +15,121 @@
namespace openage::renderer::camera {

Frustum::Frustum() :
top_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
top_face_distance{0.0f},
bottom_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
bottom_face_distance{0.0f},
right_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
right_face_distance{0.0f},
left_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
left_face_distance{0.0f},
far_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
far_face_distance{0.0f},
near_face_normal{Eigen::Vector3f(0.0f, 0.0f, 0.0f)},
near_face_distance{0.0f}
{
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)} {
}

void Frustum::Recalculate(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) {
// offsets are adjusted by zoom
void Frustum::Recalculate(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) {
// 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 halfwidth = halfscreenwidth * real_zoom;
float halfheight = halfscreenheight * real_zoom;

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

// calculate up (u) and right (s) vectors for camera
// calculate up (u) and right (s) vectors for camera
// these define the camera plane in 3D space that the input
Eigen::Vector3f f = center - eye;
Eigen::Vector3f f = center - eye;
f.normalize();
Eigen::Vector3f s = f.cross(up_direction);
s.normalize();
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;

// The frustum is a 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;
Eigen::Vector3f near_top_right = near_position + s * halfwidth + u * halfheight;
Eigen::Vector3f near_bottom_left = near_position - s * halfwidth - u * halfheight;
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_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 = -1.0f * cam_direction.normalized();
this->far_face_normal = cam_direction.normalized();

this->near_face_distance = this->near_face_normal.dot(near_position) * -1.0f;
this->far_face_distance = this->far_face_normal.dot(far_position) * -1.0f;

// Each left, right, top, and bottom plane are defined by three points on the plane
this->left_face_normal = (near_bottom_left - near_top_left).cross(far_bottom_left - near_bottom_left);
this->right_face_normal = (far_bottom_right - near_bottom_right).cross(near_bottom_right - near_top_right);
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);

this->left_face_normal.normalize();
this->right_face_normal.normalize();
this->top_face_normal.normalize();
this->bottom_face_normal.normalize();

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);
this->bottom_face_distance = this->bottom_face_normal.dot(near_bottom_left);
Eigen::Vector3f near_position = scene_pos - direction * near_distance;
Eigen::Vector3f far_position = scene_pos + direction * far_distance;

// The frustum is a 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;
Eigen::Vector3f near_top_right = near_position + s * halfwidth + u * halfheight;
Eigen::Vector3f near_bottom_left = near_position - s * halfwidth - u * halfheight;
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_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 = -1.0f * cam_direction.normalized();
this->far_face_normal = cam_direction.normalized();

this->near_face_distance = this->near_face_normal.dot(near_position) * -1.0f;
this->far_face_distance = this->far_face_normal.dot(far_position) * -1.0f;

// Each left, right, top, and bottom plane are defined by three points on the plane
this->left_face_normal = (near_bottom_left - near_top_left).cross(far_bottom_left - near_bottom_left);
this->right_face_normal = (far_bottom_right - near_bottom_right).cross(near_bottom_right - near_top_right);
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);

this->left_face_normal.normalize();
this->right_face_normal.normalize();
this->top_face_normal.normalize();
this->bottom_face_normal.normalize();

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);
this->bottom_face_distance = this->bottom_face_normal.dot(near_bottom_left);
}

bool Frustum::is_in_frustum(Eigen::Vector3f& pos) const {
// For each plane, if a point is behind one of the frustum planes, it is not within the frustum
float distance;

distance = this->top_face_normal.dot(pos) - this->top_face_distance;
if (distance < 0) {
return false;
}

distance = this->bottom_face_normal.dot(pos) - this->bottom_face_distance;
if (distance < 0) {
return false;
}

distance = this->left_face_normal.dot(pos) - this->left_face_distance;
if (distance < 0) {
return false;
}

distance = this->right_face_normal.dot(pos) - this->right_face_distance;
if (distance < 0) {
return false;
}

distance = this->far_face_normal.dot(pos) - this->far_face_distance;
if (distance < 0) {
return false;
}

distance = this->bottom_face_normal.dot(pos) - this->bottom_face_distance;
if (distance < 0) {
return false;
}

return true;
bool Frustum::is_in_frustum(Eigen::Vector3f &pos) const {
// For each plane, if a point is behind one of the frustum planes, it is not within the frustum
float distance;

distance = this->top_face_normal.dot(pos) - this->top_face_distance;
if (distance < 0) {
return false;
}

distance = this->bottom_face_normal.dot(pos) - this->bottom_face_distance;
if (distance < 0) {
return false;
}

distance = this->left_face_normal.dot(pos) - this->left_face_distance;
if (distance < 0) {
return false;
}

distance = this->right_face_normal.dot(pos) - this->right_face_distance;
if (distance < 0) {
return false;
}

distance = this->far_face_normal.dot(pos) - this->far_face_distance;
if (distance < 0) {
return false;
}

distance = this->bottom_face_normal.dot(pos) - this->bottom_face_distance;
if (distance < 0) {
return false;
}

return true;
}

}
} // namespace openage::renderer::camera
Loading

0 comments on commit d3c347e

Please sign in to comment.