Reputation: 428
I am using SDL2 to create a context for OpenGL. I use SDL_image to load the images, and I bind them to OpenGL textures. But because the coordinate system isn't the same the textures are flipped.
I found two ways to correct this:
Modify the texture after loading it
Advantage: Only done once per texture
Disadvantage: Done using the CPU which slows down the loading of each texture
Apply a rotation of 180° on the Y and Z axis when rendering
Advantage: Using super fast functions
Disadvantage: Needs to be done multiple times per frame
Is there another way to flip back the textures after they have been loaded with SDL_Image? And if not, which method is usually used?
Upvotes: 1
Views: 3402
Reputation: 1746
I would disagree with most of the previous answer's point, except for flipping the image either on load, or before first use.
The reason being that if you are following data driven software development practices, you should never allow code to dictate the nature of data. The software should be designed to support the data accurately. Anything else is not fit for purpose.
Modifying texture coordinates is hackery, despite it's ease of use. What happens if you decide at some later stage, to use a different image library which doesn't flip the image? Now your image will be inverted again during rendering.
Instead, deal with the problem at the source, and flip the image during load or before first use (I advocate on load, as it can be integrated into the code that loads the image via SDL_Image, and therefore is more easily maintained).
To flip an image, I'll post some simple pseudo code which will illustrate how to do it:
function flip_image( char* bytes, int width, int height, int bytes_per_pixel):
char buffer[bytes_per_pixel*width]
for ( i = 0 -> height/2 ) loop
offset = bytes + bytes_per_pixel*width * i
copy row (offset -> offset + bytes_per_pixel*width) -> buffer
offset2 bytes + bytes_per_pixel * height * width;
copy row (offset2 -> offset2 + bytes_per_pixel*width) -> (offset -> offset + bytes_per_pixel*width)
copy row(buffer -> buffer + width * bytes_per_pixel ) -> offset
end loop
Here is a visual illustration of one iteration of this code loop:
However, this will only work on images which have an even number of rows, which is fine as opengl doesn't like texture with non-power of two dimensions.
It should also be noted that if the image loaded does not have power of two width, SDL_Image pads it. Therefore, the "width" passed to the function should be the pitch of the image, not it's width.
Upvotes: 2
Reputation: 54572
There are a bunch of options. Some that come to mind:
You can flip the image files upside down with an image processing tool, and use the flipped images as your assets. They will look upside down when viewed in an image viewer, but will then turn out correct when used as textures.
This is the ideal solution if you're in full control of the images. It obviously won't work if you get images from external sources at runtime.
Some image loading libraries allow you to flip the image during loading. From the documentation of SOIL_image I could find, I did not see this option there. But you might be able to find an alternate library that supports it. And of course you can do this if you write your own image loading.
This is a good solution. The overhead is minimal sice you do the flipping while you're touching the data anyway. One common approach is that you read the data row by row, and store in the texture in the opposite order, using glTexSubImage2D()
.
You can create a flipped copy of the texture after you already loaded it. The typical way to do this would be by drawing a screen sized quad while sampling the original texture and rendering to an FBO that has the resulting flipped texture as a rendering target. Or, more elegant, use glBlitFramebuffer()
.
This is not very appealing because it involves copying the memory. While it should be quite efficient if you let the GPU create the copy, extra copying is always undesirable. Even if it happens only once for each texture, it can increase your startup/loading time.
You can apply a transformation to the texture coordinates in either the vertex or fragment shader. You're talking about rotations in your question, but the transformation you need is in fact trivial. You basically just map the y
of the texture coordinate to 1.0 - y
, and leave the x
unchanged.
This adds a small price to shader execution. But the operation is very simple and fast compared to the texture sampling operation it goes along with. In reality, the added overhead is probably insignificant. While I don't think it's very pretty, it's a perfectly fine solution.
This is similar to the previous option, but instead of inverting the texture coordinates in the shader, you already specify them inverted in the vertex attribute data.
This is often trivial to do. For example, it is very common to texture quads by using texture coordinates of (0, 0)
, (1, 0)
, (0, 1)
, (1, 1)
for the 4 corners. Instead, you simply replace 0
with 1
and 1
with 0
in the second components of the texture coordinates.
Or say you load a model containing texture coordinates from a file. You simply replace each y
in the texture coordinates by 1.0f - y
during reading, and before storing away the texture coordinates for later rendering.
IMHO, this is often the best solution. It's very simple to do, and has basically no performance penalty.
Upvotes: 2