From d3c347ec05c123ea858abcabc1916c0b60f4fdc1 Mon Sep 17 00:00:00 2001 From: heinezen Date: Sat, 13 Jul 2024 16:09:06 +0200 Subject: [PATCH] renderer: Get frustum from camera. --- libopenage/renderer/camera/camera.cpp | 45 +++-- libopenage/renderer/camera/camera.h | 21 +- libopenage/renderer/camera/frustum.cpp | 189 +++++++++--------- libopenage/renderer/camera/frustum.h | 46 ++--- libopenage/renderer/demo/stresstest_1.cpp | 3 +- libopenage/renderer/stages/world/object.cpp | 8 +- .../renderer/stages/world/render_stage.cpp | 2 +- 7 files changed, 159 insertions(+), 155 deletions(-) diff --git a/libopenage/renderer/camera/camera.cpp b/libopenage/renderer/camera/camera.cpp index fd94c2ce57..0e2112b6c7 100644 --- a/libopenage/renderer/camera/camera.cpp +++ b/libopenage/renderer/camera/camera.cpp @@ -25,15 +25,20 @@ Camera::Camera(const std::shared_ptr &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] @@ -46,8 +51,7 @@ Camera::Camera(const std::shared_ptr &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}, @@ -57,13 +61,18 @@ Camera::Camera(const std::shared_ptr &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] @@ -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) { @@ -289,16 +304,8 @@ void Camera::init_uniform_buffer(const std::shared_ptr &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 diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index bc4959f589..26bebd7e60 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -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; @@ -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, 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; /** @@ -195,9 +193,12 @@ class Camera { */ const std::shared_ptr &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: /** @@ -293,12 +294,6 @@ class Camera { */ std::shared_ptr 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 diff --git a/libopenage/renderer/camera/frustum.cpp b/libopenage/renderer/camera/frustum.cpp index 912cf97a5b..03735247df 100644 --- a/libopenage/renderer/camera/frustum.cpp +++ b/libopenage/renderer/camera/frustum.cpp @@ -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 diff --git a/libopenage/renderer/camera/frustum.h b/libopenage/renderer/camera/frustum.h index 8dfe9372c5..bd87147177 100644 --- a/libopenage/renderer/camera/frustum.h +++ b/libopenage/renderer/camera/frustum.h @@ -14,33 +14,33 @@ namespace openage::renderer::camera { -class Frustum -{ +class Frustum { public: - Frustum(); + Frustum(); - void 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); + void 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); - bool is_in_frustum(Eigen::Vector3f& pos) const; + bool is_in_frustum(Eigen::Vector3f &pos) const; private: - Eigen::Vector3f top_face_normal; - float top_face_distance; - - Eigen::Vector3f bottom_face_normal; - float bottom_face_distance; - - Eigen::Vector3f right_face_normal; - float right_face_distance; - - Eigen::Vector3f left_face_normal; - float left_face_distance; - - Eigen::Vector3f far_face_normal; - float far_face_distance; - - Eigen::Vector3f near_face_normal; - float near_face_distance; - + Eigen::Vector3f top_face_normal; + Eigen::Vector3f bottom_face_normal; + Eigen::Vector3f right_face_normal; + Eigen::Vector3f left_face_normal; + Eigen::Vector3f far_face_normal; + Eigen::Vector3f near_face_normal; + + float top_face_distance; + float bottom_face_distance; + float right_face_distance; + float left_face_distance; + float far_face_distance; + float near_face_distance; }; } // namespace openage::renderer::camera diff --git a/libopenage/renderer/demo/stresstest_1.cpp b/libopenage/renderer/demo/stresstest_1.cpp index 9a113eb85d..99e6e253ef 100644 --- a/libopenage/renderer/demo/stresstest_1.cpp +++ b/libopenage/renderer/demo/stresstest_1.cpp @@ -47,8 +47,7 @@ void renderer_stresstest_1(const util::Path &path) { Eigen::Vector3f{17.0f, 10.0f, 7.0f}, 1.f, 64.f, - 1.f / 49.f, - true); + 1.f / 49.f); auto cam_unifs = camera->get_uniform_buffer()->create_empty_input(); cam_unifs->update( "view", diff --git a/libopenage/renderer/stages/world/object.cpp b/libopenage/renderer/stages/world/object.cpp index d8d9120c69..da326a4faf 100644 --- a/libopenage/renderer/stages/world/object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -11,6 +11,7 @@ #include #include "renderer/camera/camera.h" +#include "renderer/camera/frustum.h" #include "renderer/definitions.h" #include "renderer/resources/animation/angle_info.h" #include "renderer/resources/animation/animation_info.h" @@ -225,12 +226,9 @@ void WorldObject::set_uniforms(std::vector &camera) { - if (!camera->using_frustum_culling()) { - return true; - } - Eigen::Vector3f current_pos = this->position.get(this->last_update).to_world_space(); - return camera->is_in_frustum(current_pos); + auto frustum = camera->get_frustum(); + return frustum.is_in_frustum(current_pos); } } // namespace openage::renderer::world diff --git a/libopenage/renderer/stages/world/render_stage.cpp b/libopenage/renderer/stages/world/render_stage.cpp index 816203b9d8..bfa745d19d 100644 --- a/libopenage/renderer/stages/world/render_stage.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -59,7 +59,7 @@ void WorldRenderStage::update() { std::unique_lock lock{this->mutex}; auto current_time = this->clock->get_real_time(); for (auto &obj : this->render_objects) { - if (!obj->within_camera_frustum(this->camera)) { + if (not obj->within_camera_frustum(this->camera)) { continue; }