FBO problem - renders only the blue component

Started by
9 comments, last by jffortin 17 years, 1 month ago
Hi, I was trying to setup a frame buffer and when I'm rendering the texture on a quad it's only rendering the blue component. The problem is that if I'm not rendering to the FBO all the colors are correct. And rendering to the FBO and then the FBO to the screen only shows the blue component. I spent a couple of hours trying to figure out the bug. After comparing my code with other frame buffer code I found on the internet and not seeing any "major" differences (well... not things that could make this to render only the blue component) I decided to come here and ask for help :). This is the code I use to setup the FBO:

	// Create frame buffer with the texture and depth buffer
	::glGenFramebuffersEXT(1, &m_hFBO);
	::glGenRenderbuffersEXT(1, &m_hDepthBuffer);
	::glGenTextures(1, &m_hTexture);

	// Bind the FBO
	::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, m_hFBO);

	// Setup the FBO (texture)
	::glBindTexture(GL_TEXTURE_2D, m_hTexture);
	::glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, m_nWidth, m_nHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
	::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	::glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
	::glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, m_hTexture, 0);
	::glGenerateMipmapEXT(GL_TEXTURE_2D);

	// Setup the FBO (depth)
	::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, m_hDepthBuffer);
	::glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, m_nWidth, m_nHeight);
	::glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, m_hDepthBuffer);

	// Check status
	GLenum err = ::glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
	if (err != GL_FRAMEBUFFER_COMPLETE_EXT)
	{
		// Error
		assert(false);
	}

	::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
	::glBindTexture(GL_TEXTURE_2D, 0);
	::glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);

This is the code I use to set the FBO:

		// Unbind any texture
		::glBindTexture(GL_TEXTURE_2D, 0);

		// Bind the frame buffer
		::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, pTarget->m_hFBO);

		// Push the viewport
		::glPushAttrib(GL_VIEWPORT_BIT);
		::glViewport(0, 0, pTarget->GetWidth(), pTarget->GetHeight());

This is the code I use to set the target to null:

		// Pop the view port attribute and set the target to null
		::glPopAttrib();
		::glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

This is the code used to render the FBO on a quad:

	float nWidth = (float)m_pWindow->GetWidth();
	float nHeight = (float)m_pWindow->GetHeight();

	// Push the projection matrix and set the new projection
	::glMatrixMode(GL_PROJECTION);
	::glPushMatrix();
	::glLoadIdentity();
	::glOrtho(0, nWidth, 0, nHeight, 0.1f, 1.0f);

	// Push the transform matrix and translate
	::glMatrixMode(GL_MODELVIEW);
	::glPushMatrix();
	::glLoadIdentity();
	::glTranslatef(0.0f, 0.0f, -0.5f);

	// Render the target buffer to the back buffer.
	::glEnable(GL_TEXTURE_2D);
	::glBindTexture(GL_TEXTURE_2D, pTarget->m_hTexture);

	::glClear (GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

	// Draw the textured quad
	::glBegin(GL_QUADS);
		::glTexCoord2f(1.0f, 1.0f); glVertex3f(nWidth, nHeight, 0.0f);
		::glTexCoord2f(0.0f, 1.0f); glVertex3f(0.0f, nHeight, 0.0f);
		::glTexCoord2f(0.0f, 0.0f); glVertex3f(0.0f, 0.0f, 0.0f);
		::glTexCoord2f(1.0f, 0.0f); glVertex3f(nWidth, 0.0f, 0.0f);
	::glEnd();

	// Pop the transform matrix from the stack
	::glPopMatrix();

	// Pop the projection matrix from the stack
	::glMatrixMode(GL_PROJECTION);
	::glPopMatrix();

	// Set the mode to the model-view matrix
	::glMatrixMode(GL_MODELVIEW);

	// Swap the buffers
	::SwapBuffers(m_hDC);

This is what I'm rendering on the FBO and only:

	::glTranslatef(0.0f, 0.0f, -5.0f);

	::glBegin(GL_TRIANGLES);
		::glColor3f(1.0f, 0.0f, 0.0f);
		::glVertex3f(0.0f, 1.0f, 0.0f);

		::glColor3f(0.0f, 1.0f, 0.0f);
		::glVertex3f(1.0f, 0.0f, 0.0f);

		::glColor3f(0.0f, 0.0f, 1.0f);
		::glVertex3f(-1.0f, 0.0f, 0.0f);
	::glEnd();

It looks exactly like if the two first glColor3f were setting the color to black and the third one to blue. JFF
Advertisement
i couldn't find any problems with your code, except that you don't need that genMipmaps command, since you're not setting up your texture with mipmaps, also it uses like 100% cpu to generate them every frame anyway. As for the colour not showing up, have you made sure that lighting is disabled? I thought maybe the two other verts are not lit for some reason.

p.s. I'm also kinda surprised to see you're from around where I live...what are the odds?

EDIT:
Here is my FBO setup code....the only difference is that i used GL_RBGA8 in texture creation, and that could actually be the problem, it took me forever to get fbos working and i found them to be very finicky. Good luck.

bool Framebuffer::Init(unsigned int width, unsigned int height, bool depthTexture, bool colourTexture, bool stencilBuffer){	if(fbID!=0)		return false;	Framebuffer::width = width;	Framebuffer::height = height;	if(width==height)		target = GL_TEXTURE_2D;	else		target = GL_TEXTURE_RECTANGLE_ARB;	glGenFramebuffersEXT (1, &fbID);	glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, fbID);	if(depthTexture)	{		glGenTextures(1, &depthTex);		glBindTexture (target, Framebuffer::depthTex);		glTexImage2D (target, 0, GL_DEPTH_COMPONENT24, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, NULL);		glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);		glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, target, depthTex, 0);	} else {		glGenRenderbuffersEXT(1, &depthRenderBuffer);		glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthRenderBuffer);		glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, width, height);		glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthRenderBuffer);	}	if(colourTexture)	{		glGenTextures(1, &colourTex);		glBindTexture(target, colourTex);		glTexImage2D(target, 0, GL_RGBA8, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);		glTexParameterf(target, GL_TEXTURE_WRAP_S, GL_CLAMP);		glTexParameterf(target, GL_TEXTURE_WRAP_T, GL_CLAMP);		glTexParameteri(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);		glTexParameteri(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);		glFramebufferTexture2DEXT (GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, target, colourTex, 0);	}	if(stencilBuffer)	{		//I'll include this one I figure out how to use the stencil buffer	}	if(depthTexture&&!colourTexture&&!stencilBuffer)	{		glDrawBuffer (GL_FALSE);		glReadBuffer (GL_FALSE);	}	glBindFramebufferEXT (GL_FRAMEBUFFER_EXT, 0);	if(getStatus())		return true;	else		//for some reason the config given resulted in an incomplete/invalid FBO	{		if(colourTex!=0)			glDeleteTextures(1, &colourTex);		if(depthTex!=0)			glDeleteTextures(1, &depthTex);		if(depthRenderBuffer!=0)			glDeleteRenderbuffersEXT(1, &depthRenderBuffer);		if(stencilRenderBuffer!=0)			glDeleteRenderbuffersEXT(1, &stencilRenderBuffer);		if(fbID!=0)			glDeleteFramebuffersEXT(1, &fbID);		Framebuffer::colourTex = 0;		Framebuffer::depthRenderBuffer = 0;		Framebuffer::depthTex = 0;		Framebuffer::fbID = 0;		Framebuffer::stencilRenderBuffer = 0;		Framebuffer::width = 0;		Framebuffer::height = 0;		return false;	}};
Quote:Original post by arbuckle911
I thought maybe the two other verts are not lit for some reason.

EDIT:
the only difference is that i used GL_RBGA8 in texture creation


For the lighting... the OpenGL code is really minimal I started this yesterday as a framework for a school project. I'm also calling "glDisable(GL_LIGHTING)" in the initialization phase.

Also, changing GL_RGBA to GL_RGBA8 doesn't change anything.

JFF

EDIT:
Quote:Original post by arbuckle911
p.s. I'm also kinda surprised to see you're from around where I live...what are the odds?

I've seen three users that have posted here that are also from Sherbrooke.
I just found out something...

If all the colors are red I see red... same thing for green and blue.

This makes a green triangle:
		::glColor3f(1.0f, 1.0f, 0.0f);		::glVertex3f(0.0f, 1.0f, 0.0f);		::glColor3f(1.0f, 1.0f, 0.0f);		::glVertex3f(1.0f, 0.0f, 0.0f);		::glColor3f(0.0f, 1.0f, 0.0f);		::glVertex3f(-1.0f, 0.0f, 0.0f);




This makes the top of the triangle green and the bottom yellow :
		::glColor3f(0.0f, 1.0f, 0.0f);		::glVertex3f(0.0f, 1.0f, 0.0f);		::glColor3f(1.0f, 1.0f, 0.0f);		::glVertex3f(1.0f, 0.0f, 0.0f);		::glColor3f(1.0f, 1.0f, 0.0f);		::glVertex3f(-1.0f, 0.0f, 0.0f);



This one also renders correctly:
		::glColor3f(0.0f, 1.0f, 0.0f);		::glVertex3f(0.0f, 1.0f, 0.0f);		::glColor3f(1.0f, 0.0f, 0.0f);		::glVertex3f(1.0f, 0.0f, 0.0f);		::glColor3f(1.0f, 1.0f, 0.0f);		::glVertex3f(-1.0f, 0.0f, 0.0f);


But if I add blue to any of those points it won't make any difference. Weird...


JFF
Here's my FBO code. It's in c# with my own opengl bindings, but it should be easy enough to convert.

I notice that I do things in a different order from both of you. And I see places where I'm passing the frame buffer object to BindFramebufferEX, while you're passing the depth buffer. Regardless, my code works fine, and is fairly simple so should be easy to duplicate. But yes, I agree FBO's are cranky. Direct3D render textures are WAY easier.

edit- in fact, the possibility that you may be treating the depth buffer as the color buffer would explain why you only ever see one color. That's just a guess though.

Here's the initialisation:

            gle.GenFramebuffersEXT( 1, out this._FrameBuffer );            gle.GenRenderbuffersEXT( 1, out this._DepthRenderBuffer );            gle.BindRenderbufferEXT( gle.GL_RENDERBUFFER_EXT, this._DepthRenderBuffer );            gle.RenderbufferStorageEXT( gle.GL_RENDERBUFFER_EXT, gle.GL_DEPTH_COMPONENT24, width, height );            int error = gle.CheckFramebufferStatusEXT( gle.GL_FRAMEBUFFER_EXT );            switch ( error )            {                case gle.GL_FRAMEBUFFER_COMPLETE_EXT:                                        break;                default:                    Logger.Instance.Write( "Error creating OpenGL dynamic texture with GL_FRAMEBUFFER_EXT", Priority.High );                    break;            }            gl.GenTextures( 1, out this._GLTexture );            gl.BindTexture( TextureTarget.TwoDimensional, this._GLTexture );            gl.TexImage2D( (int)TextureTarget.TwoDimensional, 0, 3, width, height, 0, TextureImageFormat.RGB, GLType.UnsignedByte, IntPtr.Zero );            gl.TexParameter( TextureTarget.TwoDimensional, TextureParameterName.MagFilter, TextureParameter.Nearest );            gl.TexParameter( TextureTarget.TwoDimensional, TextureParameterName.MinFilter, TextureParameter.Nearest );


Here's where the FBO is bound for rendering:

            gle.BindFramebufferEXT( gle.GL_FRAMEBUFFER_EXT, this._FrameBuffer );            gle.FramebufferTexture2DEXT( gle.GL_FRAMEBUFFER_EXT, gle.GL_COLOR_ATTACHMENT0_EXT, (int)TextureTarget.TwoDimensional, this._GLTexture, 0 );            gle.FramebufferRenderbufferEXT( gle.GL_FRAMEBUFFER_EXT, gle.GL_DEPTH_ATTACHMENT_EXT, gle.GL_RENDERBUFFER_EXT, this._DepthRenderBuffer );


Here's where the FBO is unbound:

gle.BindFramebufferEXT( gle.GL_FRAMEBUFFER_EXT, 0 );


To render the FBO as a texture, you just bind the texture associated with the FBO like you would any other texture.
Quote:Original post by gharen2
I notice that I do things in a different order from both of you.


I tried changing my code so it's in the same order as you and it was the same.

Quote:Original post by gharen2
edit- in fact, the possibility that you may be treating the depth buffer as the color buffer would explain why you only ever see one color. That's just a guess though.


I'm not sure what you mean by "the possibility that you may be treating the depth buffer as the color buffer". I can't figure where that would be... (just curious)



Also, another fact, all the colors seems to render correctly if i set the last vertex to white.

	::glBegin(GL_TRIANGLES);		::glColor3f(0.0f, 1.0f, 0.0f);		::glVertex3f(0.0f, 1.0f, 0.0f);		::glColor3f(1.0f, 0.0f, 1.0f);		::glVertex3f(1.0f, 0.0f, 0.0f);		::glColor3f(1.0f, 1.0f, 1.0f);		::glVertex3f(-1.0f, 0.0f, 0.0f);	::glEnd();
Quote:Original post by jff_f
Also, another fact, all the colors seems to render correctly if i set the last vertex to white.


I guess that was it... with that last sentence. ;)

Well, I set the color to white before rendering the quad to the screen and everything is right. I guess I learned to set things back to default now... I lost too many hours working on that problem.

But one question... The color really has an impact on the quad once its textured ?


Thanks to all who helped [smile].
The bit about the depth buffer was just a guess, as I see a spot where you bind the depth buffer, but I bind the framebuffer. The depth buffer could have been treated as a color buffer with one channel, restricting you to one color.

But as I said it was a shot in the dark. And the fact that making the last vertex white causes it to render correctly proves that's not the case.

Anyways that's pretty weird. What video card are you using? Often laptops and onboard cards have poor quality opengl implementations that have bugs. And the fact that making the last vertex white magically fixes the problem sounds like a bug to me.

The color will have an impact on the textured quad, so long as lighting is disabled. If lighting is enabled, the colors due to the lights will be used instead of the vertex colors.

[Edited by - gharen2 on March 6, 2007 2:30:23 PM]
Quote:Original post by gharen2
Anyways that's pretty weird. What video card are you using? Often laptops and onboard cards have poor quality opengl implementations that have bugs. And the fact that making the last vertex white magically fixes the problem sounds like a bug to me.



I have a GeForce7900 and the bug was also seen on two of my friends computer too (GeForce6600, GeForce6800) all with the latests drivers (I don't remember the version number but it's the latest available on nVidia). I did not try on any ATI hardware since I don't know many people with those.


JFF
Ah I see what's happening, and yeah it's not a bug.

The problem was that the value set with glColor3f is maintained as a state. So the last color you set when rendering the triangle was kept and used when rendering the quad. So yes, setting the color to white before rendering the quad is the right thing to do.

Funny how the simplest bugs are the hardest to find :P

This topic is closed to new replies.

Advertisement