Skip to main content
GameDev.net gamedev.net
🔒 Locked

Deferred rendering, basic level

Started by splush Feb 20, 2015 at 12:26 AM 6 replies 2.6k views
Original Post
splush
splush

Hi!

I've been lurking on gamedev for a while and this is my first actual post. I am looking for some help with implementing deferred rendering.

The thing I am struggling with at the moment is the actual rendering of the geometry to the g-buffer textures. I have read a lot on the subject but I still feel like I'm falling short.

The scene is just a square object rotating with a point light in the back (the light is irrelevant at this point though). The scene is rendering fine with forward rendering.

from Scene.cpp render function:


	multiplerendertargets->begin();

	// Base image texture
	glActiveTexture(GL_TEXTURE0 + 0);
	glBindTexture(GL_TEXTURE_2D, texture->textureID());

	// Draw the triangles
	glDrawElements(GL_TRIANGLES, indexCount, GL_UNSIGNED_INT, (void*)0);

	multiplerendertargets->end();

where the begin/end functions do the following:


void FrameBufferObjects::begin()
{
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);
	glPushAttrib(GL_VIEWPORT_BIT);
	glViewport(0, 0, this->width, this->height);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

	glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);

	GLenum buffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2 };
	glDrawBuffers(3, buffers);
};
void FrameBufferObjects::end()
{
	glBindFramebuffer(GL_FRAMEBUFFER, 0);
	glPopAttrib();
};

What I am trying to achieve is to output the three textures to the screen separately, the function for this seems to be in working order when not making any draw calls. However, when I make the draw call above and try to output the textures, all I get is a black screen.

Geometry pass fragment shader:


/*
Geometry pass fragment shader (first pass)
*/
#version 400
in VertexData {
	vec3 position;
	vec3 normal;
	vec2 tex_coords;
} vertexIn;

uniform sampler2D tDiffuse;

layout(location = 0) out vec4 diffuseOut;
layout(location = 1) out vec4 positionOut;
layout(location = 2) out vec4 normalsOut;

void main() {
	diffuseOut = vec4(texture(tDiffuse,vertexIn.tex_coords).rbg, 0);
	positionOut = vec4(vertexIn.position, 0);
	normalsOut = vec4(vertexIn.normal, 0);
}

What the scene looks like with forward rendering:

gv3ze2g.png

I feel like I'm missing something elementary but I just have no clue what it is and I'm going a bit blind staring away at the code. I just don't see how it would result in all the textures being completely black?

Any and all help is much appreciated.smile.png

L. Spiro
L. Spiro

I feel like I'm missing something elementary

Did you actually attach textures to the framebuffer? Where is the code proving it?

Why do you call glActiveTexture(GL_TEXTURE0); and glEnable(GL_TEXTURE_2D); inside FrameBufferObjects::begin()?

How are you attempting to draw things after the main render? If you are trying to draw each buffer, where is the code for that? Did you apply the correct framebuffer? Did you set the correct draw buffers?

Never push/pop attributes (the viewport in this case)—manually set the viewport each time you change it. It is good for performance and for stability (it is the only way to know that your viewport is correct).


L. Spiro
I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
splush
splush

I feel like I'm missing something elementary

Did you actually attach textures to the framebuffer? Where is the code proving it?

Why do you call glActiveTexture(GL_TEXTURE0); and glEnable(GL_TEXTURE_2D); inside FrameBufferObjects::begin()?

How are you attempting to draw things after the main render? If you are trying to draw each buffer, where is the code for that? Did you apply the correct framebuffer? Did you set the correct draw buffers?

Never push/pop attributes (the viewport in this case)—manually set the viewport each time you change it. It is good for performance and for stability (it is the only way to know that your viewport is correct).


L. Spiro

Hi Spiro,

First of all thank you for your reply, I'll provide some more information, and answer your questions.

"Did you actually attach textures to the framebuffer?" I initialize the fbo and bind the textures in the following function:


FrameBufferObjects::FrameBufferObjects(int width, int height)
{
	GLenum state;
	this->width = width;
	this->height = height;

	glGenFramebuffers(1, &fbo);
	glBindFramebuffer(GL_FRAMEBUFFER, fbo);

	glGenRenderbuffers(1, &depthHandle);
	glBindRenderbuffer(GL_RENDERBUFFER, depthHandle);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, this->width, this->height);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthHandle);

	glGenRenderbuffers(1, &diffuseRenderTarget);
	glBindRenderbuffer(GL_RENDERBUFFER, diffuseRenderTarget);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA, this->width, this->height);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, diffuseRenderTarget);
	
	glGenRenderbuffers(1, &positionRenderTarget);
	glBindRenderbuffer(GL_RENDERBUFFER, positionRenderTarget);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, this->width, this->height);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_RENDERBUFFER, positionRenderTarget);

	glGenRenderbuffers(1, &normalRenderTarget);
	glBindRenderbuffer(GL_RENDERBUFFER, normalRenderTarget);
	glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA32F, this->width, this->height);
	glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_RENDERBUFFER, normalRenderTarget);

	glGenTextures(1, &diffuseTex);
	glBindTexture(GL_TEXTURE_2D, diffuseTex);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, this->width, this->height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, diffuseTex, 0);

	// Generate and bind the OGL texture for positions
	glGenTextures(1, &positionTex);
	glBindTexture(GL_TEXTURE_2D, positionTex);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA32F, this->width, this->height, 0, GL_RGBA, GL_FLOAT, nullptr);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	// Attach the texture to the FBO
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, GL_TEXTURE_2D, positionTex, 0);

	// Generate and bind the OGL texture for normals
	glGenTextures(1, &normalTex);
	glBindTexture(GL_TEXTURE_2D, normalTex);
	glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA16F, this->width, this->height, 0, GL_RGBA, GL_FLOAT, nullptr);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
	glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
	// Attach the texture to the FBO
	glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, GL_TEXTURE_2D, normalTex, 0);

	state = glCheckFramebufferStatus(GL_FRAMEBUFFER);
	if (state!=GL_FRAMEBUFFER_COMPLETE)
		util::log("Frame Buffer Objects failed");
	//unbind
	glBindFramebuffer(GL_FRAMEBUFFER, 0);

};

It's a bit on the heavy side and I'm sure there are a few unnecessary calls in there. I am not using the depth attachment at the moment.

"How are you attempting to draw things after the main render?" I am using a function that attempts to copy the fbo contents using glBlitFramebuffer(...), it looks like this:


void FrameBufferObjects::showTexture(){

	//Resore destination FBO to GL_DRAW_FRAMEBUFFER
	glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);

	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	//Bind fbo as a read buffer
	bindForReading();

	GLint halfWidth = (GLint)(width / 2.0f);
	GLint halfHeight = (GLint)(height / 2.0f);

	//Diffuse
	glReadBuffer(GL_COLOR_ATTACHMENT0);
	glBlitFramebuffer(0, 0, width, height, 0, halfHeight, halfWidth, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
	
	//Position
	glReadBuffer(GL_COLOR_ATTACHMENT1);
	glBlitFramebuffer(0, 0, width, height, 0, 0, halfWidth, halfHeight, GL_COLOR_BUFFER_BIT, GL_LINEAR);

	//Normal
	glReadBuffer(GL_COLOR_ATTACHMENT2);
	glBlitFramebuffer(0, 0, width, height, halfWidth, halfHeight, width, height, GL_COLOR_BUFFER_BIT, GL_LINEAR);
}

and the bind for reading is just glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo), nothing else.

As for the glActiveTexture(GL_TEXTURE0); and glEnable(GL_TEXTURE_2D); calls, they were an accidental duplicate call, they are now only being called in the main render.

Also noted the fact that viewport should be handled manually, thank you.

The showTexture function is currently outputting the following result:

iqeSuMW.png

Where the background is cleared to a gray tone, and the contents of the three color attachments are displayed in the remaining 3 quads. Although these display nothing but the color black, and that's the problem.

I am going to try and debug the textures with gdebuffer and see where it all goes wrong.

My question now is, am I binding the buffers correctly and am I trying to display these in a sensible way?

The framebuffer status check always returns complete.

agleed
agleed

You need to show where you bind the textures to the texture slots, and where and how you set up the corresponding uniforms in the shaders.

splush
splush

You need to show where you bind the textures to the texture slots, and where and how you set up the corresponding uniforms in the shaders.

Hi agleed and thank you for posting,

Where I bind the textures to the texture slots? I'm not sure what you mean, so I'll address the both possibilities I can think of:

either you're talking about attaching the textures to the color attachments, which is done in the fbo constructor

ex.


glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, diffuseTex, 0);

Or you are referring to


	glActiveTexture(GL_TEXTURE0);
	glEnable(GL_TEXTURE_2D);
	glBindTexture(GL_TEXTURE_2D, multiplerendertargets->getDiffuseTex());
	glUniform1i(diffuseID, 0);

Which, if my understanding is correct, I don't really have to invoke until I am about to perform the lighting pass (please do correct me if I'm wrong).

As for the uniforms being used in the shader, the only one that is new since the forward rendering is tDiffuse, which is passed with the following code:


diffuseID = shaderProgram->uniformLocation("tDiffuse");

//Function origin

GLuint ShaderProgram::uniformLocation(const char* name) const {
	return glGetUniformLocation(shaderProgram, name);
}

And is called as a uniform sampler2D in the fragment shader (code is in the first post).

The rendering to the color attachments shouldn't require me to bind the textures to texture slots, it should complete with the drawbuffer command if I'm not mistaken.

I hope I answered your question, I'm not certain I did.

splush
splush

Quick update: I tried changing the output to some arbitrary vec4 from the fragment shader such as (1,1,0,0) and it still displays the same way as before. So the problem is (probably) not in the shaders. I'll do further debugging and make an update in the morning.

L. Spiro
L. Spiro

Renderbuffers are write-only. There is no reason to bind them.

I don’t know how you pass the framebuffer-complete check, since all attachments must be the same number of bits total.

Finally, the way in which you attempt to display the framebuffer is simply weird. Attach the textures regularly and sample them in a shader.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid
splush
splush

Just a final update: I ended up re-doing my gbuffer class from scratch since it was so messy, and it ended up solving the issues I was having. Deferred rendering is now implemented and running smoothly.

Thanks for all the help, it's much appreciated.

For anyone having trouble with deferred rendering I would like to suggest a few guides that I found helpful:

http://ogldev.atspace.co.uk/www/tutorial35/tutorial35.html

http://gamedevelopment.tutsplus.com/articles/forward-rendering-vs-deferred-rendering--gamedev-12342 (not a guide, more of an overview).

http://gamedevs.org/uploads/deferred-shading-tutorial.pdf

Splush

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.