Blitting several textures at once with glBlitFramebuffer - opengl

I have got a small OpenGL app and I am looking for the optimal way of blitting several texture buffers at once.
Let's say I have got two framebuffers (fbo1, fbo2) that each contain two texture buffers. And I have got a target fbo (fbo3) with four texture buffers. And I want to blit all the textures from fbo1 and fbo2 to fbo3.
Currently I am doing it separately for each texture like,
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo1)
glReadBuffer(GL_COLOR_ATTACHMENT0)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, fbo3)
glDrawBuffer(GL_COLOR_ATTACHMENT0)
glBlitFramebuffer(0, 0, width, height, 0, 0, ds_width, ds_height, GL_COLOR_BUFFER_BIT, GL_LINEAR)
glBindFramebuffer(GL_READ_FRAMEBUFFER, 0)
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0)
How is it usually done? And is that even doable?

It isn't "usually" done because people generally don't go around copying a bunch of framebuffer images a lot. Indeed, if you are, that strongly suggests that you're probably doing something wrong.
The only way to do it is the way you've done here (though the needless rebinding of the framebuffers can go away): change the read/draw buffers each time and blit.

Related

Draw OpenGL renderbuffer to screen

I created a Renderbuffer, that's then modified in OpenCL.
//OpenGL
glGenFramebuffers(1, &frameBuffer);
glBindFramebuffer(GL_FRAMEBUFFER, frameBuffer);
glGenRenderbuffers(1, &colorRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorRenderbuffer);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, 600, 600);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorRenderbuffer);
//OpenCL
renderEngine = new OpenCLProgram("render.cl");
renderEngine->addArgumentGLRBuffer(colorRenderbuffer);
How would I then proceed drawing my OpenCL creation, the buffer to the screen? I could bind it to a texture and draw a quad the size of my window, but I am not that sure, if it is the most efficient way. Also, if there was a better way of drawing to the screen from OpenCL, that would help!
The call you're looking for is glBlitFramebuffer(). To use this, you bind your FBO as the read framebuffer, and the default framebuffer as the draw framebuffer:
glBindFramebuffer(GL_READ_FRAMEBUFFER, srcFbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, srcWidth, srcHeight, 0, 0, dstWidth, dstHeight,
GL_COLOR_BUFFER_BIT, GL_NEAREST);
Adjust the parameters for your specific use based on the linked man page.
This is preferable over writing your own shader and rendering a screen sized quad. Not only is it simpler, and requires fewer state changes, it can also be more efficient. Knowing that a blit operation needs to be performed gives the implementation a chance to use a more efficient path. For example, where present, it could use a dedicated blit engine that can run asynchronously to the general rendering functionality of the GPU.
Whether you should use a renderbuffer or texture is not as clear cut. Chances are that it won't make much of a difference. Still, I would recommend to use a renderbuffer as long as that's all you need. Because it has more limited functionality, the driver has the option to create a memory allocation that is more optimized for the purpose. Rendering to a renderbuffer can potentially be more efficient than rendering to a texture on some hardware, particularly if your rendering is pixel output limited.
Don't make it a renderbuffer.
OpenGL renderbuffers exist for the sole purpose of being render targets. The only OpenGL operations that read from them are per-sample operations during rendering to the framebuffer, framebuffer blits, and pixel transfer operations.
Use a texture instead. There is no reason you couldn't create a 600x600 GL_RGBA8 2D texture.

Multisampling with glBlitFramebuffer

This is my first attempt to do multisampling (for anti-aliasing) with opengl. Basically, I'm drawing a background to the screen (which should not get anti-aliased) and subsequently I'm drawing the vertices that should be anti-aliased.
What I've done so far:
//create the framebuffer:
glGenFramebuffers(1, &fbo);
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//Generate color buffer:
glGenRenderbuffers(1, &cb);
glBindRenderbuffer(GL_RENDERBUFFER, cb);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_RGBA8, x_size, y_size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, cb);
//Generate depth buffer:
glGenRenderbuffers(1, &db);
glBindRenderbuffer(GL_RENDERBUFFER, db);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH_COMPONENT, x_size, y_size);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, db);
...
glBindFramebuffer(GL_FRAMEBUFFER, 0);
//draw background ... ...
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
//draw things that should get anti-aliased ... ...
//finally:
glBindFramebuffer(GL_READ_FRAMEBUFFER, fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, x_size, y_size, 0, 0, x_size, y_size, GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT, GL_NEAREST);
The problem is: when I call glBlitFramebuffer(...) the whole background gets black and I only see the anti-aliased vertices.
Any suggestions?
Normally, blending is the most obvious option if you want to render a new image/texture on top of existing rendering while taking transparency in the image into account. Looking at the rendering into the multisampled framebuffer as an image with transparency, that's exactly the situation you have.
In this case, there are a couple of challenges that make the use of blending more difficult than usual. First of all, glBlitFramebuffer() does not apply blending. From the spec:
Blit operations bypass the fragment pipeline. The only fragment operations which affect a blit are the pixel ownership test and the scissor test.
Without multisampling in play, this is fairly easy to overcome. Instead of using glBlitFramebuffer(), you perform the blit by drawing a screen sized textured quad. Since all fragment operations are in play now, you could use blending.
Howerver, the "drawing a textured quad" part gets much trickier since your content is multisampled. A few options come to mind.
Render background to FBO
You could render the background to the multisampled FBO instead of the primary framebuffer. Then you can use glBlitFramebuffer() exactly as you do now.
You may think: "But I don't want my background to be anti-aliased!" That's not really a problem. You simply disable multisampling while drawing the background:
glDisable(GL_MULTISAMPLE);
I think that should give you what you want. And if it does, it's by far the easiest option.
Multisample Textures
OpenGL 3.2 and later support multisample textures. For this, you would use a texture instead of a renderbuffer as the color buffer of your FBO. The texture is allocated with:
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, 4, GL_RGBA8,
xsize, ysize, GL_FALSE);
There are other aspects that I can't all cover here. If you want to explore this option, you can read up on all the details in the spec or other sources. For example, sampling of the texture in the shader code works differently, with a different sampler type, and sampling functions that only allow you to read one sample at a time.
Two-Stage Blitting
You could use a hybrid of glBlitFramebuffer() for resolving the multisample content, and the "manual" blit for blending the content into the default framebuffer:
Create a second FBO where the color attachment is a regular, not multisampled texture.
Use glBlitFramebuffer() to copy from multisampled renderbuffer in first FBO to texture in second FBO.
Set up and enable blending.
Draw a screen sized quad using the texture that was the attachment to the second FBO.
While this seems somewhat awkward, and requires an extra copy which is undesirable for performance, it is fairly straightforward.
Render the background last
For this, you do exactly what you're doing now, copying the multisampled FBO content to the default framebuffer with glBlitFramebuffer(). But you do this first, and render the background afterwards.
You may think that this wouldn't work because it puts the background in front of the other content, which makes it... not much of a background.
But here is where blending comes into play again. While blending content on top of other content is the most common way of using blending, you can also use it to render things behind existing content. To do this, you need a few things:
A framebuffer with alpha planes. How you request that depends on the window system/toolkit you use for your OpenGL setup. It's typically in the same area where you request your depth buffer, stencil buffer (if needed), etc. It is often specified as a number of alpha planes, which you typically set to 8.
The right blend function. For front to back blending, you typically use:
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
This adds the new rendering where nothing was previously rendered (i.e. the alpha in the destination is 0), and will keep the previous rendering unchanged where there was already rendering (i.e. the destination alpha is 1).
The blending setup can get a little trickier if your rendering involves partial transparency.
This may look somewhat complicated, but it's really quite intuitive once you wrap your head around how the blend functions work. And I think it's overall an elegant and efficient solution for your overall problem.

Copy depth texture to RGBA texture

I have created a texture using
glTexImage2D(GL_TEXTURE_RECTANGLE_NV, 0, CONSENSUS_DEPTH_COMPONENT, width, height, 0, GL_DEPTH_COMPONENT, GL_FLOAT, 0);
This texture is used in other code and filled with depth. Now I want to copy the depth values to an RGBA texture (doesn't matter which color channel).
How can I do this?
If it needs to be fast, I'd say render an orthograhic quad the size of the texture and use a shader to read from the depth texture and write to the target texture.
If performance doesn't matter that much you can use PBOs (might even be faster depending on your render pipeline but stalls the CPU). Here's an overview on said PBOs
I don't know of any inherent OpenGL method to do this.

low resolution in OpenGL to mimic older games

I'm interested in know how is the right way to mimic the low resolution of the older games (like Atari 2600) in OpenGL to do a fps game. I imagine the best way to do it is writing the buffer into a texture, put onto a quad and display it to the screen resolution.
Take a look of http://www.youtube.com/watch?v=_ELRv06sa-c, for example (great game!)
Any advice, help or sample-code will be welcome.
I think the best way to do it would be like you said, render everything into a low-res texture (best done using FBOs) and then just display the texture by drawing a sceen-sized quad (of course using GL_NEAREST as magnification filter for the texture). Maybe you can also use glBlitFramebuffer for copying directly from the low-res FBO into the high-res framebuffer, although I don't know if you can copy directly into the default framebuffer (the displayed one) this way.
EDIT: After looking up the specification for framebuffer_blit it seems you can just copy from the low-res FBO into the high-res default framebuffer using glBlitFramebuffer(EXT/ARB). This might be faster than using a texture mapped quad as it completely bypasses the vertex-fragment-pipeline (although this would have been a simple one). And another advantage is that you also get the low-res depth and stencil buffers if needed and can this way render high-res content on top of the low-res background which might be an interesting effect. So it would happen somehow like this:
generate FBO with low-res renderbuffers for color and depth (and stencil)
...
glBindFramebuffer(GL_FRAMEBUFFER, lowFBO);
render_scene();
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, 640, 480, 0, 0, 1024, 768,
GL_COLOR_BUFFER_BIT [| GL_DEPTH_BUFFER_BIT], GL_NEAREST);

how to use glCopyImage2D

I'm trying something like
glEnable(texture_2d)
glBindTexture
glCopyTexImage2D
glDisable(GL_TEXTURE_2D);
I think glCopyTexImage2D won't work with a non-power of two image, so that's one problem; I've also tried glReadPixels, but it's too slow for my purposes.
If glReadPixels is too slow for you, then glCopyTexImage2D and glCopyTexSubImage2D aren’t going to be a whole lot faster. On platforms with support for framebuffer objects, like iOS, the recommended (i.e. faster) way to get GPU-rendered image data into a texture is to use that texture as the color attachment for a framebuffer object and render directly into it. That said, if you still want to pursue this method, here’s what you need to do to fix it:
First, you’re passing bad arguments to glCopyTexImage2D. The third argument, internalformat, should probably be GL_RGBA instead of 0. If you had called glGetError after calling glCopyTexImage2D, you would probably have gotten GL_INVALID_OPERATION. See the OpenGL ES 1.1 man pages for glCopyTexImage2D and glCopyTexSubImage2D.
Second, as you’ve already observed, glCopyTexImage2D requires its width and height arguments to be power-of-two as well. The correct way to deal with this is to allocate a texture image using glTexImage2D (you can pass NULL for pixels here), then use glCopyTexSubImage2D to copy your framebuffer contents into a rectangle. Note that glCopyTexSubImage2D doesn’t take an internalformat argument—because it’s updating a subrectangle of a texture, it uses the texture’s existing format.
For the record, glGetTexImage doesn’t exist in OpenGL ES 1.1 or 2.0, which is why you’re getting an implicit declaration.
You can check if the video card supports non-power of 2 textures if it supports the ARB_texture_non_power_of_two extension. See here for info.
glCopyTexImage2D does work with NPOT image.
NPT image (non-power of two) is limited supported by OpenGLES 2/OpenGL 1 or WebGL, In OpenGLES 3/OpenGL 2 or later it is fully supported.
If you want to copy color attachment of fbo to newTexture.
glBindFramebuffer(GL_FRAMEBUFFER, fbo);
glReadBuffer(GL_COLOR_ATTACHMENT0);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, newTexture);
glTexImage2D(bindTarget, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glCopyTexSubImage2D(target, level, 0, 0, 0, 0, width, height);
NPT image will output black color in fragment shader sampling if texture mipmap, magnification filter and repeat mode setting is wrong.
To help figure out if the "non-power of two" thing is a problem, use glGetError() like this:
printf("error: %#06x\n", glGetError());
Put that in different places in your code to make sure what line is causing the problem, then check the error code here: https://www.khronos.org/opengl/wiki/OpenGL_Error
To copy a texture I did:
glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, 0, 0, TEXTURE_WIDTH, TEXTURE_HEIGHT, 0);
glGenerateMipmap(GL_TEXTURE_2D);
after binding the texture. Check the docs on those two functions for more info.