openGL invert textures orientation during pixel-transfer?

umläute picture umläute · Jun 1, 2013 · Viewed 7.7k times · Source

as we all know, openGL uses a pixel-data orientation that has 0/0 at left/bottom, whereas the rest of the world (including virtually all image formats) uses left/top. this has been a source of endless worries (at least for me) for years, and i still have not been able to come up with a good solution.

in my application i want to support following image data as textures:

  1. image data from various image sources (including still-images, video-files and live-video)
  2. image data acquired via copying the framebuffer to main memory (glReadPixels)
  3. image data acquired via grabbing the framebuffer to texture (glCopyTexImage)

(case #1 delivers images with top-down orientation (in about 98% of the cases; for the sake of simplicity let's assume that all "external images" have top-down orientation); #2 and #3 have bottom-up orientation)

i want to be able to apply all of these textures onto various arbitrarily complex objects (e.g. 3D-models read from disk, that have texture coordinate information stored).

thus i want a single representation of the texture_coords of an object. when rendering the object, i do not want to be bothered with the orientation of the image source. (until now, i have always carried a topdown-flag alongside the texture id, that get's used when the texture coordinates are actually set. i want to get rid of this clumsy hack!

basically i see three ways to solve the problem.

  1. make sure all image data is in the "correct" (in openGL terms this is upside down) orientation, converting all the "incorrect" data, before passing it to openGL
  2. provide different texture-coordinates depending on the image-orientation (0..1 for bottom-up images, 1..0 for top-down images)
  3. flip the images on the gfx-card

in the olde times i've been doing #1, but it turned out to be too slow. we want to avoid the copy of the pixel-buffer at all cost.

so i've switched to #2 a couple of years ago, but it is way to complicated to maintain. i don't really understand why i should carry metadata of the original image around, once i transfered the image to the gfx-card and have a nice little abstract "texture"-object. i'm in the process of finally converting my code to VBOs, and would like to avoit having to update my texcoord arrays, just because i'm using an image of the same size but with different orientation!

which leaves #3, which i never managed to work for me (but i believe it must be quite simple). intuitively i though about using something like glPixelZoom(). this works great with glDrawPixels() (but who is using that in real life?), and afaik it should work with glReadPixels(). the latter is great as it allows me to at least force a reasonably fast homogenous pixel orientation (top-down) for all images in main memory.

however, it seems thatglPixelZoom() has no effect on data transfered via glTexImage2D, let alone glCopyTex2D(), so the textures generated from main-memory pixels will all be upside down (which i could live with, as this only means that i have to convert all incoming texcoords to top-down when loading them). now the remaining problem is, that i haven't found a way yet to copy a framebuffer to a texture (using glCopyTex(Sub)Image) that can be used with those top-down texcoords (that is: how to flip the image when using glCopyTexImage())

is there a solution for this simple problem? something that is fast, easy to maintain and runs on openGL-1.1 through 4.x?

ah, and ideally it would work with both power-of-two and non-power-of-two (or rectangle) textures. (as far as this is possible...)

Answer

Nicol Bolas picture Nicol Bolas · Jun 1, 2013

is there a solution for this simple problem? something that is fast, easy to maintain and runs on openGL-1.1 through 4.x?

No.

There is no method to change the orientation of pixel data at pixel upload time. There is no method to change the orientation of a texture in-situ. The only method for changing the orientation of a texture (besides downloading, flipping and re-uploading) is to use an upside-down framebuffer blit from a framebuffer containing a source texture to a framebuffer containing a destination texture. And glFramebufferBlit is not available on any hardware that's so old it doesn't support GL 2.x.

So you're going to have to do what everyone else does: flip your textures before uploading them. Or better yet, flip the textures on disk, then load them without flipping them.

However, if you really, really want to not flip data, you could simply have all of your shaders take a uniform that tells them whether or not to invert the Y of their texture coordinate data. Inversion shouldn't be anything more than a multiply/add operation. This could be done in the vertex shader to minimize processing time.

Or, if you're coding in the dark ages of fixed-function, you can apply a texture matrix that inverts the Y.