#ifndef Magnum_Framebuffer_h #define Magnum_Framebuffer_h /* Copyright © 2010, 2011, 2012 Vladimír Vondruš This file is part of Magnum. Magnum is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License version 3 only, as published by the Free Software Foundation. Magnum is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License version 3 for more details. */ /** @file * @brief Class Magnum::Framebuffer */ #include "AbstractFramebuffer.h" #include "CubeMapTexture.h" namespace Magnum { /** @brief %Framebuffer Unlike DefaultFramebuffer, which is used for on-screen rendering, this class is used for off-screen rendering, usable either in windowless applications, texture generation or for various post-processing effects. @section Framebuffer-usage Example usage See @ref DefaultFramebuffer-usage "DefaultFramebuffer documentation" for introduction. Imagine you have shader with multiple outputs (e.g. for deferred rendering). You want to render them off-screen to textures and then use the textures for actual on-screen rendering. First you need to create the framebuffer with the same viewport as default framebuffer and attach textures and renderbuffers to desired outputs: @code Framebuffer framebuffer(defaultFramebuffer.viewportPosition(), defaultFramebuffer.viewportSize()); Texture2D color, normal; Renderbuffer depthStencil; // configure the textures and allocate texture memory... framebuffer.attachTexture2D(0, &color); framebuffer.attachTexture2D(1, &normal); framebuffer.attachRenderbuffer(Framebuffer::DepthStencilAttachment::DepthStencil, &depthStencil); @endcode Then you need to map outputs of your shader to color attachments in the framebuffer: @code framebuffer.mapForDraw({{MyShader::ColorOutput, 0}, {MyShader::NormalOutput, 1}}); @endcode The actual @ref Platform::GlutApplication::drawEvent() "drawEvent()" might look like this. First you clear all buffers you need, perform drawing to off-screen framebuffer, then bind the default and render the textures on screen: @code void drawEvent() { defaultFramebuffer.clear(AbstractFramebuffer::Clear::Color) framebuffer.clear(AbstractFramebuffer::Clear::Color|AbstractFramebuffer::Clear::Depth|AbstractFramebuffer::Clear::Stencil); framebuffer.bind(AbstractFramebuffer::Target::Draw); // ... defaultFramebuffer.bind(AbstractFramebuffer::Target::Draw); // ... } @endcode @section Framebuffer-performance-optimization Performance optimizations See also @ref AbstractFramebuffer-performance-optimization "relevant section in AbstractFramebuffer". If extension @extension{EXT,direct_state_access} is available, functions mapForDraw(), mapForRead(), attachRenderbuffer(), attachTexture1D(), attachTexture2D(), attachCubeMapTexture() and attachTexture3D() use DSA to avoid unnecessary calls to @fn_gl{BindFramebuffer}. See their respective documentation for more information. @requires_gl30 %Extension @extension{EXT,framebuffer_object} */ class MAGNUM_EXPORT Framebuffer: public AbstractFramebuffer { friend class Context; public: /** * @brief Constructor * * Generates new OpenGL framebuffer. * @see @fn_gl{GenFramebuffers} */ explicit Framebuffer(const Vector2i& viewportPosition, const Vector2i& viewportSize); /** * @brief Destructor * * Deletes associated OpenGL framebuffer. * @see @fn_gl{DeleteFramebuffers} */ ~Framebuffer(); /** * @brief Map shader output to color attachment * * @p attachments is list of shader outputs mapped to framebuffer * color attachment IDs. Shader outputs which are not listed are not * used, you can achieve the same by passing `-1` as color attachment * ID. Example usage: * @code * framebuffer.mapForDraw({{MyShader::ColorOutput, 0}, * {MyShader::NormalOutput, 1}}); * @endcode * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see mapForRead(), @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffers} or * @fn_gl_extension{FramebufferDrawBuffers,EXT,direct_state_access} * @requires_gles30 %Extension @es_extension2{NV,draw_buffers,GL_NV_draw_buffers} */ void mapForDraw(std::initializer_list> attachments); /** * @brief Map shader output to color attachment * @param attachment Color attachment ID * * Similar to above function, can be used in cases when shader has * only one (unnamed) output. * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see mapForRead(), @fn_gl{BindFramebuffer}, @fn_gl{DrawBuffer} or * @fn_gl_extension{FramebufferDrawBuffer,EXT,direct_state_access} * @requires_gles30 %Extension @es_extension2{NV,draw_buffers,GL_NV_draw_buffers} */ inline void mapForDraw(std::int8_t attachment) { (this->*drawBufferImplementation)(static_cast(GL_COLOR_ATTACHMENT0 + attachment)); } /** * @brief Map given color attachment for reading * @param attachment Color attachment ID * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see mapForDraw(), @fn_gl{BindFramebuffer}, @fn_gl{ReadBuffer} or * @fn_gl_extension{FramebufferReadBuffer,EXT,direct_state_access} * @requires_gles30 %Extension @es_extension2{NV,read_buffer,GL_NV_read_buffer} */ inline void mapForRead(std::uint8_t attachment) { (this->*readBufferImplementation)(GL_COLOR_ATTACHMENT0 + attachment); } /** * @brief Attachment for depth/stencil part of fragment shader output * * @see attachRenderbuffer(), attachTexture1D(), attachTexture2D(), * attachCubeMapTexture(), attachTexture3D() */ enum class DepthStencilAttachment: GLenum { Depth = GL_DEPTH_ATTACHMENT, /**< Depth output only. */ Stencil = GL_STENCIL_ATTACHMENT /**< Stencil output only. */ #ifndef MAGNUM_TARGET_GLES2 , /** * Both depth and stencil output. * @requires_gles30 Combined depth and stencil attachment is not * available in OpenGL ES 2.0. */ DepthStencil = GL_DEPTH_STENCIL_ATTACHMENT #endif }; /** * @brief Attach renderbuffer to given framebuffer depth/stencil attachment * @param depthStencilAttachment Depth/stencil attachment * @param renderbuffer %Renderbuffer * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see @fn_gl{BindFramebuffer}, @fn_gl{FramebufferRenderbuffer} or * @fn_gl_extension{NamedFramebufferRenderbuffer,EXT,direct_state_access} */ inline void attachRenderbuffer(DepthStencilAttachment depthStencilAttachment, Renderbuffer* renderbuffer) { (this->*renderbufferImplementation)(GLenum(depthStencilAttachment), renderbuffer); } /** * @brief Attach renderbuffer to given framebuffer color attachment * @param colorAttachment Color attachment ID (number between 0 and 15) * @param renderbuffer %Renderbuffer * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see @fn_gl{BindFramebuffer}, @fn_gl{FramebufferRenderbuffer} or * @fn_gl_extension{NamedFramebufferRenderbuffer,EXT,direct_state_access} */ inline void attachRenderbuffer(std::uint8_t colorAttachment, Renderbuffer* renderbuffer) { (this->*renderbufferImplementation)(GL_COLOR_ATTACHMENT0 + colorAttachment, renderbuffer); } #ifndef MAGNUM_TARGET_GLES /** * @brief Attach 1D texture to given framebuffer depth/stencil attachment * @param depthStencilAttachment Depth/stencil attachment * @param texture 1D texture * @param mipLevel Mip level * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} or * @fn_gl_extension{NamedFramebufferTexture1D,EXT,direct_state_access} * @requires_gl Only 2D and 3D textures are available in OpenGL ES. */ inline void attachTexture1D(DepthStencilAttachment depthStencilAttachment, Texture1D* texture, GLint mipLevel) { (this->*texture1DImplementation)(GLenum(depthStencilAttachment), texture, mipLevel); } /** * @brief Attach 1D texture to given framebuffer color attachment * @param colorAttachment Color attachment ID (number between 0 and 15) * @param texture 1D texture * @param mipLevel Mip level * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} or * @fn_gl_extension{NamedFramebufferTexture1D,EXT,direct_state_access} * @requires_gl Only 2D and 3D textures are available in OpenGL ES. */ inline void attachTexture1D(std::uint8_t colorAttachment, Texture1D* texture, GLint mipLevel) { (this->*texture1DImplementation)(GL_COLOR_ATTACHMENT0 + colorAttachment, texture, mipLevel); } #endif /** * @brief Attach 2D texture to given framebuffer depth/stencil attachment * @param depthStencilAttachment Depth/stencil attachment * @param texture 2D texture * @param mipLevel Mip level. For rectangle textures it * should be always 0. * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see attachCubeMapTexture(), @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} * or @fn_gl_extension{NamedFramebufferTexture2D,EXT,direct_state_access} */ void attachTexture2D(DepthStencilAttachment depthStencilAttachment, Texture2D* texture, GLint mipLevel); /** * @brief Attach 2D texture to given framebuffer color attachment * @param colorAttachment Color attachment ID (number between 0 and 15) * @param texture 2D texture * @param mipLevel Mip level. For rectangle textures it * should be always 0. * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see attachCubeMapTexture(), @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} * or @fn_gl_extension{NamedFramebufferTexture2D,EXT,direct_state_access} */ void attachTexture2D(std::uint8_t colorAttachment, Texture2D* texture, GLint mipLevel); /** * @brief Attach cube map texture to given framebuffer depth/stencil attachment * @param depthStencilAttachment Depth/stencil attachment * @param texture Cube map texture * @param coordinate Cube map coordinate * @param mipLevel Mip level * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see attachTexture2D(), @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} * or @fn_gl_extension{NamedFramebufferTexture2D,EXT,direct_state_access} */ inline void attachCubeMapTexture(DepthStencilAttachment depthStencilAttachment, CubeMapTexture* texture, CubeMapTexture::Coordinate coordinate, GLint mipLevel) { (this->*texture2DImplementation)(GLenum(depthStencilAttachment), GLenum(coordinate), texture->id(), mipLevel); } /** * @brief Attach cube map texture to given framebuffer color attachment * @param colorAttachment Color attachment ID (number between 0 and 15) * @param texture Cube map texture * @param coordinate Cube map coordinate * @param mipLevel Mip level * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see attachTexture2D(), @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} * or @fn_gl_extension{NamedFramebufferTexture2D,EXT,direct_state_access} */ inline void attachCubeMapTexture(std::uint8_t colorAttachment, CubeMapTexture* texture, CubeMapTexture::Coordinate coordinate, GLint mipLevel) { (this->*texture2DImplementation)(GL_COLOR_ATTACHMENT0 + colorAttachment, GLenum(coordinate), texture->id(), mipLevel); } /** * @brief Attach 3D texture to given framebuffer depth/stencil attachment * @param depthStencilAttachment Depth/stencil attachment * @param texture 3D texture * @param mipLevel Mip level * @param layer Layer of 2D image within a 3D texture * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} or * @fn_gl_extension{NamedFramebufferTexture3D,EXT,direct_state_access} * @requires_es_extension %Extension @es_extension{OES,texture_3D} */ inline void attachTexture3D(DepthStencilAttachment depthStencilAttachment, Texture3D* texture, GLint mipLevel, GLint layer) { /** @todo Check for texture target compatibility */ (this->*texture3DImplementation)(GLenum(depthStencilAttachment), texture, mipLevel, layer); } /** * @brief Attach 3D texture to given framebuffer color attachment * @param colorAttachment Color attachment ID (number between 0 and 15) * @param texture 3D texture * @param mipLevel Mip level * @param layer Layer of 2D image within a 3D texture. * * If @extension{EXT,direct_state_access} is not available and the * framebufferbuffer is not currently bound, it is bound before the * operation. * @see @fn_gl{BindFramebuffer}, @fn_gl{FramebufferTexture} or * @fn_gl_extension{NamedFramebufferTexture3D,EXT,direct_state_access} * @requires_es_extension %Extension @es_extension{OES,texture_3D} */ inline void attachTexture3D(std::uint8_t colorAttachment, Texture3D* texture, GLint mipLevel, GLint layer) { /** @todo Check for texture target compatibility */ (this->*texture3DImplementation)(GL_COLOR_ATTACHMENT0 + colorAttachment, texture, mipLevel, layer); } private: static void MAGNUM_LOCAL initializeContextBasedFunctionality(Context* context); typedef void(Framebuffer::*RenderbufferImplementation)(GLenum, Renderbuffer*); void MAGNUM_LOCAL renderbufferImplementationDefault(GLenum attachment, Renderbuffer* renderbuffer); #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL renderbufferImplementationDSA(GLenum attachment, Renderbuffer* renderbuffer); #endif static RenderbufferImplementation renderbufferImplementation; #ifndef MAGNUM_TARGET_GLES typedef void(Framebuffer::*Texture1DImplementation)(GLenum, Texture1D*, GLint); void MAGNUM_LOCAL texture1DImplementationDefault(GLenum attachment, Texture1D* texture, GLint mipLevel); void MAGNUM_LOCAL texture1DImplementationDSA(GLenum attachment, Texture1D* texture, GLint mipLevel); static Texture1DImplementation texture1DImplementation; #endif typedef void(Framebuffer::*Texture2DImplementation)(GLenum, GLenum, GLuint, GLint); void MAGNUM_LOCAL texture2DImplementationDefault(GLenum attachment, GLenum textureTarget, GLuint textureId, GLint mipLevel); #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL texture2DImplementationDSA(GLenum attachment, GLenum textureTarget, GLuint textureId, GLint mipLevel); #endif static MAGNUM_LOCAL Texture2DImplementation texture2DImplementation; typedef void(Framebuffer::*Texture3DImplementation)(GLenum, Texture3D*, GLint, GLint); void MAGNUM_LOCAL texture3DImplementationDefault(GLenum attachment, Texture3D* texture, GLint mipLevel, GLint layer); #ifndef MAGNUM_TARGET_GLES void MAGNUM_LOCAL texture3DImplementationDSA(GLenum attachment, Texture3D* texture, GLint mipLevel, GLint layer); #endif static Texture3DImplementation texture3DImplementation; }; } #endif