From 98c810b6d2256aa65986bbde12c38917678121bb Mon Sep 17 00:00:00 2001 From: Mikko Rasa Date: Mon, 21 Sep 2009 13:50:58 +0000 Subject: [PATCH] Add get_width() / get_height() methods to Renderbuffer and Framebuffer Add some framebuffer-related global functions Set viewport size when binding a framebuffer and restore it when unbinding Allow detaching attachments from a Framebuffer Configurable texture unit number in ShadowMap --- source/framebuffer.cpp | 104 +++++++++++++++++++++++++++++++++++++++- source/framebuffer.h | 54 +++++++++++++++++++++ source/misc.cpp | 7 ++- source/misc.h | 3 ++ source/pipeline.cpp | 8 +--- source/renderbuffer.cpp | 4 +- source/renderbuffer.h | 4 ++ source/shadowmap.cpp | 15 ++++-- source/shadowmap.h | 18 ++++++- 9 files changed, 200 insertions(+), 17 deletions(-) diff --git a/source/framebuffer.cpp b/source/framebuffer.cpp index 7ae391a4..229c7f0a 100644 --- a/source/framebuffer.cpp +++ b/source/framebuffer.cpp @@ -8,13 +8,18 @@ Distributed under the LGPL #include "extension.h" #include "ext_framebuffer_object.h" #include "framebuffer.h" +#include "misc.h" #include "renderbuffer.h" #include "texture2d.h" +using namespace std; + namespace Msp { namespace GL { -Framebuffer::Framebuffer() +Framebuffer::Framebuffer(): + width(0), + height(0) { static RequireExtension _ext("GL_EXT_framebuffer_object"); @@ -29,20 +34,44 @@ Framebuffer::~Framebuffer() void Framebuffer::bind() const { + if(!cur_fbo) + get(GL_VIEWPORT, sys_viewport); glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, id); cur_fbo=this; + if(width && height) + viewport(0, 0, width, height); } void Framebuffer::attach(FramebufferAttachment attch, Renderbuffer &rbuf) { maybe_bind(); glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch, GL_RENDERBUFFER_EXT, rbuf.get_id()); + get_or_create_attachment(attch)=rbuf; + check_size(); } void Framebuffer::attach(FramebufferAttachment attch, Texture2D &tex, int level) { maybe_bind(); glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch, tex.get_target(), tex.get_id(), level); + get_or_create_attachment(attch)=tex; + check_size(); +} + +void Framebuffer::detach(FramebufferAttachment attch) +{ + maybe_bind(); + for(vector::iterator i=attachments.begin(); i!=attachments.end(); ++i) + if(i->attachment==attch) + { + if(i->type==GL_RENDERBUFFER_EXT) + glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, attch, GL_RENDERBUFFER_EXT, 0); + else if(i->type==GL_TEXTURE_2D) + glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, attch, GL_TEXTURE_2D, 0, 0); + attachments.erase(i); + check_size(); + return; + } } FramebufferStatus Framebuffer::check_status() const @@ -62,6 +91,7 @@ void Framebuffer::unbind() { glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0); cur_fbo=0; + viewport(sys_viewport[0], sys_viewport[1], sys_viewport[2], sys_viewport[3]); } } @@ -71,7 +101,79 @@ void Framebuffer::maybe_bind() const bind(); } +Framebuffer::Attachment &Framebuffer::get_or_create_attachment(FramebufferAttachment attch) +{ + for(vector::iterator i=attachments.begin(); i!=attachments.end(); ++i) + if(i->attachment==attch) + return *i; + attachments.push_back(Attachment(attch)); + return attachments.back(); +} + +void Framebuffer::check_size() +{ + if(!attachments.empty()) + { + const Attachment &attch=attachments.front(); + if(attch.type==GL_RENDERBUFFER_EXT) + { + width=attch.rbuf->get_width(); + height=attch.rbuf->get_height(); + } + else if(attch.type==GL_TEXTURE_2D) + { + Texture2D *tex=static_cast(attch.tex); + width=tex->get_width(); + height=tex->get_height(); + } + if(cur_fbo==this) + viewport(0, 0, width, height); + } +} + const Framebuffer *Framebuffer::cur_fbo=0; +int Framebuffer::sys_viewport[4]={ 0, 0, 1, 1 }; + + +Framebuffer::Attachment::Attachment(FramebufferAttachment a): + attachment(a), + type(0) +{ } + +Framebuffer::Attachment &Framebuffer::Attachment::operator=(Renderbuffer &r) +{ + type=GL_RENDERBUFFER_EXT; + rbuf=&r; + return *this; +} + +Framebuffer::Attachment &Framebuffer::Attachment::operator=(Texture &t) +{ + type=t.get_target(); + tex=&t; + return *this; +} + + +void viewport(int x, int y, unsigned w, unsigned h) +{ + glViewport(x, y, w, h); +} + +void clear(BufferBits bits) +{ + glClear(bits); +} + +void draw_buffer(RWBuffer buf) +{ + glDrawBuffer(buf); +} + +void read_buffer(RWBuffer buf) +{ + glReadBuffer(buf); +} } // namespace GL } // namespace Msp diff --git a/source/framebuffer.h b/source/framebuffer.h index ce362125..9fe4cd8b 100644 --- a/source/framebuffer.h +++ b/source/framebuffer.h @@ -8,6 +8,7 @@ Distributed under the LGPL #ifndef MSP_GL_FRAMEBUFFER_H_ #define MSP_GL_FRAMEBUFFER_H_ +#include #include "gl.h" #include "types.h" @@ -15,6 +16,7 @@ namespace Msp { namespace GL { class Renderbuffer; +class Texture; class Texture2D; enum FramebufferAttachment @@ -39,6 +41,28 @@ enum FramebufferStatus FRAMEBUFFER_COMPLETE = GL_FRAMEBUFFER_COMPLETE_EXT }; +enum BufferBits +{ + COLOR_BUFFER_BIT = GL_COLOR_BUFFER_BIT, + DEPTH_BUFFER_BIT = GL_DEPTH_BUFFER_BIT, + STENCIL_BUFFER_BIT = GL_STENCIL_BUFFER_BIT, + ACCUM_BUFFER_BIT = GL_ACCUM_BUFFER_BIT +}; + +enum RWBuffer +{ + NO_BUFFER = GL_NONE, + FRONT_LEFT = GL_FRONT_LEFT, + FRONT_RIGHT = GL_FRONT_RIGHT, + BACK_LEFT = GL_BACK_LEFT, + BACK_RIGHT = GL_BACK_RIGHT, + FRONT = GL_FRONT, + BACK = GL_BACK, + LEFT = GL_LEFT, + RIGHT = GL_RIGHT, + FRONT_AND_BACK = GL_FRONT_AND_BACK +}; + /** Framebuffer objects can be used to perform offscreen rendering. The most common application is rendering to a texture, which can then be used for @@ -53,9 +77,28 @@ Requires the GL_EXT_framebuffer_object extension. class Framebuffer { private: + struct Attachment + { + FramebufferAttachment attachment; + GLenum type; + union + { + Renderbuffer *rbuf; + Texture *tex; + }; + + Attachment(FramebufferAttachment); + Attachment &operator=(Renderbuffer &); + Attachment &operator=(Texture &); + }; + uint id; + std::vector attachments; + unsigned width; + unsigned height; static const Framebuffer *cur_fbo; + static int sys_viewport[4]; public: Framebuffer(); @@ -65,6 +108,7 @@ public: void attach(FramebufferAttachment attch, Renderbuffer &rbuf); void attach(FramebufferAttachment attch, Texture2D &tex, int level); + void detach(FramebufferAttachment attch); /** Checks the completeness status of the framebuffer. Returns @@ -77,8 +121,18 @@ public: static void unbind(); private: void maybe_bind() const; + Attachment &get_or_create_attachment(FramebufferAttachment); + void check_size(); }; +inline BufferBits operator|(BufferBits a, BufferBits b) +{ return static_cast(static_cast(a)|static_cast(b)); } + +void viewport(int, int, unsigned, unsigned); +void clear(BufferBits); +void draw_buffer(RWBuffer); +void read_buffer(RWBuffer); + } // namespace GL } // namespace Msp diff --git a/source/misc.cpp b/source/misc.cpp index 70b300a6..c09d17f5 100644 --- a/source/misc.cpp +++ b/source/misc.cpp @@ -33,10 +33,15 @@ void get(GLenum state, int &data) glGetIntegerv(state, &data); } +void get(GLenum state, int *data) +{ + glGetIntegerv(state, data); +} + int get_i(GLenum state) { int data; - get(state, data); + get(state, &data); return data; } diff --git a/source/misc.h b/source/misc.h index 7aa8aaf9..11cb3a60 100644 --- a/source/misc.h +++ b/source/misc.h @@ -17,7 +17,10 @@ void enable(GLenum); void disable(GLenum); void set(GLenum, bool); +///Deprecated (can't properly pass an array through a reference) void get(GLenum, int &); + +void get(GLenum, int *); int get_i(GLenum); class Bind diff --git a/source/pipeline.cpp b/source/pipeline.cpp index b0a38a2d..ea8b56a3 100644 --- a/source/pipeline.cpp +++ b/source/pipeline.cpp @@ -101,10 +101,7 @@ void Pipeline::render(const Tag &tag) const if(pass.lighting) pass.lighting->bind(); for(vector::const_iterator i=pass.effects.begin(); i!=pass.effects.end(); ++i) - { (*i)->prepare(); - glViewport(0, 0, width, height); - } for(vector::const_iterator i=renderables.begin(); i!=renderables.end(); ++i) (*i)->render(tag); for(vector::const_iterator i=pass.effects.end(); i--!=pass.effects.begin();) @@ -118,13 +115,10 @@ void Pipeline::render_all() const if(fbo) { fbo->bind(); - glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); + clear(COLOR_BUFFER_BIT|DEPTH_BUFFER_BIT); } for(vector::const_iterator i=effects.begin(); i!=effects.end(); ++i) - { (*i)->prepare(); - glViewport(0, 0, width, height); - } for(vector::const_iterator i=pass_order.begin(); i!=pass_order.end(); ++i) render(*i); for(vector::const_iterator i=effects.end(); i--!=effects.begin();) diff --git a/source/renderbuffer.cpp b/source/renderbuffer.cpp index 0f52ff59..ac0a34cd 100644 --- a/source/renderbuffer.cpp +++ b/source/renderbuffer.cpp @@ -30,9 +30,11 @@ void Renderbuffer::bind() const glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, id); } -void Renderbuffer::storage(PixelFormat fmt, sizei width, sizei height) +void Renderbuffer::storage(PixelFormat fmt, sizei w, sizei h) { bind(); + width=w; + height=h; glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, fmt, width, height); } diff --git a/source/renderbuffer.h b/source/renderbuffer.h index a9c25ad7..040dad44 100644 --- a/source/renderbuffer.h +++ b/source/renderbuffer.h @@ -25,12 +25,16 @@ class Renderbuffer { private: uint id; + sizei width; + sizei height; public: Renderbuffer(); ~Renderbuffer(); uint get_id() const { return id; } + sizei get_width() const { return width; } + sizei get_height() const { return height; } void bind() const; diff --git a/source/shadowmap.cpp b/source/shadowmap.cpp index 3691773a..5f66d898 100644 --- a/source/shadowmap.cpp +++ b/source/shadowmap.cpp @@ -24,6 +24,7 @@ ShadowMap::ShadowMap(unsigned s, const Scene &c, const Light &l): size(s), scene(c), light(l), + unit(3), radius(1) { depth_buf.set_min_filter(LINEAR); @@ -35,7 +36,7 @@ ShadowMap::ShadowMap(unsigned s, const Scene &c, const Light &l): depth_buf.parameter(GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); depth_buf.parameter(GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); fbo.attach(DEPTH_ATTACHMENT, depth_buf, 0); - glDrawBuffer(GL_NONE); + draw_buffer(NO_BUFFER); Framebuffer::unbind(); Texture::unbind(); } @@ -46,6 +47,11 @@ void ShadowMap::set_target(const Vector3 &t, float r) radius=r; } +void ShadowMap::set_texture_unit(unsigned u) +{ + unit=u; +} + void ShadowMap::prepare() { const Vector4 &lpos=light.get_position(); @@ -97,8 +103,7 @@ void ShadowMap::prepare() const Framebuffer *old_fbo=Framebuffer::current(); fbo.bind(); - glViewport(0, 0, size, size); - glClear(GL_DEPTH_BUFFER_BIT); + clear(DEPTH_BUFFER_BIT); scene.render("shadow"); matrix_mode(PROJECTION); @@ -111,7 +116,7 @@ void ShadowMap::prepare() Framebuffer::unbind(); } - depth_buf.bind_to(3); + depth_buf.bind_to(unit); float diam=radius*2; float s_eq[4]={ matrix[0]/diam, matrix[4]/diam, matrix[8]/diam, matrix[12]/diam+0.5 }; float t_eq[4]={ matrix[1]/diam, matrix[5]/diam, matrix[9]/diam, matrix[13]/diam+0.5 }; @@ -131,7 +136,7 @@ void ShadowMap::prepare() void ShadowMap::cleanup() { - TexUnit::activate(3); + TexUnit::activate(unit); Texture::unbind(); disable(GL_TEXTURE_GEN_S); disable(GL_TEXTURE_GEN_T); diff --git a/source/shadowmap.h b/source/shadowmap.h index 17e17ca9..29bede31 100644 --- a/source/shadowmap.h +++ b/source/shadowmap.h @@ -19,6 +19,12 @@ namespace GL { class Light; class Scene; +/** +Creates shadows on a Scene through a shadow map texture. In the preparation +phase, the scene is rendered to a depth texture from the point of view of the +lightsource. This texture is then used in the rendering phase together with +texture coordinate generation to determine whether each fragment is lit. +*/ class ShadowMap: public Effect { private: @@ -26,15 +32,23 @@ private: const Scene &scene; const Light &light; Framebuffer fbo; -public: + unsigned unit; Texture2D depth_buf; -private: Vector3 target; float radius; public: ShadowMap(unsigned, const Scene &, const Light &); + + /** Sets the ShadowMap target point and radius. The transformation matrix is + computed so that a sphere with the specified parameters will be completely + covered by the ShadowMap. */ void set_target(const Vector3 &, float); + + /** Sets the texture unit to bind the shadow map to during the rendering + phase. The default is texture unit 3. */ + void set_texture_unit(unsigned); + virtual void prepare(); virtual void cleanup(); }; -- 2.43.0