Drawing on top of the scene

Hello everyone,

is there a way to manually draw (and delete) 2D objects on top of the scene without using overlay drawing or XOR drawing?
Overlay drawing doesn’t work on my card, and XOR drawing sometimes leaves artifacts when the objects move, so I’m in big need of a workaround.

Thank you for any advice.
Inos

Draw all GUI objects to a separate framebuffer then blend that FBO texture onto of the main backbuffer to display them. Deleting an object is as simple as not drawing it to the offscreen framebuffer.

Thanks for the reply, BionicBytes!

After staring at your answer for some time, I’ve found a great tutorial about framebuffer objects, but there are some things that confuse me (not surprisingly, since I’m jumping in before learning to swim…):

I actually need to draw a visual aid around the mouse cursor, and it will always be drawn on top of the scene, so there’s no need for mipmaps, right? Also, the visual aid obviously needs to follow the mouse on every move, so is that a problem, with all the texture changing and blending? When the mouse moves, and the underlying scene doesn’t change, do I still have to redraw the whole scene, or can I just remove the previous texture and apply a modified one?

Thank you very much for your patience!

The need for mipmapping depends upon the size of the area you are trying to draw the texture to. If the texture image is large and you are drawing it into a small screen space rectangle - it need reducing - hence mipmapping. This is automatic (assuming you have enabled it) so it should not concern you.

the visual aid obviously needs to follow the mouse on every move, so is that a problem, with all the texture changing and blending?

No problem at all. In my game engine I have an OpenGL mouse pointer which follows the hardware windows mouse.

When the mouse moves, and the underlying scene doesn’t change, do I still have to redraw the whole scene, or can I just remove the previous texture and apply a modified one?

Think of the GUI overlay as a blank canvas which you will blend on top of your main scene every frame. On that blank canvas (off screen texture - or Framebuffer) you will draw the mouse cursor at it’s new position.

So in your case, the off-screen framebuffer will be redrawn every frame, but the ‘main’ contents can be redrawn when there is a change to the scene. Our off-screen framebuffer texture is just blended on top.

Hello BionicBytes!

So, I’ve made some progress - I used the FBO to obtain a texture that looks like this:

But now I’m wondering how to show it on top of the viewport, and do it in such a way that the black area is transparent. Could you please shed some light?

Thanks so much.
Inos

Enable glBlend and set the alpha channel of the drawn area to 1.0 (assuming you cleared the FBO alpha to 0 before rendering).
Use the blend function gl_src_alpha, gl_one_minus_src_alpha

OK, I understood that, I checked the individual pixels of the image - the background is (0, 0, 0, 0) and the red circle pixels are (255, 0, 0, 255).
The part that I don’t seem to understand is how to actually show the texture on top of the scene. I’ve tried just drawing a quad that covers the whole viewport, and texturing it using the following code:


                glPushAttrib(GL_ALL_ATTRIB_BITS);
		glClear(GL_CURRENT_BIT);
		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();

		glMatrixMode(GL_PROJECTION);
		glPushMatrix();
		glLoadIdentity();
		gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
		glMatrixMode(GL_MODELVIEW);
		glShadeModel(GL_SMOOTH);

		glBindTexture (GL_TEXTURE_2D, textureId);
		glBegin (GL_QUADS);
			glTexCoord2f (0.0, 0.0);
			glVertex3f (10.0, 10.0, 0.0);
			glTexCoord2f (0.0, 1.0);
			glVertex3f (10.0, h-10.0, 0.0);
			glTexCoord2f (1.0, 1.0);
			glVertex3f (w-10.0, h-10.0, 0.0);
			glTexCoord2f (1.0, 0.0);
			glVertex3f (w-10.0, 10.0, 0.0);
		glEnd ();
                glBindTexture (GL_TEXTURE_2D, 0);

		glMatrixMode(GL_PROJECTION);
		glPopMatrix();
		glMatrixMode(GL_MODELVIEW);
		glPopMatrix();
		SwapBuffers(view->deviceContext());

		glPopAttrib();

but the quad just appears white (fully opaque) with no texture. Is this the wrong way to do it, or is it right but I’m doing it wrong?

Did you glEnable(GL_TEXTURE_2D) somewhere ?

This is wrong. What are you trying to do - clear the colour buffer? The emuerant for that is GL_COLOR_BUFFER_BIT


		glMatrixMode(GL_MODELVIEW);
		glPushMatrix();
		glLoadIdentity();

You don’t need to swith to the ModelView matrix twice as you also do this just after switching to Projection matrix.

 textureId

What is textureId? Is it the texture which was attached as a color buffer to your FrameBuffer object? Have you properly disabled the framebuffer?

Also, I don’t see anywhere where you have set your viewport.
Depth test should be switched off so that 2D objects can be overlayed onto your screen.

The answer to both questions is yes, here is the code used for initialization:


    // init GL
    glDisable(GL_DEPTH_TEST);
    glEnable(GL_TEXTURE_2D);
	
    //texture initialization
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); 
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
    glTexEnvf(GL_TEXTURE_2D, GL_TEXTURE_ENV_MODE, GL_DECAL);
    glShadeModel(GL_FLAT);
    glBindTexture(GL_TEXTURE_2D, 0);

    //create framebuffer object
    glGenFramebuffersEXT(1, &fboId);
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);

    //create renderbuffer object
    glGenRenderbuffersEXT(1, &rboId);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, rboId);
    glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, TEXTURE_WIDTH, TEXTURE_HEIGHT);
    glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);


    // attach a texture to FBO color attachement point
    glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, textureId, 0);

    // attach a renderbuffer to depth attachment point
    glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, rboId);

    //detach framebuffer object
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

I’ve read that there has to be a renderbuffer attached for depth testing, otherwise the framebuffer will not be complete, but is that true?
If I’ve previously disabled depth testing, is there really the need for a depth buffer?

Isn’t that in the following lines?


    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
    glMatrixMode(GL_MODELVIEW);

If not, then how should I do it properly?

The code I’m using for drawing is the following:


    //set the rendering destination to FBO
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fboId);

    //clear buffer
    glClearColor(0, 0, 0, 0);
    glClear(GL_COLOR_BUFFER_BIT);

    
    glPushAttrib(GL_ALL_ATTRIB_BITS);
    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
    glMatrixMode(GL_MODELVIEW);
    glShadeModel(GL_SMOOTH);
    
    //draw the visual aid around the mouse cursor
    circleDraw(screenCoords, radius, color);

    glMatrixMode(GL_PROJECTION);
    glPopMatrix();
    glMatrixMode(GL_MODELVIEW);
    glPopMatrix();
    
    SwapBuffers(view->deviceContext());

    glPopAttrib();

     //back to normal window-system-provided framebuffer
    glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);

    //generate mipmaps
    glBindTexture(GL_TEXTURE_2D, textureId);
    glGenerateMipmapEXT(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, 0);

This is done before drawing the quad from my previous post.
I apologize for the long post, and thank you so much for you help and patience.


glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); 

This is letting OpenGL auto generate mipmaps.

but later on you use


glGenerateMipmapEXT(GL_TEXTURE_2D);

So…it’s not a good idea to use glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE)
for manual mipmap generation and you should instead call glGenerateMipmapEXT(GL_TEXTURE_2D) at then end of the section defining your texture so that it’s mipmap complete (i.e. after calling glTexImage2D).


    glMatrixMode(GL_PROJECTION);
    glPushMatrix();
    glLoadIdentity();
    gluOrtho2D(0.0, (GLdouble)w, 0.0, (GLdouble)h);
    glMatrixMode(GL_MODELVIEW);

does not setup the viewport - it sets up the projection matix.
The view port is defined with glViewPort (x,y,w,h)

The white texture is now probably due to the FBO attachment not having a valid mipmap chain.
You can also test that by removing the need for mipmaps and change the min texture filtering to GL_LINEAR instead of GL_LINEAR_MIPMAP_LINEAR.

I’ve done everything you suggested, but I just can’t get it to work… Even when I don’t use mipmapping, the quad is still white.
But it would seem that the texture gets properly created, because when I get the pixels into the array with

glGetTexImage(GL_TEXTURE_2D, mipMapLevel, GL_RGBA, GL_UNSIGNED_BYTE, buffer);

everything in the pixel array is as it should be.
I just can’t get it to map onto the quad, which probably means that something else is making the texture object incomplete.
Never would have thought that this could be so painful to get done. I guess I’ve got a lot of reading to do…

Thank you very much for your help!
Inos

try drawing the textured quad to the full size of the screen and viewport. Do not use your previous post where you specify glvertex3f (w-10,…)
…as this means there is a gap or border between the quad and the full screen size. The point here is that this smaller sized quad would be rendered with a mip-map level. I want you to ensure you can render the level 0 mipmap.

Unfortunately, it still renders white (or whatever other color is set before drawing).
At the moment, I have no clue as to what else to change to try and make it work, so I guess there’s no point in taking up your time with random questions…