From dbb4cb39a13e77f328ad4c33f0ae15b4aed255ba Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Mon, 7 Oct 2019 14:30:14 +0200 Subject: [PATCH] GL: improve Framebuffer usage docs. --- doc/snippets/MagnumGL.cpp | 80 +++++++++++++++++++++++++++++++----- src/Magnum/GL/Framebuffer.h | 55 +++++++++++++++++-------- src/Magnum/GL/Renderbuffer.h | 4 +- 3 files changed, 110 insertions(+), 29 deletions(-) diff --git a/doc/snippets/MagnumGL.cpp b/doc/snippets/MagnumGL.cpp index 2e2f79b67..7137b9217 100644 --- a/doc/snippets/MagnumGL.cpp +++ b/doc/snippets/MagnumGL.cpp @@ -41,6 +41,7 @@ #include "Magnum/GL/PixelFormat.h" #include "Magnum/GL/Renderer.h" #include "Magnum/GL/Renderbuffer.h" +#include "Magnum/GL/RenderbufferFormat.h" #include "Magnum/GL/Shader.h" #include "Magnum/GL/Texture.h" #include "Magnum/GL/TextureFormat.h" @@ -893,21 +894,71 @@ GL::defaultFramebuffer.mapForDraw({ #ifndef MAGNUM_TARGET_GLES2 { -/* [Framebuffer-usage-attach] */ -GL::Framebuffer framebuffer{GL::defaultFramebuffer.viewport()}; -GL::Texture2D color, normal; +struct MyShader { + void bindTexture(GL::Texture2D&) {} +} myShader; +Vector2i size; +/* [Framebuffer-usage] */ +GL::Texture2D color; GL::Renderbuffer depthStencil; +color.setStorage(1, GL::TextureFormat::RGBA8, size); +depthStencil.setStorage(GL::RenderbufferFormat::Depth24Stencil8, size); -// configure the textures and allocate texture memory... - +GL::Framebuffer framebuffer{{{}, size}}; framebuffer.attachTexture(GL::Framebuffer::ColorAttachment{0}, color, 0); -framebuffer.attachTexture(GL::Framebuffer::ColorAttachment{1}, normal, 0); framebuffer.attachRenderbuffer( GL::Framebuffer::BufferAttachment::DepthStencil, depthStencil); -/* [Framebuffer-usage-attach] */ +/* [Framebuffer-usage] */ + +/* [Framebuffer-usage-rendering] */ +framebuffer + .clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth) + .bind(); + +// draw to this framebuffer ... + +/* Switch back to the default framebuffer */ +GL::defaultFramebuffer + .clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth) + .bind(); + +// use the rendered texture in a shader ... +myShader.bindTexture(color); +/* [Framebuffer-usage-rendering] */ +} +#endif + +#ifndef MAGNUM_TARGET_GLES2 +{ +/* [Framebuffer-usage-multisample] */ +Vector2i size = GL::defaultFramebuffer.viewport().size(); + +/* 8x MSAA */ +GL::Renderbuffer color, depthStencil; +color.setStorageMultisample(8, GL::RenderbufferFormat::RGBA8, size); +depthStencil.setStorageMultisample(8, + GL::RenderbufferFormat::Depth24Stencil8, size); + +GL::Framebuffer framebuffer{{{}, size}}; +framebuffer.attachRenderbuffer(GL::Framebuffer::ColorAttachment{0}, color); +framebuffer.attachRenderbuffer( + GL::Framebuffer::BufferAttachment::DepthStencil, depthStencil); + +framebuffer.clear(GL::FramebufferClear::Color|GL::FramebufferClear::Depth) + .bind(); + +// draw to the multisampled framebuffer ... + +/* Resolve the color output to a single-sampled default framebuffer */ +GL::defaultFramebuffer.clear(GL::FramebufferClear::Color) + .bind(); +GL::Framebuffer::blit(framebuffer, GL::defaultFramebuffer, + {{}, size}, GL::FramebufferBlit::Color); +/* [Framebuffer-usage-multisample] */ } #endif +#ifndef MAGNUM_TARGET_GLES2 { struct MyShader { enum: UnsignedInt { @@ -915,12 +966,20 @@ struct MyShader { NormalOutput = 1 }; }; -GL::Framebuffer framebuffer{{}}; -/* [Framebuffer-usage-map] */ +/* [Framebuffer-usage-deferred] */ +GL::Framebuffer framebuffer{GL::defaultFramebuffer.viewport()}; +GL::Texture2D color, normal; +GL::Renderbuffer depthStencil; +// setStorage() ... + +framebuffer.attachTexture(GL::Framebuffer::ColorAttachment{0}, color, 0); +framebuffer.attachTexture(GL::Framebuffer::ColorAttachment{1}, normal, 0); +framebuffer.attachRenderbuffer( + GL::Framebuffer::BufferAttachment::DepthStencil, depthStencil); framebuffer.mapForDraw({ {MyShader::ColorOutput, GL::Framebuffer::ColorAttachment(0)}, {MyShader::NormalOutput, GL::Framebuffer::ColorAttachment(1)}}); -/* [Framebuffer-usage-map] */ +/* [Framebuffer-usage-deferred] */ /* [Framebuffer-mapForDraw] */ framebuffer.mapForDraw({ @@ -928,6 +987,7 @@ framebuffer.mapForDraw({ {MyShader::NormalOutput, GL::Framebuffer::DrawAttachment::None}}); /* [Framebuffer-mapForDraw] */ } +#endif { /* [Mesh-nonindexed] */ diff --git a/src/Magnum/GL/Framebuffer.h b/src/Magnum/GL/Framebuffer.h index 1b6222416..526e12cb2 100644 --- a/src/Magnum/GL/Framebuffer.h +++ b/src/Magnum/GL/Framebuffer.h @@ -44,30 +44,51 @@ namespace Magnum { namespace GL { @brief Framebuffer Unlike @ref 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. +class is used for off-screen rendering, usable in windowless applications, for +texture generation or for various post-processing effects. -@section GL-Framebuffer-usage Example usage +@section GL-Framebuffer-usage Basic usage -See @ref GL-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: +A basic setup for rendering to a texture of a desired `size` might look like +below. Apart from a color attachment, there's also a depth/stencil attachment +in order to make depth test work properly. If you render 2D or don't need to +use a depth/stencil test, you can have just a color attachment. It's possible +to attach either a @ref Texture or a @ref Renderbuffer --- a texture allows +you to read the rendered output later from a shader, while a renderbuffer +allows you to only @ref read() or @ref blit() from it. In modern desktop OpenGL +there's not much reason to use @ref Renderbuffer anymore, however in OpenGL ES +and WebGL due to various texture format restrictions, renderbuffers are still +the more flexible option if you don't need to use the result in a shader. -@snippet MagnumGL.cpp Framebuffer-usage-attach +@snippet MagnumGL.cpp Framebuffer-usage -Then you need to map outputs of your shader to color attachments in the -framebuffer: +Rendering then usually consists of switching between different framebuffers +using @ref bind() and reusing the rendered texture in subsequent draws: -@snippet MagnumGL.cpp Framebuffer-usage-map +@snippet MagnumGL.cpp Framebuffer-usage-rendering -The actual @ref Platform::Sdl2Application::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: +@section GL-Framebuffer-usage-multisample Multisampled rendering -@snippet MagnumGL-framebuffer.cpp Framebuffer-usage-draw +Another use case for custom framebuffers is multisampled rendering --- as +you're not always allowed to control the MSAA setting on a default framebuffer, +or you might want your rendered texture to be multisampled as well: + +@snippet MagnumGL.cpp Framebuffer-usage-multisample + +Here @ref Renderbuffer gets used for the color attachment as well. While it's +possible to achieve the same with a @ref MultisampleTexture2D, support for it +is rather sparse on OpenGL ES and completely nonexistent on WebGL or +macOS / iOS. + +@section GL-Framebuffer-usage-multiple-output Multiple fragment shader outputs + +In a deferred rendering setup for example, a shader usually has more than one +output. That's finally where non-zero @ref ColorAttachment and @ref mapForDraw() +gets used. In builtin shaders this is also how the +@ref Shaders::Flat::ColorOutput / @ref Shaders::Flat::ObjectIdOutput etc. get +used: + +@snippet MagnumGL.cpp Framebuffer-usage-deferred @section GL-Framebuffer-performance-optimizations Performance optimizations diff --git a/src/Magnum/GL/Renderbuffer.h b/src/Magnum/GL/Renderbuffer.h index 28e95f6b2..600478956 100644 --- a/src/Magnum/GL/Renderbuffer.h +++ b/src/Magnum/GL/Renderbuffer.h @@ -42,8 +42,8 @@ namespace Implementation { struct FramebufferState; } /** @brief Renderbuffer -Attachable to framebuffer as render target, see @ref Framebuffer documentation -for more information. +Attachable to a framebuffer as render target, see the @ref Framebuffer class +for a detailed usage example. @section GL-Renderbuffer-performance-optimizations Performance optimizations