diff --git a/libopenage/renderer/camera/camera.cpp b/libopenage/renderer/camera/camera.cpp index 5b43bc1bb5..309b29737c 100644 --- a/libopenage/renderer/camera/camera.cpp +++ b/libopenage/renderer/camera/camera.cpp @@ -25,21 +25,18 @@ Camera::Camera(const std::shared_ptr &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] @@ -61,19 +58,16 @@ Camera::Camera(const std::shared_ptr &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] @@ -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) { @@ -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(); @@ -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(); @@ -294,7 +287,7 @@ const std::shared_ptr &Camera::get_uniform_buffer() con return this->uniform_buffer; } -Frustum Camera::get_frustum() const { +const Frustum &Camera::get_frustum() const { return this->frustum; } diff --git a/libopenage/renderer/camera/camera.h b/libopenage/renderer/camera/camera.h index e1fd0f0f44..8fc72b2a72 100644 --- a/libopenage/renderer/camera/camera.h +++ b/libopenage/renderer/camera/camera.h @@ -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 { @@ -184,7 +187,7 @@ class Camera { * * @return Frustum object. */ - Frustum get_frustum() const; + const Frustum &get_frustum() const; private: /** @@ -292,8 +295,7 @@ class Camera { std::shared_ptr 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; }; diff --git a/libopenage/renderer/camera/definitions.h b/libopenage/renderer/camera/definitions.h index 3ce6aa4f22..9586239a2f 100644 --- a/libopenage/renderer/camera/definitions.h +++ b/libopenage/renderer/camera/definitions.h @@ -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. * diff --git a/libopenage/renderer/camera/frustum.cpp b/libopenage/renderer/camera/frustum.cpp index 234dbe500a..48894324e9 100644 --- a/libopenage/renderer/camera/frustum.cpp +++ b/libopenage/renderer/camera/frustum.cpp @@ -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 @@ -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; @@ -67,7 +69,7 @@ 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; @@ -75,6 +77,7 @@ void Frustum::update(util::Vector2s &viewport_size, 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); @@ -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); diff --git a/libopenage/renderer/camera/frustum.h b/libopenage/renderer/camera/frustum.h index d1d32ce296..e216156a78 100644 --- a/libopenage/renderer/camera/frustum.h +++ b/libopenage/renderer/camera/frustum.h @@ -15,17 +15,35 @@ 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. @@ -33,18 +51,18 @@ class 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 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. @@ -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 diff --git a/libopenage/renderer/stages/world/object.cpp b/libopenage/renderer/stages/world/object.cpp index a33f89f152..d099e2451a 100644 --- a/libopenage/renderer/stages/world/object.cpp +++ b/libopenage/renderer/stages/world/object.cpp @@ -10,7 +10,6 @@ #include -#include "renderer/camera/camera.h" #include "renderer/camera/frustum.h" #include "renderer/definitions.h" #include "renderer/resources/animation/angle_info.h" @@ -225,10 +224,9 @@ void WorldObject::set_uniforms(std::vectorlayer_uniforms = std::move(uniforms); } -bool WorldObject::within_camera_frustum(const std::shared_ptr &camera, - const time::time_t &time) { +bool WorldObject::is_visible(const camera::Frustum &frustum, + const time::time_t &time) { Eigen::Vector3f current_pos = this->position.get(time).to_world_space(); - auto frustum = camera->get_frustum(); return frustum.in_frustum(current_pos); } diff --git a/libopenage/renderer/stages/world/object.h b/libopenage/renderer/stages/world/object.h index cdad725ae8..63a07696eb 100644 --- a/libopenage/renderer/stages/world/object.h +++ b/libopenage/renderer/stages/world/object.h @@ -20,7 +20,7 @@ namespace openage::renderer { class UniformInput; namespace camera { -class Camera; +class Frustum; } namespace resources { @@ -133,8 +133,16 @@ class WorldObject { */ void set_uniforms(std::vector> &&uniforms); - bool within_camera_frustum(const std::shared_ptr &camera, - const time::time_t &time); + /** + * Check whether the object is visible in the camera view. + * + * @param frustum Camera frustum for culling. + * @param time Current simulation time. + * + * @return true if the object is visible, else false. + */ + bool is_visible(const camera::Frustum &frustum, + const time::time_t &time); /** * Shader uniform IDs for setting uniform values. diff --git a/libopenage/renderer/stages/world/render_stage.cpp b/libopenage/renderer/stages/world/render_stage.cpp index 2e70776c18..1b3608885b 100644 --- a/libopenage/renderer/stages/world/render_stage.cpp +++ b/libopenage/renderer/stages/world/render_stage.cpp @@ -3,6 +3,7 @@ #include "render_stage.h" #include "renderer/camera/camera.h" +#include "renderer/camera/frustum.h" #include "renderer/opengl/context.h" #include "renderer/render_pass.h" #include "renderer/render_target.h" @@ -58,10 +59,11 @@ void WorldRenderStage::add_render_entity(const std::shared_ptrmutex}; auto current_time = this->clock->get_real_time(); + auto &camera_frustum = this->camera->get_frustum(); for (auto &obj : this->render_objects) { obj->fetch_updates(current_time); - if (not obj->within_camera_frustum(this->camera, current_time)) { + if (not obj->is_visible(camera_frustum, current_time)) { continue; }