Advertisement

OpenGL not rendering all VertexBuffers when using FrameBuffer

Started by April 29, 2020 03:49 AM
3 comments, last by 21st Century Moose 4 years, 6 months ago

My OpenGL renderer works fine when I do simple forward rendering, but when I try to render to a frame buffer it does not display the last Vertex Array that I tell it to draw. I've changed the order in which they are passed and it always draws all but the last one.

I'm hoping that it's just something silly, like calling something out of order or forgetting to switch a value.

Drawing directly to the back buffer
Drawing to the frame buffer

Here's the render code:

void OpenGLRenderer::render(std::vector<RenderObject>& scene, const Camera& camera)
{
	glBindFramebuffer(GL_FRAMEBUFFER, camera.targetBuffer); // remove to draw directly to back buffer
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	for (auto currentObject : scene)
	{
		glUseProgram(currentObject.shader);
		glBindVertexArray(currentObject.model);
		GLint currentModelSize;
		glGetBufferParameteriv(GL_ELEMENT_ARRAY_BUFFER, GL_BUFFER_SIZE, &currentModelSize);
		currentModelSize /= sizeof(GL_UNSIGNED_INT);
		GLuint transformLocation = glGetUniformLocation(currentObject.shader, "transform");
		glm::mat4 mvp = camera.projection * camera.view * currentObject.transform;
		glUniformMatrix4fv(transformLocation, 1, GL_FALSE, glm::value_ptr(mvp));
		glDrawElements(GL_TRIANGLES, currentModelSize, GL_UNSIGNED_INT, (void*)0);
	}
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
}

and the main run loop:

while (isRunning)
{
	getInput();
	renderer.render(scene, camera);
	renderer.drawBufferToWindow(outputFrameBuffer.textureId, 800, 600); // remove to draw directly to backbuffer
	glfwSwapBuffers(window);	
}

the function do draw the framebuffer to the screen:

void OpenGLRenderer::drawBufferToWindow(const GLuint textureToRender, const unsigned int windowWidth, const unsigned int windowHeight)
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);	// set default (window) render target
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glViewport(0, 0, windowWidth, windowHeight);
	glEnableVertexAttribArray(0); // make sure no other arrays are bound
	glBindBuffer(GL_ARRAY_BUFFER, windowRenderPoly);
	glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, (void*)0);
	glBindTexture(GL_TEXTURE_2D, textureToRender);
	glUseProgram(windowRenderShader);
	glDrawArrays(GL_TRIANGLES, 0, 3);
	glDisableVertexAttribArray(0);
}

finally the renderObject class:

class RenderObject
{
public:
	unsigned int shader;
	unsigned int model;
	glm::mat4 transform;
};

I also found that if I remove the glClear command in the main render function it draws everything, even when drawing to the frame buffer. Obviously it can't actually be used this way though, as it leaves artifacts from previous frames.

The way i see it you dont draw it correctly. You dont take the same steps you use when drawing to main buff

Advertisement

Impossible to say what's wrong. A thing I don't understand: you define the buffer layout every frame, this is only done once usually. And it's good habit to store releavnt information like number of elements somewhere for direct use instead of asking opengl for the size and dividing by the number of elements.

And yes, when the clear is removed there is no clearing and the screen will fill up with all the drawing. I see two calls to glClear, maybe you clear away previously drawn things. After clearing once at the beginning of a frame one draws to the framebuffer observing good habits, and after that blit it to the default buffer for display.

One can clear the buffers to different colors to see if either drawing or blitting doesn't work.

This is happening because the VAO for the last object drawn is still bound when you draw the FBO to the screen. The vertex array state you set up at this time therefore overwrites the state for that last object saved in the VAO and you lose the state for the last object.

To resolve, just bind another VAO at the start of your OpenGLRenderer::drawBufferToWindow routine (it can be VAO 0 if you're using a compatibility context).

I also found that if I remove the glClear command in the main render function it draws everything, even when drawing to the frame buffer.

This is happening because it's actually drawing correctly the first time through your rendering loop, but second and subsequent times you'll have overwritten the state in that last object's VAO and it draws incorrectly. When you don't clear it preserves the framebuffer from the first, correct, pass and it looks right (even though it's still not drawing the last object, the preserved framebuffer makes it look as though it is).

So what's happening looks like this:

  • First time through loop
  • Bind VAO object 0
  • Draw object 0
  • Bind VAO object 1
  • Draw object 1
  • Draw FBO to screen → this overwrites the state stored in the VAO for object 1
  • Second and subsequent times
  • Bind VAO object 0
  • Draw object 0
  • Bind VAO object 1 → this VAO now has overwritten state
  • Draw object 1

Direct3D has need of instancing, but we do not. We have plenty of glVertexAttrib calls.

This topic is closed to new replies.

Advertisement