-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Frustum culling #1642
base: master
Are you sure you want to change the base?
Frustum culling #1642
Changes from all commits
2738b1c
5e4a50c
7b7383a
96a7fa6
68a91a9
5e9dd26
de32bb9
11be4a8
6dd2172
ef03a27
64a2e82
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -20,3 +20,4 @@ Tobias Feldballe <[email protected]> <[email protected]> | |
Tobias Feldballe <[email protected]> <[email protected]> | ||
Jonas Borchelt <[email protected]> | ||
Derek Frogget <[email protected]> <[email protected]> | ||
Nikhil Ghosh <[email protected]> |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
add_sources(libopenage | ||
camera.cpp | ||
frustum.cpp | ||
) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,4 @@ | ||
// Copyright 2022-2023 the openage authors. See copying.md for legal info. | ||
// Copyright 2022-2024 the openage authors. See copying.md for legal info. | ||
|
||
#include "camera.h" | ||
|
||
|
@@ -25,14 +25,19 @@ 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_culling{false} { | ||
this->look_at_scene(Eigen::Vector3f(0.0f, 0.0f, 0.0f)); | ||
|
||
resources::UBOInput view_input{"view", resources::ubo_input_t::M4F32}; | ||
resources::UBOInput proj_input{"proj", resources::ubo_input_t::M4F32}; | ||
auto ubo_info = resources::UniformBufferInfo{resources::ubo_layout_t::STD140, {view_input, proj_input}}; | ||
this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); | ||
|
||
// 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; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The factor used here ( There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also, you don't need to calculate the frustum here, since line 30 moves the camera (and calls |
||
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] | ||
<< ", " << this->scene_pos[1] | ||
|
@@ -44,7 +49,8 @@ Camera::Camera(const std::shared_ptr<Renderer> &renderer, | |
Eigen::Vector3f scene_pos, | ||
float zoom, | ||
float max_zoom_out, | ||
float default_zoom_ratio) : | ||
float default_zoom_ratio, | ||
bool frustum_culling) : | ||
scene_pos{scene_pos}, | ||
viewport_size{viewport_size}, | ||
zoom{zoom}, | ||
|
@@ -54,12 +60,17 @@ 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_culling{frustum_culling} { | ||
resources::UBOInput view_input{"view", resources::ubo_input_t::M4F32}; | ||
resources::UBOInput proj_input{"proj", resources::ubo_input_t::M4F32}; | ||
auto ubo_info = resources::UniformBufferInfo{resources::ubo_layout_t::STD140, {view_input, proj_input}}; | ||
this->uniform_buffer = renderer->add_uniform_buffer(ubo_info); | ||
|
||
// 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); | ||
Comment on lines
+71
to
+72
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here for the factor. |
||
|
||
log::log(INFO << "Created new camera at position " | ||
<< "(" << this->scene_pos[0] | ||
<< ", " << this->scene_pos[1] | ||
|
@@ -113,6 +124,10 @@ 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 = 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); | ||
Comment on lines
+129
to
+130
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. same here for the factor. |
||
} | ||
|
||
void Camera::move_rel(Eigen::Vector3f direction, float delta) { | ||
|
@@ -269,4 +284,16 @@ const std::shared_ptr<renderer::UniformBuffer> &Camera::get_uniform_buffer() con | |
return this->uniform_buffer; | ||
} | ||
|
||
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); | ||
} | ||
|
||
} // namespace openage::renderer::camera |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,8 @@ | |
#include "coord/scene.h" | ||
#include "util/vector.h" | ||
|
||
#include "frustum.h" | ||
|
||
namespace openage::renderer { | ||
class Renderer; | ||
class UniformBuffer; | ||
|
@@ -30,6 +32,9 @@ static const Eigen::Vector3f cam_direction{ | |
-1 * (std::sqrt(6.f) / 4), | ||
}; | ||
|
||
static const float near_distance = 0.01f; | ||
static const float far_distance = 100.0f; | ||
|
||
Comment on lines
+35
to
+37
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These should be configured for the frustum class and not in the camera file. |
||
/** | ||
* Camera for selecting what part of the ingame world is displayed. | ||
* | ||
|
@@ -56,13 +61,15 @@ 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); | ||
float default_zoom_ratio = 1.0f / 49, | ||
bool frustum_culling = false); | ||
~Camera() = default; | ||
|
||
/** | ||
|
@@ -186,6 +193,10 @@ 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; | ||
|
||
private: | ||
/** | ||
* Position in the 3D scene. | ||
|
@@ -272,6 +283,18 @@ class Camera { | |
* Uniform buffer for the camera matrices. | ||
*/ | ||
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 | ||
*/ | ||
Frustum frustum; | ||
Comment on lines
+287
to
+297
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I thought about this for a while and I think it's better if the frustum is not a camera member for now. Instead, a frustum should be created and returned by the camera using a To create the frustum, you can add a function like this: /**
* Get a frustum for this camera
*
* @param scalefactor Resize the frustum to ensure objects on the boundary get rendered
*/
Frustum get_frustum(float scalefactor)
|
||
}; | ||
|
||
} // namespace camera | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
// Copyright 2024-2024 the openage authors. See copying.md for legal info. | ||
|
||
#include "frustum.h" | ||
|
||
#include <array> | ||
#include <numbers> | ||
#include <string> | ||
#include <utility> | ||
|
||
#include "coord/pixel.h" | ||
#include "coord/scene.h" | ||
#include "renderer/renderer.h" | ||
#include "renderer/resources/buffer_info.h" | ||
|
||
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} | ||
{ | ||
} | ||
|
||
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 halfheight = halfscreenheight * real_zoom; | ||
|
||
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 | ||
// these define the camera plane in 3D space that the input | ||
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); | ||
} | ||
|
||
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; | ||
} | ||
|
||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright 2024-2024 the openage authors. See copying.md for legal info. | ||
|
||
#pragma once | ||
|
||
#include <cmath> | ||
#include <cstddef> | ||
#include <memory> | ||
|
||
#include <eigen3/Eigen/Dense> | ||
|
||
#include "coord/pixel.h" | ||
#include "coord/scene.h" | ||
#include "util/vector.h" | ||
|
||
namespace openage::renderer::camera { | ||
|
||
class Frustum | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please document al public functions :) |
||
{ | ||
public: | ||
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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. function names should be lower case, see our styleguides in https://github.com/SFTtech/openage/tree/master/doc/code_style |
||
|
||
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; | ||
Comment on lines
+27
to
+43
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please document these members :) |
||
|
||
}; | ||
} // namespace openage::renderer::camera |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -6,6 +6,7 @@ add_sources(libopenage | |
demo_4.cpp | ||
demo_5.cpp | ||
stresstest_0.cpp | ||
stresstest_1.cpp | ||
tests.cpp | ||
util.cpp | ||
) | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the CI fails because you are missing an
|
at the end of the table. Then the mailmap entry is also not necessary.