4

Shortly: I need a quick way to resize the buffer image and then return the pixels to me to save them to file etc.

Currently I first use glReadPixels(), and then I go through the pixels myself to resize them with my own resize function.

Is there any way to speed up this resizing, for example make OpenGL do the work for me? I think I could use glGetTexImage() with miplevel and mipmapping enabled, but as I noticed earlier, that function is bugged on my GFX card, so I can't use it.

I only need one miplevel, which could be anything from 1 to 4, but not all of them, to conserve some GPU memory. So is it possible to generate only one miplevel of wanted size?

Note: i dont think i can use multisampling, because i need pixel precise rendering for stencil tests, so if i rendered it with multisampling, it would make blurry pixels and they would fail with stencil test and masking and result would be incorrect (AFAIK). Edit: i only want to scale the colors (RGBA) buffer!

Rookie
  • 4,064
  • 6
  • 54
  • 86

2 Answers2

3

If you have OpenGL 3.0 or alternatively EXT_framebuffer_blit available (very likely -- all nVidia cards since around 2005, all ATI cards since around 2008 have it, and even Intel HD graphics claims to support it), then you can glBlitFramebuffer[EXT] into a smaller framebuffer (with a respectively smaller rectangle) and have the graphics card do the work.
Note that you cannot ever safely rescale inside the same frambuffer even if you were to say "I don't need the original", because overlapped blits are undefined (allowed, but undefined).

Or, you can of course just draw a fullscreen quad with a simple downscaling pixel shader (aniso decimation, if you want).

In fact, since you mention stencil in your last paragraph... if it is stencil (or depth) that you want to rescale, then you most definitively want to draw a fullscreen quad with a shader, because it will very likely not give the desired result otherwise. Usually, one will choose a max filter rather than interpolation in such a case (e.g. what reasonable, meaningful result could interpolating a stencil value of 0 and a value of 10 give -- something else is needed, such as "any nonzero" or "max value in sample area").

fospathi
  • 537
  • 1
  • 6
  • 7
Damon
  • 67,688
  • 20
  • 135
  • 185
  • i dont know what you mean by rescaling stencil. basically i just want that after i am done with my rendering, i just call "glReadPixelsResized" function to get for example 25% of the original image size resized smoothly. it doesnt matter what that image contains or how it was made; i just want all the pixel data downscaled correctly. i dont want to read stencil buffer, just the RGBA colors. – Rookie Oct 12 '12 at 12:30
  • OK perfect, then `glBlitFrameBuffer` will just do. I was confused about stencil because you wrote about multisampling and stencil in your last paragraph, so I was unsure what was the exact thing you wanted to do (e.g. downscale color _and_ stencil?). If it's just color you want, it's all good. – Damon Oct 12 '12 at 13:04
  • Does this actually resize it smoothly, as if it was multisampled ? To my experiences, GL_LINEAR does not resize an image in that manner, i would have to generate the mipmap*s* first? – Rookie Oct 12 '12 at 14:52
  • One should expect that an unweighted average of the values in the rectangle corresponding to the pixel in the downscaled image will be generated, because that is what linear filtering is. However the specification does not define what _exactly_ `GL_LINEAR` means here (it only defines it for texture minification where it is a 2x2 samples downscale). Insofar, an implementation might be cheap and do just the minimum. In any case, it will _not_ be the same as with MSAA, because samples in MSAA are typically rotated or jittered in a random way to hide artefacts. – Damon Oct 12 '12 at 15:36
  • In any case, _perfect_ downsampling is not at all trivial anyway, as you can read for example [here](http://number-none.com/product/Mipmapping,%20Part%201/index.html) ([part 2](http://number-none.com/product/Mipmapping,%20Part%202/index.html)). There are a multitude of techniques of differing complexity, and none of them is truly superior in every respect -- they all have advantages and disadvantages. – Damon Oct 12 '12 at 15:37
2

Create a framebuffer of the desired target size and draw your source image with a full-resized-buffer-sized textured quad. Then read the resized framebuffer contents using glReadPixels.

Psuedocode:

unbind_texture(OriginalSizeFBOattachmentColorTex);

glBindFramebuffer(OriginalSizeFBO);
render_picture();

glBindFramebuffer(TargetSizeFBO); // TargetSizeFBO used a Renderbuffer color attachment
glBindTexture(OriginalSizeFBOattachmentColorTex);
glViewport(TargetSize);
render_full_viewport_quad_with_texture();

glReadPixels(...);
datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • Does this actually resize it smoothly, as if it was multisampled ? To my experiences, GL_LINEAR does not resize an image in that manner, i would have to generate the mipmap*s* first? – Rookie Oct 12 '12 at 14:51
  • @Rookie: Indeed you must implement filtering in some way. Either using generated mipmaps. Or you could throw a shader at it that does minification. That would allow you to use some more advanced filtering than the simple bilinear filtering that GL_LINEAR_MIPMAP_LINEAR with mipmap generation would give you. – datenwolf Oct 12 '12 at 14:53
  • Could i somehow use perhaps `GL_GENERATE_MIPMAP` in a way that it would only generate a specific miplevel for me? How would this shader be made? I read x and y (+ 0 to 3) pixels, take average of them (by using mix() w*h times?) and then... write it to a texture somehow? I dont need any more advanced filtering, i just want to take average color of, lets say 4x4 pixels. – Rookie Oct 12 '12 at 15:10
  • @Rookie: You can use glGenerateMipmap (explicit call to this function), the mipmap levels being generated are determined from the GL_TEXTURE_BASE_LEVEL and GL_TEXTURE_MAX_LEVEL glTexParameter values. Regarding the shader, yes such a box filtering like you propose would work, but you don't write it to a texture but just emit it to gl_FragColor as you're rendering to a FBO and you're going to read back from that one. – datenwolf Oct 12 '12 at 15:42
  • Ok so if i use glGenerateMipmap(), i can just call it once, then use glReadPixels() to get the resized pixels? or do i need glGetTexImage() with the miplevel set in place? so i dont need a shader after all? – Rookie Oct 12 '12 at 17:08
  • @Rookie: You're not reading back from a texture. You generate mipmaps for the the source image texture. Then you draw this texture to another FBO's renderbuffer in the desired size (mipmap minification does the filtering) and read back using glReadPixels. – datenwolf Oct 12 '12 at 17:18
  • that sounds like a nasty way... why do i need to draw it again if its in the memory already? cant i just get the memory directly to myself? – Rookie Oct 13 '12 at 08:34
  • @Rookie: Because the mipmap probably is not in the very size you want to have. The available mipmap levels are "original image size"·2^(-n) in size. Well, and if you use a shader based filtering approach that drawing step is required to actually execute the shader. – datenwolf Oct 13 '12 at 09:54
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/17971/discussion-between-rookie-and-datenwolf) – Rookie Oct 13 '12 at 11:19