[LWJGL] 3 - FrameBuffer Objects

I’m trying to work with FBOs, but struggling some. I’ve looked at the LWJGL3 demos on it, I think I understand what I’m seeing, but I’m not making it work correctly for my implementation.

What I want to do is essentially make a texture from my other textures. I just need to be able to generate and draw an image. FBO seems like the right answer, yes?

I used this example to learn from. I don’t actually think I need MultiSampling, but I don’t know what a simpler FBO setup would look like.

But I end up getting:
[icode]Message: GL_INVALID_OPERATION error generated. The SAMPLES values of the read and draw framebuffers should match.[/icode]
I don’t know how to make the samples match. But then I also don’t know if I even need multisampling. I don’t know enough about what’s going on to sniff out how I work with these samples. In my implementation, the creation of the FBO and calling it to draw to the main screen are in separate areas, but I don’t know what to look at to make their states line up.

Specifically in my code, I have almost exactly the same FBO setup as that demo I linked. And I have,

glBindFramebuffer(GL_READ_FRAMEBUFFER, object.fboID());
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);

in my main draw loop. I expect I am missing some statements before this that get the state right for the draw.

I simply have been unable to find the right info on this or other examples to get this one on my own. Really appreciate any help.

There are some other good resources on the internet to look at.
For example (Googled “fbo tutorial”):

For your concrete problem with multisampling: You can attach multisampled textures and renderbuffers to a FBO or you can attach single-sampled textures and renderbuffers to a FBO; never a mixture of them.
If you don’t need multisampling (because you either don’t want antialiasing or want to realize it differently), then you can attach just normal single-sampled textures and renderbuffers to the FBO, created with:

  • glGenTextures/glTexImage2D or glCreateTextures/glTextureStorage2D and glFramebufferTexture2D
  • glGenRenderbuffers/glRenderbufferStorage (without ‘Multisample’) and glFramebufferRenderbuffer
    If you don’t need a depth buffer, you can omit the render buffer creation and attachment.
    You could also use a texture (and not a renderbuffer) as a depth buffer.

Regarding that error, you can only blit between a read framebuffer and a write framebuffer, when either:

  • read has samples >= 0 and write has samples = 0
  • read has samples = N and write has samples = N
    In the former case, the blit performs a sample “resolve” operation. In the latter case, the samples are just copied from the read FBO to the write FBO attachments.
    So if your write framebuffer (e.g. the window’s default framebuffer with index = 0) is also multisampled, and has a different number of samples than your read FBO attachments, the blit will not work.

For more information see section 18.3 “Copying Pixels” (PDF page 538, document page 517) of the OpenGL 4.5 specification.

In general, also read chapter 9 (PDF page 305, document page 284) of the OpenGL 4.5 specification for this.
In particular section 9.2.2.

That was quite helpful. Part of it is just helpful having people vet information. I have trouble discerning what is the right material sometimes. Like, that opengl.org/wiki entry says specifically try not to use that stuff as it isn’t core anymore. I’d looked past it already before making this post. I just don’t know how much of it is still good since I’m still green. I also frankly hadn’t thought of OpenGL having a manual like that, but that was most helpful.

I ended up trying to go off this answer. I have:


public void makeFBOTexture()
{
		   int WIDTH = 1600;
		   int HEIGHT = 900;
		
		  //int FBOTexture; <<for show. is created elsewhere.
		   int fbo;
		   int depthbuffer;
		   
	      fbo = glGenFramebuffersEXT();
	      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
	      
	      //depth buffer
	      depthbuffer = glGenRenderbuffersEXT();
	      glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, depthbuffer);
	      
	      //allocate space for the renderbuffer
	      glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT, WIDTH, HEIGHT);
	      
	      //attach depth buffer to fbo
	      glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, depthbuffer);
	      
	      //create texture to render to
	      FBOTexture = glGenTextures();
	      glBindTexture(GL_TEXTURE_2D, FBOTexture);
	      glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
	      glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, WIDTH, HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, (java.nio.ByteBuffer)null);
	      
	      //attach texture to the fbo
	      glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, FBOTexture, 0);
	      
	      //check completeness
	      if(glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT) == GL_FRAMEBUFFER_COMPLETE_EXT){
	         System.out.println("Frame buffer created sucessfully.");
	      }
	      else
	         System.out.println("An error occured creating the frame buffer.");
	      
	      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, fbo);
	      
	      draw();
	      
	      glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
}

This gives me a texture of size 1600x900 with the contents of that draw() call.

1 problem: This for some reason produces a texture where the X axis is flipped. That is, I have to give texture coords 0,1,1,0 for it to appear right. I just don’t know what this could be related to to make sense of it.

1 shortcoming: As before with the first FBO attempt, I couldn’t grasp how to move the “origin” of where it starts worrying about what is being recorded to the FBO. Like, that draw() command calls stuff that only starts drawing up around the middle of the screen. If I make the WIDTH, HEIGHT vars smaller, it just grabs less content and it starts from the origin (bottom left in this case). What is the correct thing to change? I messed with glViewPort but that never came out right.

I call this only a shortcoming because I could theoretically just find and supply the texture coords for these oversized textures, but that seems wrong.