]> git.tdb.fi Git - libs/gl.git/commitdiff
Move frustum culling from Scene to Camera
authorMikko Rasa <tdb@tdb.fi>
Sun, 13 Mar 2022 09:42:53 +0000 (11:42 +0200)
committerMikko Rasa <tdb@tdb.fi>
Sun, 13 Mar 2022 09:45:07 +0000 (11:45 +0200)
Also refactor it so that the camera's object matrix is accounted for
when computing the frustum planes.

source/render/camera.cpp
source/render/camera.h
source/render/occludedscene.cpp
source/render/orderedscene.cpp
source/render/scene.cpp
source/render/scene.h
source/render/simplescene.cpp
source/render/zsortedscene.cpp

index 3ace92b9ecb7166de54fa20b33f07c943997dae1..7ffe0c155e60f6a6b968869901485a2c399fd7e6 100644 (file)
@@ -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<float, 3> *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<float>::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)
index cb9b4ce19423e36cdf126303a6e0b197eeb2ff69..59a0ac2e62f677c4c356339a97f5c794515d7562 100644 (file)
@@ -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 &);
index 644e380d15ab10a25917571fd29d61522040a99f..cb37a60e924eb266624f5762e0a990c0e23c5a2d 100644 (file)
@@ -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;
 
index 48ad7d644c43c4b39047528cb5d772584c667e10..a62087e34dac1bd54a94e7d65e690277bbb81555 100644 (file)
@@ -1,4 +1,5 @@
 #include <msp/core/algorithm.h>
+#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
index 0f7737227e8da37c4ba69e0260d99f4e893b87c0..a9cf8f96b326f76d388fed95187fbacd4dcdfa25 100644 (file)
@@ -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<float, 3> *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;
index d07a81da80b82fef2998eed732c4d3aca8bef8b3..6f4f48d675e6bce3db2850ca24eb78466157769e 100644 (file)
@@ -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<typename T>
        static void register_type(const std::string &);
 private:
index 68db812b2db18c34f5338b0844932d5146dc2a74..e78b4709b49be463b6c8eed8b8d505e3b73d1323 100644 (file)
@@ -1,4 +1,5 @@
 #include <msp/core/algorithm.h>
+#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
index 25255dc693569a9d34b2adfc7b64f709089af63e..9c56f26e8ca39a3f583f836fd0ede67fb5922b33 100644 (file)
@@ -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;