Render To Texture

Started by CodeBunny, March 23, 2011, 00:51:41

Previous topic - Next topic

CodeBunny

So, I'm writing a convenience class for rendering to textures, and I've got a bit of a problem. Basically, I think that I start rendering to texture with the method I have in place, but I never stop, so my subsequent render operations are all to the texture and not to the screen. I've been looking over it for a while and I'm tired, so I figure I'll just post the lot and ask what I'm doing wrong.

Rendering to a texture is set up to actually be "recursive," you can begin rendering to another texture while drawing to another, and pick up where you left off. I haven't been able to do enough testing yet to see how well that's working, so I might be missing something there as well.

Here are the pertinent methods:

private static Stack<Integer> frameBuffersActive = new Stack<Integer>();

	/** Begins drawing to the specified texture. */
	public static boolean beginDrawingTo(Texture texture)
	{
		if(renderToTextureEnabled())
		{
			IntBuffer buffer = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
			EXTFramebufferObject.glGenFramebuffersEXT(buffer);
			frameBuffersActive.push(buffer.get());
			
			EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
					frameBuffersActive.peek());
			EXTFramebufferObject.glFramebufferTexture2DEXT( EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
					EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, texture.getTextureID(), 0);

			checkFBO(frameBuffersActive.peek());

			GL11.glPushMatrix();
			GL11.glOrtho(0, texture.getImageWidth(), 0, -texture.getImageHeight(),  -1, 1);
			
			GL11.glLoadIdentity();
			GL11.glTranslated(0, WindowManager.getScreenHeight() - texture.getImageHeight(), 0);
			GL11.glTranslated(texture.getImageWidth() / 2, texture.getImageHeight() / 2, 0);
			GL11.glScalef(1f, -1f, 1f);
			
			return true;
		}
		return false;
	}
	
	public static void finishDrawing()
	{
		frameBuffersActive.pop();
		if(!renderingToTexture())
		{
			EXTFramebufferObject.glBindFramebufferEXT( EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);
		}
		else
		{
			EXTFramebufferObject.glBindFramebufferEXT( EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 
					frameBuffersActive.peek());
		}
		GL11.glPopMatrix();
	}

	public static void checkFBO(int FBOID)
	{
		int framebuffer = EXTFramebufferObject.glCheckFramebufferStatusEXT( EXTFramebufferObject.GL_FRAMEBUFFER_EXT ); 
		switch ( framebuffer )
		{
			case EXTFramebufferObject.GL_FRAMEBUFFER_COMPLETE_EXT:
				break;
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT:
				throw new RuntimeException( "FrameBuffer: " + FBOID
						+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT exception" );
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT:
				throw new RuntimeException( "FrameBuffer: " + FBOID
						+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT exception" );
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT:
				throw new RuntimeException( "FrameBuffer: " + FBOID
						+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT exception" );
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT:
				throw new RuntimeException( "FrameBuffer: " + FBOID
						+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT exception" );
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT:
				throw new RuntimeException( "FrameBuffer: " + FBOID
						+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT exception" );
			case EXTFramebufferObject.GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT:
				throw new RuntimeException( "FrameBuffer: " + FBOID
						+ ", has caused a GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT exception" );
			default:
				throw new RuntimeException( "Unexpected reply from glCheckFramebufferStatusEXT: " + framebuffer );
		}
	}
[\code]

So, in short: what do I have to do to make this work like intended?

broumbroum

check your puch-n-pops. you do pop() on finishDrawing() but it shouldn't if texturerendering is not enabled.
:)

CodeBunny

Good point. Fixed.

However, I've still got the issue - render to texture is enabled on my computer, so that wasn't causing a problem. I enter the render-to-texture, and rendering calls stop going to the display (and I assume that they go to the texture I specified), but even though I call the code to stop rendering to texture, the display isn't updated any further and I assume that all further calls are going to the texture still.

I've simplified to be closer to the tutorial here: http://lwjgl.org/wiki/index.php?title=Using_Frame_Buffer_Objects_%28FBO%29

public static boolean beginDrawingTo(Texture texture)
	{
		if(renderToTextureEnabled())
		{
			IntBuffer buffer = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
			EXTFramebufferObject.glGenFramebuffersEXT(buffer);
			frameBuffersActive.push(buffer.get());
			
			EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
					frameBuffersActive.peek());
			EXTFramebufferObject.glFramebufferTexture2DEXT( EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
					EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, texture.getTextureID(), 0);

			checkFBO(frameBuffersActive.peek());
			
			int width = texture.getImageWidth();
			int height = texture.getImageHeight();

			GL11.glPushAttrib(GL11.GL_VIEWPORT_BIT);
			GL11.glViewport(0, 0, width, height);
			
			GL11.glLoadIdentity();
			GL11.glTranslated(width / 2, height / 2, 0);		// Makes all render operations centered on the canvas.
			
			return true;
		}
		return false;
	}
	
	public static boolean finishDrawing()
	{
		if(renderToTextureEnabled() && renderingToTexture())
		{
			frameBuffersActive.pop();
			if(renderingToTexture())
			{
				EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 
						frameBuffersActive.peek());
			}
			else
			{
				EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);
			}
			GL11.glPopAttrib();
			return true;
		}
		return false;
	}

broumbroum

I was talking about frameBuffersActive.push() and pop() :

public static boolean beginDrawingTo(Texture texture)
{
if(renderToTextureEnabled())
{
IntBuffer buffer = ByteBuffer.allocateDirect(4).order(ByteOrder.nativeOrder()).asIntBuffer();
..
frameBuffersActive.push(buffer.get());

..
frameBuffersActive.peek());
..
checkFBO(frameBuffersActive.peek());
...
return true;
}
return false;
}

public static boolean finishDrawing()
{
if(renderToTextureEnabled() && renderingToTexture())
{
System.out.println("Stopping render to texture!");
// NOT THERE frameBuffersActive.pop(); 
if(renderingToTexture())
{
frameBuffersActive.pop(); // <- BUT THERE BELOW
System.out.println("Rendering to: " + frameBuffersActive.peek());
EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT,
frameBuffersActive.peek());
}
else
{
System.out.println("All renders to texture completed!");
...
}
...
return true;
}
return false;
}

CodeBunny

Yeah. I thought I fixed that in my updated method.

That's a good thing to notice, but there still seems to be a problem on the OpenGL side of things in that the rendering doesn't go back to the display proper. After I enter a render to texture, I don't stop, even when I call finishDrawing() (which I thought mirrored the tutorial's methodology). So, I end up with a screen exactly how it was before I began rendering to the texture.

broumbroum

the tutorial ends up on FBO rendering. But then you should bind the texture you rendered to (my textureID) .
This binds FBO to texture (done at the begginning)
EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, myTextureID, 0);

such as rendering a normal textured quad (essentially):
finishdrawing();
GLTexture2D.bind(myTextureID);
GLbegin();
// render a quand
GLvertex();...
GLEnd();


CodeBunny

Quote from: broumbroum on March 25, 2011, 10:45:06This binds FBO to texture (done at the begginning)
EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, myTextureID, 0);

Don't I do that? :\ That's how I start out with the FBO.



Quote from: broumbroum on March 25, 2011, 10:45:06
...then you should bind the texture you rendered to (my textureID).

Trust me, I do that. This is a 2D engine, and I have an Image class that renders textured quads. It makes a display list of the quad and then, to render:

draw()
	{
		texture.bind();
		GL11.glCallList(displayListID);
	}


What I do to render to an image is pass a reference to an Image's texture (which has already been defined with a width and height), so that after I've finished rendering, calls to Image.draw() will use the modified texture automatically.

Broumbroum, I really appreciate the help, but I don't think you understand the problem. I've gone over the code and it seems like it's the same as in the tutorial - I've copied and pasted a few of the methods, changing the variables so that they match with my code. I quite clearly begin rendering to the FBO, but I never seem to stop, even though I definitely call finishDrawing().

Here's an example:

I'm working on a game, and on the menu I'm trying to add some pre-rendered text (Some text is rendered onto a texture and thereafter the texture is drawn instead of worrying about each character). Here's an example of the menu without it:



However, when I try to use the pre-rendered text (which uses these render-to-texture methods):



It first renders the background, and then begins drawing to the texture. That finishes (I've used debug code and it goes on to other methods without errors), but the additional render operations (namely, the rest of the menu and then further frames as the game continues) don't show up on screen. The only thing I can think of is that the rendering is still stuck in render-to-texture.

This is directly correlated to my FBO code. Take it out, and it works fine. Put it back in, problem. It's reproducible on other projects, as well.

jediTofu

I'm thinking you need to unbind the FBO once you're done drawing to it.  Add this after all of that is done:

glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
cool story, bro

CodeBunny

Like this?

EXTFramebufferObject.glBindFramebufferEXT(EXTFramebufferObject.GL_FRAMEBUFFER_EXT, 0);


It's in there, and my biggest frustration is that it doesn't seem to be working. I've put a System.out.println("Finishing Drawing!"); right before it as a test, and sure enough, it enters the code block. It's getting called.

PS: I probably sound pissy about this whole thing - if so, I'm sorry. I'm really grateful to everyone who's trying to help, I'm just really frustrated by this bug.

jediTofu

Try creating a new project and copying verbatim example code, while also having code that draws to the screen.  Just search google for example code.  Here is what I found:

http://www.opengl.org/wiki/GL_EXT_framebuffer_object

If it still doesn't work even with exact code that should work from the web, then it may even be a problem with LWJGL's underlying code.

Also, I know pop has already been mentioned, but I don't see "GL11.glPopMatrix();" after messing with the matrix.  I think you 2 were talking about a different pop, unless I am mistaken.  It's worth a shot...
cool story, bro

broumbroum

Quote from: CodeBunny on March 25, 2011, 14:26:00
Quote from: broumbroum on March 25, 2011, 10:45:06This binds FBO to texture (done at the begginning)
EXTFramebufferObject.GL_COLOR_ATTACHMENT0_EXT, GL11.GL_TEXTURE_2D, myTextureID, 0);

Don't I do that? :\ That's how I start out with the FBO.



Quote from: broumbroum on March 25, 2011, 10:45:06
...then you should bind the texture you rendered to (my textureID).

Trust me, I do that. This is a 2D engine, and I have an Image class that renders textured quads. It makes a display list of the quad and then, to render:

draw()
	{
		texture.bind();
		GL11.glCallList(displayListID);
	}

....
This is directly correlated to my FBO code. Take it out, and it works fine. Put it back in, problem. It's reproducible on other projects, as well.
I do understand what you're asking. You need help for debugging. I was in a similar issue for rendering Font with tesselation. The problem disappeared by replacing display lists with the actual lines of code they were encapsulating.
Try without callList and see what happens. CallList only registers parts of the GLcalls and  states.
I'm done with my remarks ! .)

CodeBunny

Broumbroum - you're right, for some reason it's the font render code (that uses display lists) that messes up. After changing a few things, using my Image class works fine, but fonts still cause it to crash.

At least I know why it's happening now. Thanks!  :)