From b160f64ab6da260bad06a4e5f5bfed243e2cdc24 Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 7 Dec 2015 09:04:10 +0200 Subject: [PATCH] Implement a scene class that performs occlusion culling on renderables --- extensions/arb_occlusion_query.glext | 1 + source/occludedscene.cpp | 186 +++++++++++++++++++++++++++ source/occludedscene.h | 54 ++++++++ 3 files changed, 241 insertions(+) create mode 100644 extensions/arb_occlusion_query.glext create mode 100644 source/occludedscene.cpp create mode 100644 source/occludedscene.h diff --git a/extensions/arb_occlusion_query.glext b/extensions/arb_occlusion_query.glext new file mode 100644 index 00000000..1f9d049a --- /dev/null +++ b/extensions/arb_occlusion_query.glext @@ -0,0 +1 @@ +extension ARB_occlusion_query diff --git a/source/occludedscene.cpp b/source/occludedscene.cpp new file mode 100644 index 00000000..8d05f7c1 --- /dev/null +++ b/source/occludedscene.cpp @@ -0,0 +1,186 @@ +#include +#include +#include "camera.h" +#include "occludedscene.h" +#include "renderer.h" +#include "sphere.h" + +using namespace std; + +namespace { + +const char vshader[] = + "void main()\n" + "{\n" + " gl_Position = gl_ProjectionMatrix*gl_ModelViewMatrix*gl_Vertex;\n" + "}"; + +const char fshader[] = + "void main()\n" + "{\n" + " gl_FragColor = vec4(1.0);\n" + "}"; + +} + +namespace Msp { +namespace GL { + +OccludedScene::OccludedScene(): + bounding_mesh((VERTEX3, NORMAL3)), + bounding_shader(vshader, fshader), + occluder_min_size(0.25f), + cache_dirty(false) +{ + static Require req(ARB_occlusion_query); + + /* Use a slightly larger radius to ensure that all parts of the renderable + fit inside the icosahedron */ + IcoSphereBuilder(1.26f, 1).build(bounding_mesh); + bounding_mesh.set_winding(&WindingTest::counterclockwise()); +} + +OccludedScene::~OccludedScene() +{ + vector queries; + queries.reserve(occluded_cache.size()); + for(OccludedArray::iterator i=occluded_cache.begin(); i!=occluded_cache.end(); ++i) + queries.push_back(i->query); + glDeleteQueries(queries.size(), &queries[0]); +} + +void OccludedScene::add(const Renderable &r) +{ + renderables.insert(&r); + cache_dirty = true; +} + +void OccludedScene::remove(const Renderable &r) +{ + renderables.erase(&r); + cache_dirty = true; +} + +void OccludedScene::render(Renderer &renderer, const Tag &tag) const +{ + if(renderables.empty()) + return; + + const Camera *camera = renderer.get_camera(); + if(!camera) + { + for(RenderableSet::const_iterator i=renderables.begin(); i!=renderables.end(); ++i) + renderer.render(**i, tag); + return; + } + + if(cache_dirty) + { + if(occluded_cache.size() new_queries(occluded_cache.size()-old_size); + glGenQueries(new_queries.size(), &new_queries[0]); + for(unsigned i=0; irenderable = *i; + for(; j!=occluded_cache.end(); ++j) + { + j->renderable = 0; + j->in_frustum = false; + } + + cache_dirty = false; + } + + const Vector3 &camera_pos = camera->get_position(); + const Vector3 &look_dir = camera->get_look_direction(); + float near_clip = camera->get_near_clip(); + 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(OccludedArray::iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->renderable); ++i) + { + i->in_frustum = (!use_frustum || !frustum_cull(*i->renderable)); + if(!i->in_frustum) + continue; + + const Matrix *matrix = i->renderable->get_matrix(); + i->bounding_sphere = i->renderable->get_bounding_sphere(); + if(matrix && i->bounding_sphere) + { + float depth = dot(*matrix*i->bounding_sphere->get_center()-camera_pos, look_dir); + float size = i->bounding_sphere->get_radius()*2/max(depth, near_clip); + i->occluder = (size>frustum_h*occluder_min_size); + } + else + // If the size can't be calculated, treat it as occluder + i->occluder = true; + + if(i->occluder) + renderer.render(*i->renderable, tag); + } + + // Move all objects within the frustum to the beginning of the array + for(OccludedArray::iterator i=occluded_cache.begin(), j=i+renderables.size()-1; i!=j; ) + { + if(i->in_frustum) + ++i; + else if(j->in_frustum) + swap(*i, *j); + else + --j; + } + + { + Renderer::Push push(renderer); + renderer.set_shader_program(&bounding_shader); + + glColorMask(false, false, false, false); + glDepthMask(false); + + // Fire off the occlusion queries + for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i) + if(!i->occluder) + { + glBeginQuery(GL_SAMPLES_PASSED, i->query); + Renderer::Push push2(renderer); + renderer.transform(Matrix(*i->renderable->get_matrix()) + .translate(i->bounding_sphere->get_center()) + .scale(i->bounding_sphere->get_radius())); + bounding_mesh.draw(renderer); + glEndQuery(GL_SAMPLES_PASSED); + } + + glColorMask(true, true, true, true); + glDepthMask(true); + } + + // Render anything that has a chance of being visible + for(OccludedArray::const_iterator i=occluded_cache.begin(); (i!=occluded_cache.end() && i->in_frustum); ++i) + if(!i->occluder) + { + int samples_passed; + glGetQueryObjectiv(i->query, GL_QUERY_RESULT, &samples_passed); + if(samples_passed>0) + renderer.render(*i->renderable, tag); + } +} + + +OccludedScene::OccludedRenderable::OccludedRenderable(): + renderable(0), + bounding_sphere(0), + in_frustum(false), + occluder(false), + query(0) +{ } + +} // namespace GL +} // namespace Msp diff --git a/source/occludedscene.h b/source/occludedscene.h new file mode 100644 index 00000000..3f8ca76c --- /dev/null +++ b/source/occludedscene.h @@ -0,0 +1,54 @@ +#ifndef MSP_GL_OCCLUDEDSCENE_H_ +#define MSP_GL_OCCLUDEDSCENE_H_ + +#include +#include +#include "mesh.h" +#include "program.h" +#include "scene.h" + +namespace Msp { +namespace GL { + +/** +A scene that performs occlusion queries on renderables to skip those that are +entirely occluded by others. +*/ +class OccludedScene: public Scene +{ +private: + struct OccludedRenderable + { + const Renderable *renderable; + const Geometry::BoundingSphere *bounding_sphere; + bool in_frustum; + bool occluder; + unsigned query; + + OccludedRenderable(); + }; + + typedef std::set RenderableSet; + typedef std::vector OccludedArray; + + Mesh bounding_mesh; + Program bounding_shader; + RenderableSet renderables; + float occluder_min_size; + mutable OccludedArray occluded_cache; + mutable bool cache_dirty; + +public: + OccludedScene(); + ~OccludedScene(); + + virtual void add(const Renderable &); + virtual void remove(const Renderable &); + + virtual void render(Renderer &, const Tag &) const; +}; + +} // namespace GL +} // namespace Msp + +#endif -- 2.43.0