From 4f2f558123db15393607d8b21b949d7798561dec Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Sun, 13 Mar 2022 11:42:53 +0200 Subject: [PATCH] Move frustum culling from Scene to Camera Also refactor it so that the camera's object matrix is accounted for when computing the frustum planes. --- source/render/camera.cpp | 60 +++++++++++++++++++++++++++++++++ source/render/camera.h | 4 +++ source/render/occludedscene.cpp | 3 +- source/render/orderedscene.cpp | 5 +-- source/render/scene.cpp | 58 ------------------------------- source/render/scene.h | 6 ---- source/render/simplescene.cpp | 5 +-- source/render/zsortedscene.cpp | 3 +- 8 files changed, 72 insertions(+), 72 deletions(-) diff --git a/source/render/camera.cpp b/source/render/camera.cpp index 3ace92b9..7ffe0c15 100644 --- a/source/render/camera.cpp +++ b/source/render/camera.cpp @@ -125,6 +125,27 @@ Vector3 Camera::unproject(const Vector3 &p) const return unproject(Vector4(p.x, p.y, p.z, 1.0f)).slice<3>(0); } +bool Camera::is_in_frustum(const Renderable &renderable) const +{ + const Matrix *rmatrix = renderable.get_matrix(); + const Geometry::BoundingSphere *bsphere = renderable.get_bounding_sphere(); + if(!rmatrix || !bsphere) + return true; + + Vector4 center = *rmatrix*compose(bsphere->get_center(), 1.0f); + Vector3 x_axis = (rmatrix->column(0)*bsphere->get_radius()).slice<3>(0); + float radius_sq = inner_product(x_axis, x_axis); + + for(unsigned i=0; i<6; ++i) + { + float distance = inner_product(center, frustum_planes[i]); + if(distance<0 && distance*distance>radius_sq) + return false; + } + + return true; +} + void Camera::update_projection_matrix() { float frustum_h = (fov!=Geometry::Angle::zero() ? tan(fov/2.0f)*clip_near : height/2); @@ -141,6 +162,8 @@ void Camera::update_projection_matrix() shdata.uniform("clip_eye_matrix", proj_matrix); shdata.uniform("eye_clip_matrix", invert(proj_matrix)); + + update_frustum_planes(); } void Camera::update_object_matrix() @@ -156,6 +179,43 @@ void Camera::update_object_matrix() shdata.uniform("world_eye_matrix", matrix); shdata.uniform("eye_world_matrix", view_matrix); + + update_frustum_planes(); +} + +void Camera::update_frustum_planes() +{ + // TODO Handle off-center and rotated frustums + if(is_orthographic()) + { + frustum_planes[0] = Vector4(0, 1, 0, height); + frustum_planes[1] = Vector4(0, -1, 0, height); + + frustum_planes[2] = Vector4(1, 0, 0, height*aspect); + frustum_planes[3] = Vector4(-1, 0, 0, height*aspect); + } + else + { + float y = tan(fov/2.0f); + float s = sqrt(y*y+1); + frustum_planes[0] = Vector4(0, 1/s, -y/s, 0); + frustum_planes[1] = Vector4(0, -1/s, -y/s, 0); + + float x = y*aspect; + s = sqrt(x*x+1); + frustum_planes[2] = Vector4(1/s, 0, -x/s, 0); + frustum_planes[3] = Vector4(-1/s, 0, -x/s, 0); + } + + frustum_planes[4] = Vector4(0, 0, 1, clip_far); + frustum_planes[5] = Vector4(0, 0, -1, -clip_near); + + for(unsigned i=0; i<6; ++i) + { + Vector3 normal = frustum_planes[i].slice<3>(0); + normal = (matrix*compose(normal, 0.0f)).slice<3>(0); + frustum_planes[i] = compose(normal, frustum_planes[i].w-dot(normal, matrix.column(3).slice<3>(0))); + } } void Camera::set_debug_name(const string &name) diff --git a/source/render/camera.h b/source/render/camera.h index cb9b4ce1..59a0ac2e 100644 --- a/source/render/camera.h +++ b/source/render/camera.h @@ -57,6 +57,7 @@ private: Matrix view_matrix; Matrix proj_matrix; ProgramData shdata; + Vector4 frustum_planes[6]; public: Camera(); @@ -124,9 +125,12 @@ public: /** Returns a ProgramData object containing the camera matrices. */ const ProgramData &get_shader_data() const { return shdata; } + bool is_in_frustum(const Renderable &) const; + private: void update_projection_matrix(); void update_object_matrix(); + void update_frustum_planes(); public: void set_debug_name(const std::string &); diff --git a/source/render/occludedscene.cpp b/source/render/occludedscene.cpp index 644e380d..cb37a60e 100644 --- a/source/render/occludedscene.cpp +++ b/source/render/occludedscene.cpp @@ -95,10 +95,9 @@ void OccludedScene::render(Renderer &renderer, Tag tag) const float frustum_h = tan(camera->get_field_of_view()/2.0f)*2.0f; // Perform frustum culling and render any major occluders - bool use_frustum = setup_frustum(renderer); for(auto i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i) { - i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable)); + i->in_frustum = camera->is_in_frustum(*i->renderable); if(!i->in_frustum) continue; diff --git a/source/render/orderedscene.cpp b/source/render/orderedscene.cpp index 48ad7d64..a62087e3 100644 --- a/source/render/orderedscene.cpp +++ b/source/render/orderedscene.cpp @@ -1,4 +1,5 @@ #include +#include "camera.h" #include "orderedscene.h" #include "renderer.h" @@ -48,10 +49,10 @@ void OrderedScene::finish_frame() void OrderedScene::render(Renderer &renderer, Tag tag) const { - if(setup_frustum(renderer)) + if(const Camera *camera = renderer.get_camera()) { for(Renderable *r: content) - if(!frustum_cull(*r)) + if(camera->is_in_frustum(*r)) r->render(renderer, tag); } else diff --git a/source/render/scene.cpp b/source/render/scene.cpp index 0f773722..a9cf8f96 100644 --- a/source/render/scene.cpp +++ b/source/render/scene.cpp @@ -15,64 +15,6 @@ using namespace std; namespace Msp { namespace GL { -bool Scene::setup_frustum(const Renderer &renderer) const -{ - const Camera *camera = renderer.get_camera(); - if(!camera) - return false; - - culling_matrix = camera->get_view_matrix()*renderer.get_matrix(); - - if(camera->is_orthographic()) - { - float h = camera->get_orthographic_height(); - frustum_edges[0] = Vector4(0, 1, 0, -h); - frustum_edges[1] = Vector4(0, -1, 0, -h); - - float w = camera->get_orthographic_width(); - frustum_edges[2] = Vector4(1, 0, 0, -w); - frustum_edges[3] = Vector4(-1, 0, 0, -w); - } - else - { - float y = tan(camera->get_field_of_view()/2.0f); - float s = sqrt(y*y+1); - frustum_edges[0] = Vector4(0, 1/s, y/s, 0); - frustum_edges[1] = Vector4(0, -1/s, y/s, 0); - - float x = y*camera->get_aspect_ratio(); - s = sqrt(x*x+1); - frustum_edges[2] = Vector4(1/s, 0, x/s, 0); - frustum_edges[3] = Vector4(-1/s, 0, x/s, 0); - } - - frustum_edges[4] = Vector4(0, 0, -1, -camera->get_far_clip()); - frustum_edges[5] = Vector4(0, 0, 1, camera->get_near_clip()); - - return true; -} - -bool Scene::frustum_cull(const Renderable &renderable) const -{ - const Matrix *matrix = renderable.get_matrix(); - const Geometry::BoundingSphere *bsphere = renderable.get_bounding_sphere(); - if(!matrix || !bsphere) - return false; - - Vector4 center = culling_matrix*(*matrix*compose(bsphere->get_center(), 1.0f)); - Vector3 x_axis = (matrix->column(0)*bsphere->get_radius()).slice<3>(0); - float radius_sq = inner_product(x_axis, x_axis); - - for(unsigned i=0; i<6; ++i) - { - float distance = inner_product(center, frustum_edges[i]); - if(distance>0 && distance*distance>radius_sq) - return true; - } - - return false; -} - Scene::GenericLoader::TypeRegistry &Scene::get_scene_registry() { static Scene::GenericLoader::TypeRegistry registry; diff --git a/source/render/scene.h b/source/render/scene.h index d07a81da..6f4f48d6 100644 --- a/source/render/scene.h +++ b/source/render/scene.h @@ -60,7 +60,6 @@ public: protected: mutable Matrix culling_matrix; - mutable Vector4 frustum_edges[6]; Scene() = default; public: @@ -69,11 +68,6 @@ public: virtual void add(Renderable &) = 0; virtual void remove(Renderable &) = 0; -protected: - bool setup_frustum(const Renderer &) const; - bool frustum_cull(const Renderable &) const; - -public: template static void register_type(const std::string &); private: diff --git a/source/render/simplescene.cpp b/source/render/simplescene.cpp index 68db812b..e78b4709 100644 --- a/source/render/simplescene.cpp +++ b/source/render/simplescene.cpp @@ -1,4 +1,5 @@ #include +#include "camera.h" #include "renderer.h" #include "simplescene.h" @@ -33,10 +34,10 @@ void SimpleScene::finish_frame() void SimpleScene::render(Renderer &renderer, Tag tag) const { - if(setup_frustum(renderer)) + if(const Camera *camera = renderer.get_camera()) { for(Renderable *r: content) - if(!frustum_cull(*r)) + if(camera->is_in_frustum(*r)) r->render(renderer, tag); } else diff --git a/source/render/zsortedscene.cpp b/source/render/zsortedscene.cpp index 25255dc6..9c56f26e 100644 --- a/source/render/zsortedscene.cpp +++ b/source/render/zsortedscene.cpp @@ -81,10 +81,9 @@ void ZSortedScene::render(Renderer &renderer, Tag tag) const float radius_factor = reference-1.0f; float sign = 1.0f-order*2.0f; - bool use_frustum = setup_frustum(renderer); for(SortedRenderable &r: sorted_cache) { - r.in_frustum = (!use_frustum || !frustum_cull(*r.renderable)); + r.in_frustum = camera->is_in_frustum(*r.renderable); if(!r.in_frustum) continue; -- 2.43.0