BARE2D
FBORenderer.cpp
Go to the documentation of this file.
1 #include "FBORenderer.hpp"
2 
3 #include <GL/glew.h>
4 #include <glm/gtc/type_ptr.hpp>
5 
6 #include "GLContextManager.hpp"
7 
8 #include "BAREErrors.hpp"
9 #include "Vertex.hpp"
10 
11 #include "Logger.hpp"
12 
13 namespace BARE2D {
14 
15  FBORenderer::FBORenderer(std::string& fragShader,
16  std::string& vertShader,
17  unsigned int windowWidth,
18  unsigned int windowHeight,
19  unsigned int numColourAttachments) :
20  m_fragmentShaderPath(fragShader),
21  m_vertexShaderPath(vertShader) {
22  m_camera = std::make_unique<Camera2D>();
23  m_camera->init(windowWidth, windowHeight);
24  m_camera->setFocus(glm::vec2(0.0f));
25  m_camera->setScale(windowWidth / 2.0f, windowHeight / 2.0f);
26 
27  m_numTextures = numColourAttachments + 1; // n colour attachments, one depth attachment
28  }
29 
31  delete[] m_textureIDs;
32  }
33 
34  void FBORenderer::setCamera(std::shared_ptr<Camera2D>& cam) {
35  m_camera = cam;
36  }
37 
38  std::shared_ptr<Camera2D> FBORenderer::getCamera() {
39  return m_camera;
40  }
41 
42  void FBORenderer::disableAttachment(unsigned int index) {
43  glColorMaski(index, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
44  }
45 
46  void FBORenderer::enableAttachment(unsigned int index) {
47  glColorMaski(index, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
48  }
49 
51  for(unsigned int i = 0; i < m_numTextures - 1; i++) {
52  glColorMaski(i, GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE);
53  }
54  }
55 
57  for(unsigned int i = 0; i < m_numTextures - 1; i++) {
58  glColorMaski(i, GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
59  }
60  }
61 
63  // Actually initialize our shader
65 
66  link({"vertexPosition", "vertexColour", "vertexUV"});
67 
68  Renderer::init(); // Initializes the VAO, so we can add attributes
69 
70  m_vertexArrayObject.addVertexAttribute(3, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, position));
72  GL_UNSIGNED_BYTE,
73  GL_TRUE,
74  sizeof(Vertex),
75  (void*)offsetof(Vertex, colour));
76  m_vertexArrayObject.addVertexAttribute(2, GL_FLOAT, GL_FALSE, sizeof(Vertex), (void*)offsetof(Vertex, uv));
77 
78  // Create the actual textures and FBO
79  createFBO();
80 
81  m_shader.use();
82 
83  initUniforms();
84 
85  m_shader.unuse();
86 
87  bind();
88 
89  // This call specifies which buffers should be drawn to - in our case, it's just the colour attachment for the FBO
90  // This essentially means that the output from the fragment shader goes directly into the FBO's attached colour
91  // texture! Please note that the Framebuffer object actually stores a lot of state information, such as draw
92  // buffers. That's why this is in the init. Here we just generate all the colour attachments from 0 to
93  // (m_numColourAttachments-1).
94  const unsigned int attachments = m_numTextures - 1;
95  GLenum m_colourAttachments[attachments];
96 
97  int max = 0;
98  glGetIntegerv(GL_MAX_DRAW_BUFFERS, &max);
99 
100  for(unsigned int i = 0; i < attachments;
101  i++) { // Remember that the number of colour attachments will always be number of textures - 1
102  m_colourAttachments[i] = (GL_COLOR_ATTACHMENT0 + i);
103  }
104 
105  glNamedFramebufferDrawBuffers(m_fboID, m_numTextures - 1, &(m_colourAttachments[0]));
106 
107  unbind();
108  }
109 
111  // Now, for convenience, set the uniforms of the FBO shader program
112  for(unsigned int i = 0; i < m_numTextures - 1; i++) {
113  GLint colourTexture = i;
114  m_shader.setUniform("colourTexture" + std::to_string(i), &colourTexture);
115  }
116 
117  m_shaderHasDepth = m_shader.doesUniformExist("depthTexture");
118  if(m_shaderHasDepth) {
119  GLint depthTexture = m_numTextures - 1;
120  m_shader.setUniform("depthTexture", &depthTexture);
121  }
122  }
123 
125  glDeleteFramebuffers(1, &m_fboID);
126  m_fboID = 0;
127  glDeleteTextures(m_numTextures, &m_textureIDs[0]);
128  delete[] m_textureIDs;
129  m_textureIDs = nullptr;
130 
132  }
133 
135  Renderer::begin();
136 
137  // This begin function actually should bind the FBO and set the necessary settings
138 
139  // First, obviously bind the FBO & attached textures.
140  bind();
141 
142  // Make sure that we clear the actual buffer objects
143  glClearDepth(1.0f);
144  glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
145 
146  // Enable writing to the depth attachment
147  glEnable(GL_DEPTH_TEST);
148  // Set a normalized depth variable
149  glDepthMask(GL_TRUE);
150  // Set it so that closer depths are smaller numbers (0.0 appears in front of 0.5 which appears in front of 1.0, etc)
151  glDepthFunc(GL_ALWAYS);
152 
153  // Make sure that we blend the alpha channels on each texture
154  glEnable(GL_BLEND);
155  // Blend pretty normally for transparency stuff (according to the docs for glBlendFunc, this is the best
156  // transparency blending config)
157  glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
158 
159  m_shader.use();
160 
161  // Now we will not unbind until the end() call, after all the draw calls that actually populate the FBO's textures'
162  // data.
163  }
164 
166  m_shader.unuse();
167 
168  // All we need to do is unbind the FBO and associated textures.
169  unbind();
170  }
171 
173  // Make sure that the FBO is written as a single texture, essentially - not depth-tested by parts.
174  glDisable(GL_DEPTH_TEST);
175 
176  // Pre-render is going to be pretty bare, as our shader just needs to be called for the uploading of texture data
177  // (in createRenderBatches()), and the only uniforms used are constants.
178  glm::mat4 matrix = m_camera->getCameraMatrix();
179  m_shader.setUniformMatrix("projectionMatrix", GL_FALSE, &matrix);
180  /// The camera should only be used to actually draw the FBO.
181 
182  // Set GL_TEXTUREs
184  for(int i = m_numTextures - 1; i >= 0; i--) {
185  context->setActiveTexture(GL_TEXTURE0 + i);
186  context->bindTexture(GL_TEXTURE_2D, m_textureIDs[i]);
187  }
188 
189  m_camera->update();
190  }
191 
193  // We need to now create the "renderbatches", which in our case are just going to be two triangles (for a
194  // full-screen quad) Easiest way to do this is just to create a glyph, then follow how BasicRenderer did it! Why
195  // re-invent the wheel?
196 
197  // Just render the full "screen" (secondary renderer will render the FBO to a smaller section)
198  glm::vec2 position = glm::vec2(-1.0f); // m_camera->getWorldspacePosition(glm::vec2(0.0f));
199  glm::vec2 size = glm::vec2(2.0f); // m_camera->getWorldspaceSize(m_size);
200 
201  glm::vec4 destRect(position.x, position.y, size.x, size.y);
202  glm::vec4 uvRect(0.0f, 1.0f, 1.0f, -1.0f); // We need to flip the FBO.
203  float depth = 0.0f;
204  Colour col = Colour(255, 255, 255, 255);
205  Glyph fullscreen(destRect, uvRect, m_textureIDs[0], depth, col);
206 
207  // Our collection of 6 vertices (for a square)
208  std::vector<Vertex> vertices;
209  vertices.resize(6);
210 
211  // Actually add the texture to the render batches
212  m_batches.emplace_back(0, 6, fullscreen.texture);
213 
214  // Set up the vertices
215  vertices[0] = fullscreen.topLeft;
216  vertices[1] = fullscreen.bottomLeft;
217  vertices[2] = fullscreen.bottomRight;
218  vertices[3] = fullscreen.bottomRight;
219  vertices[4] = fullscreen.topRight;
220  vertices[5] = fullscreen.topLeft;
221 
222  // Bind the VBO
224 
225  // Upload the data to the VBO
226  glBufferData(GL_ARRAY_BUFFER, vertices.size() * sizeof(Vertex), vertices.data(), GL_DYNAMIC_DRAW);
227 
228  // Unbind the VBO once again
230  }
231 
233  // Ensure that we're actually using the shader for the glDrawArrays call!
234  m_shader.use();
235 
236  // Make sure we're in the regular rendering texture (arbitrarily set to texture0, just for consistency)
238  glContext->setActiveTexture(GL_TEXTURE0);
239 
240  // First we must bind the vertex array object and vertex buffer object
242 
243  preRender();
244 
245  // Create RenderBatches (uploading the vertex data to the bound texture, so that we can actually draw the textures
246  // next.)
248 
249  // Now we can render each renderbatch, uploading their texture data respectively.
250  for(unsigned int i = 0; i < m_batches.size();
251  i++) { // regularly, there will only be one - to the full screen... but you never know!
252  // Bind the texture information to the texture "slot"
253  // This method of binding the textures decreases speed slightly for completely random textures, but if we are
254  // rendering a lot of the same texture, this is similar (if not identical) to instance rendering
255 
256  // Upload the data
257  glDrawArrays(GL_TRIANGLES, (GLint)m_batches[i].offset, (GLsizei)m_batches[i].numVertices);
258  }
259 
260  // Unbind for safety!
262 
263  // Release the shader
264  m_shader.unuse();
265  }
266 
268  // Time to actually create the textures for the colour and depth to be stored into.
269  // Create array for handles
270  m_textureIDs = new GLuint[m_numTextures];
271 
272  // Create the texture instances and get the handles
273  glGenTextures(m_numTextures, &m_textureIDs[0]);
274 
275  // Now, for each texture we need to bind it, define it, then attach it to the FBO
276  for(unsigned int i = 0; i < m_numTextures; i++) {
277  // first, set the active texture to that of GL_TEXTURE0 + i, so each attachment gets its own "spot"
279 
280  // Now bind the texture to its slot
282 
283  // Just define the texture's basic properties - how it stores data, its size, etc. No data is added here.
284  GLenum attachmentType =
285  ((i == m_numTextures - 1) && m_shaderHasDepth) ? GL_DEPTH_ATTACHMENT : (GL_COLOR_ATTACHMENT0 + i);
286  switch(attachmentType) {
287  case GL_DEPTH_ATTACHMENT:
288  /*// Set the texture's type to 32 total bits, with 8 reserved for the stencil.
289  glTexImage2D(GL_TEXTURE_2D,
290  0,
291  GL_DEPTH24_STENCIL8,
292  m_camera->getViewspaceResolution().x,
293  m_camera->getViewspaceResolution().y,
294  0,
295  GL_DEPTH_STENCIL,
296  GL_UNSIGNED_INT_24_8,
297  nullptr);
298  break;*/
299  // Just add a depth component
300  glTexImage2D(GL_TEXTURE_2D,
301  0,
302  GL_DEPTH_COMPONENT,
303  m_camera->getViewspaceResolution().x,
304  m_camera->getViewspaceResolution().y,
305  0,
306  GL_DEPTH_COMPONENT,
307  GL_FLOAT,
308  nullptr);
309  break;
310  default:
311  // Just a regular texture.
312  glTexImage2D(GL_TEXTURE_2D,
313  0,
314  GL_RGBA,
315  m_camera->getViewspaceResolution().x,
316  m_camera->getViewspaceResolution().y,
317  0,
318  GL_RGBA,
319  GL_UNSIGNED_BYTE,
320  nullptr);
321  break;
322  }
323 
324  // Give the texture some more basic settings.
325  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
326  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
327  float backColour[] = {0.0f, 0.0f, 0.0f, 0.0f}; // Fully transparent
328  glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, backColour);
329  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
330  glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
331 
332  // Now that we've defined the texture, we need to attach it to the FBO!
333  // Attaches either a color or depth attachment of type texture2D with 0 mipmapping levels.
334  glFramebufferTexture2D(GL_FRAMEBUFFER, attachmentType, GL_TEXTURE_2D, m_textureIDs[i], 0);
335 
336  // Now that its attached, we can unbind the texture
337  GLContextManager::getContext()->bindTexture(GL_TEXTURE_2D, 0);
338  }
339  }
340 
342  // Tell OpenGL we want to create an FBO, and store its handle
343  glGenFramebuffers(1, &m_fboID);
344 
345  // Bind it
346  glBindFramebuffer(GL_FRAMEBUFFER, m_fboID);
347 
348  // Create all of its "child" textures
349  createTextures();
350 
351  // Check to make sure it worked.
352  GLint status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
353  if(status != GL_FRAMEBUFFER_COMPLETE) {
355  // Set the fbo handle to 0 to make sure we show its borked
356  m_fboID = 0;
357  }
358 
359  // Finally, unbind it
360  glBindFramebuffer(GL_FRAMEBUFFER, 0);
361  }
362 
364  // First, the FBO
365  glBindFramebuffer(GL_FRAMEBUFFER, m_fboID);
366 
367  // Now the textures.
369  for(unsigned int i = 0; i < m_numTextures - 1; i++) {
370  context->setActiveTexture(GL_TEXTURE0 + i);
371  context->bindTexture(GL_TEXTURE_2D, m_textureIDs[i]);
372  }
373  }
374 
376  // First, unbind the textures
377  for(unsigned int i = 0; i < m_numTextures - 1; i++)
378  GLContextManager::getContext()->unbindTexture(GL_TEXTURE_2D, GL_TEXTURE0 + i);
379 
380  // Now, unbind the FBO
381  glBindFramebuffer(GL_FRAMEBUFFER, 0);
382  }
383 
384 } // namespace BARE2D
BARE2D::FBORenderer::m_fboID
GLuint m_fboID
Definition: FBORenderer.hpp:69
BARE2D::ShaderProgram::setUniformMatrix
void setUniformMatrix(const std::string uniform, bool transpose, T *data, unsigned int num=1)
Similar to setUniform.
BARE2D::FBORenderer::~FBORenderer
virtual ~FBORenderer()
Definition: FBORenderer.cpp:30
BARE2D::Vertex
Just holds vertex data for convenience.
Definition: Vertex.hpp:39
BARE2D::FBORenderer::m_vertexShaderPath
std::string m_vertexShaderPath
Definition: FBORenderer.hpp:81
BARE2D::ShaderProgram::use
void use()
Activates this shader program for the renderer to use.
Definition: ShaderProgram.cpp:147
BARE2D
Definition: App.cpp:13
BARE2D::VAO::unbind
void unbind()
Unbinds the vao.
Definition: VAO.cpp:53
BARE2D::FBORenderer::createFBO
virtual void createFBO()
Creates the OpenGL FBO instance and holds its handle in m_fboID;.
Definition: FBORenderer.cpp:341
BARE2D::ShaderProgram::unuse
void unuse()
Deactivates this shader program.
Definition: ShaderProgram.cpp:160
BARE2D::FBORenderer::createTextures
virtual void createTextures()
Creates all of the OpenGL textures, and takes their handles into m_colourTextureID,...
Definition: FBORenderer.cpp:267
BARE2D::Renderer::m_vertexArrayObject
VAO m_vertexArrayObject
Definition: Renderer.hpp:66
BARE2D::GLContext::bindTexture
void bindTexture(GLenum target, GLenum texture)
Binds a texture to target in the currently active texture slot.
Definition: GLContextManager.cpp:22
BARE2D::FBORenderer::preRender
virtual void preRender() override
Does stuff inside of the render function, within the shader's use.
Definition: FBORenderer.cpp:172
BARE2D::FBORenderer::m_numTextures
unsigned int m_numTextures
Definition: FBORenderer.hpp:74
BARE2D::FBORenderer::unbind
virtual void unbind()
Unbinds the FBO + textures.
Definition: FBORenderer.cpp:375
BARE2D::GLContext::unbindTexture
void unbindTexture(GLenum target, GLenum textureslot)
Unbinds a texture.
Definition: GLContextManager.cpp:32
BARE2D::Glyph::topRight
Vertex topRight
Definition: Vertex.hpp:100
BARE2D::FBORenderer::enableAttachment
void enableAttachment(unsigned int index)
Definition: FBORenderer.cpp:46
BARE2D::Renderer::link
virtual void link(std::initializer_list< std::string > attributes)
Definition: Renderer.cpp:85
BARE2D::FBORenderer::disableAttachment
void disableAttachment(unsigned int index)
Definition: FBORenderer.cpp:42
BARE2D::Glyph::bottomRight
Vertex bottomRight
Definition: Vertex.hpp:100
Vertex.hpp
BARE2D::Glyph::bottomLeft
Vertex bottomLeft
Definition: Vertex.hpp:100
BARE2D::ShaderProgram::compileShaders
void compileShaders(const char *vertexShaderPath, const char *fragmentShaderPath)
Compiles the shaders. Does not link them.
Definition: ShaderProgram.cpp:23
BAREErrors.hpp
GLContextManager.hpp
BARE2D::FBORenderer::init
virtual void init() override
Initializes all necessary bits of the renderer.
Definition: FBORenderer.cpp:62
BARE2D::FBORenderer::FBORenderer
FBORenderer(std::string &fragShader, std::string &vertShader, unsigned int windowWidth, unsigned int windowHeight, unsigned int numColourAttachments=1)
Definition: FBORenderer.cpp:15
BARE2D::VAO::bind
void bind()
Binds this vertex array object.
Definition: VAO.cpp:48
BARE2D::FBORenderer::setCamera
void setCamera(std::shared_ptr< Camera2D > &cam)
Definition: FBORenderer.cpp:34
BARE2D::VAO::unbindVBO
void unbindVBO()
Unbinds this VAO's VBO.
Definition: VAO.cpp:62
BARE2D::Renderer::begin
virtual void begin()
Clears the necessary vectors, etc. to prepare for draw() calls, etc.
Definition: Renderer.cpp:13
BARE2D::FBORenderer::createRenderBatches
virtual void createRenderBatches() override
Constructs all of the render batches from data given by, say, draw() calls.
Definition: FBORenderer.cpp:192
BARE2D::Renderer::destroy
virtual void destroy()
Frees all necessary memory.
Definition: Renderer.cpp:43
BARE2D::GLContext
Definition: GLContextManager.hpp:7
BARE2D::Renderer::m_shader
ShaderProgram m_shader
Definition: Renderer.hpp:65
BARE2D::Glyph
The glyph represents a renderbatch's primitive data, which is created from each draw call in the basi...
Definition: Vertex.hpp:87
BARE2D::GLContext::setActiveTexture
void setActiveTexture(GLenum texture)
Sets the active texture "slot". This can be GL_TEXTURE0 to GL_TEXTURE8 (I think. Check the literature...
Definition: GLContextManager.cpp:13
BARE2D::ShaderProgram::setUniform
void setUniform(const std::string uniform, T *data, unsigned int num=1)
A generalized wrapper to find and set a uniform for this shader.
BARE2D::FBORenderer::initUniforms
virtual void initUniforms() override
Initializes all uniforms, such as colour attachments, depth attachments, etc.
Definition: FBORenderer.cpp:110
BARE2D::Glyph::topLeft
Vertex topLeft
Definition: Vertex.hpp:100
BARE2D::FBORenderer::m_fragmentShaderPath
std::string m_fragmentShaderPath
Definition: FBORenderer.hpp:81
BARE2D::Glyph::texture
GLuint texture
Definition: Vertex.hpp:94
BARE2D::FBORenderer::m_camera
std::shared_ptr< Camera2D > m_camera
Definition: FBORenderer.hpp:78
BARE2D::FBORenderer::end
virtual void end() override
Creates the renderbatches, does necessary stuff before render() call.
Definition: FBORenderer.cpp:165
BARE2D::VAO::bindVBO
void bindVBO()
Binds the VAO's VBO.
Definition: VAO.cpp:57
BARE2D::FBORenderer::bind
virtual void bind()
Binds the FBO and appropriate texture attachments.
Definition: FBORenderer.cpp:363
BARE2D::ShaderProgram::doesUniformExist
bool doesUniformExist(const std::string uniform)
Definition: ShaderProgram.cpp:213
BARE2D::FBORenderer::m_shaderHasDepth
bool m_shaderHasDepth
Definition: FBORenderer.hpp:84
BARE2D::GLContextManager::getContext
static GLContext * getContext()
Definition: GLContextManager.cpp:44
BARE2D::BAREError::FBO_FAILURE
@ FBO_FAILURE
BARE2D::FBORenderer::m_textureIDs
GLuint * m_textureIDs
Definition: FBORenderer.hpp:71
BARE2D::FBORenderer::enableAttachments
void enableAttachments()
Definition: FBORenderer.cpp:56
FBORenderer.hpp
BARE2D::Renderer::m_batches
std::vector< RenderBatch > m_batches
Definition: Renderer.hpp:67
BARE2D::FBORenderer::render
virtual void render() override
Actually renders the contents to the screen!
Definition: FBORenderer.cpp:232
BARE2D::FBORenderer::getCamera
std::shared_ptr< Camera2D > getCamera()
Definition: FBORenderer.cpp:38
BARE2D::FBORenderer::disableAttachments
void disableAttachments()
Definition: FBORenderer.cpp:50
BARE2D::FBORenderer::begin
virtual void begin() override
Clears the necessary vectors, etc. to prepare for draw() calls, etc.
Definition: FBORenderer.cpp:134
BARE2D::VAO::addVertexAttribute
void addVertexAttribute(GLint size, GLenum type, GLboolean normalized, GLsizei stride, const void *data)
Wrapper for glVertexAttribPointer - Adds an attribute to the VBO - each vertex data slot will gain so...
Definition: VAO.cpp:66
BARE2D::Colour
An RGBA 8-bit colour value.
Definition: Vertex.hpp:20
BARE2D::throwFatalError
void throwFatalError(BAREError err, std::string message)
Throws an error (fatal). Also calls displayErrors and exits the program.
Definition: BAREErrors.cpp:178
BARE2D::FBORenderer::destroy
virtual void destroy() override
Frees all necessary memory.
Definition: FBORenderer.cpp:124
Logger.hpp
BARE2D::Renderer::init
virtual void init()
Initializes all necessary bits of the renderer.
Definition: Renderer.cpp:25