15

This is just a quick question before I dive deeper into converting my current rendering system to openGL. I heard that textures needed to be in base 2 sizes in order to be stored for rendering. Is this true?

My application is very tight on memory, but most of the bitmaps are not powers of two. Does storing non-base 2 textures consume more memory?

Kleptine
  • 5,089
  • 16
  • 58
  • 81

4 Answers4

10

It's true depending on the OpenGL ES version, OpenGL ES 1.0/1.1 have the power of two restriction. OpenGL ES 2.0 doesn't have the limitation, but it restrict the wrap modes for non power of two textures.

Creating bigger textures to match POT dimensions does waste texture memory.

Dr. Snoopy
  • 55,122
  • 7
  • 121
  • 140
  • So there's absolutely no way to render a non power of two sized bitmap with openGL without wasting memory? That's annoying. Oh well, glad I didn't start overhauling my code yet. – Kleptine Sep 18 '10 at 04:27
  • By the way, you should know that OpenGL ES and OpenGL doesn't have the same limitations, OpenGL 2.0 and beyond supports NPOT textures directly. – Dr. Snoopy Sep 18 '10 at 16:09
  • What are the wrap mode restricts for OpenGL ES 2.0 when using non power of two textures ? – dirhem Sep 07 '11 at 14:28
  • 3
    @dirhem No mipmaps and only GL_CLAMP_TO_EDGE is supported as a wrap mode. See the notes in http://www.khronos.org/opengles/sdk/docs/man/xhtml/glTexParameter.xml – Dr. Snoopy Sep 07 '11 at 15:23
6

Suresh, the power of 2 limitation was built into OpenGL back in the (very) early days of computer graphics (before affordable hardware acceleration), and it was done for performance reasons. Low-level rendering code gets a decent performance boost when it can be hard-coded for power-of-two textures. Even in modern GPU's, POT textures are faster than NPOT textures, but the speed difference is much smaller than it used to be (though it may still be noticeable on many ES devices).

GuyNoir, what you should do is build a texture atlas. I just solved this problem myself this past weekend for my own Android game. I created a class called TextureAtlas, and its constructor calls glTexImage2D() to create a large texture of any size I choose (passing null for the pixel values). Then I can call add(id, bitmap), which calls glTexSubImage2D(), repeatedly to pack in the smaller images. The TextureAtlas class tracks the used and free space within the larger texture and the rectangles each bitmap is stored in. Then the rendering code can call get(id) to get the rectangle for an image within the atlas (which it can then convert to texture coordinates).

Side note #1: Choosing the best way to pack in various texture sizes is NOT a trivial task. I chose to start with simple logic in the TextureAtlas class (think typewriter + carriage return + line feed) and make sure I load the images in the best order to take advantage of that logic. In my case, that was to start with the smallest square-ish images and work my way up to the medium square-ish images. Then I load any short+wide images, force a CR+LF, and then load any tall+skinny images. I load the largest square-ish images last.

Side note #2: If you need multiple texture atlases, try to group images inside each that will be rendered together to minimize the number of times you need to switch textures (which can kill performance). For example, in my Android game I put all the static game board elements into one atlas and all the frames of various animation effects in a second atlas. That way I can bind atlas #1 and draw everything on the game board, then I can bind atlas #2 and draw all the special effects on top of it. Two texture selects per frame is very efficient.

Side note #3: If you need repeating/mirroring textures, they need to go into their own textures, and you need to scale them (not add black pixels to fill in the edges).

Sean O'Neil
  • 61
  • 1
  • 1
  • Yeah, I considered something similar. The only thing is that there's still some small amounts of wasted memory, and in a program where I'm constantly pushing the 24mb bounds, I need all the memory I can get. – Kleptine Dec 09 '10 at 15:36
3

No, it must be a 2base. However, you can get around this by adding black bars to the top and/or bottom of your image, then using the texture coordinates array to restrict where the texture will be mapped from your image. For example, lets say you have a 13 x 16 pixel texture. You can add 3 pixels of black to the right side then do the following:

static const GLfloat texCoords[] = {
        0.0, 0.0,
        0.0, 13.0/16.0,
        1.0, 0.0,
        1.0, 13.0/16.0
    };

Now, you have a 2base image file, but a non-2base texture. Just make sure you use linear scaling :)

Beaker
  • 1,633
  • 13
  • 22
  • The problem here is that it wastes texture memory; something that I am very pressed for with my application. – Kleptine Oct 01 '10 at 04:05
2

This is a bit late but Non-power of 2 textures are supported under OpenGL ES 1/2 through extensions.

The main one is GL_OES_texture_npot. There is also GL_IMG_texture_npot and GL_APPLE_texture_2D_limited_npot for iOS devices

Check for these extensions by calling glGetString(GL_EXTENSIONS) and searching for the extension you need.

I would also advise keeping your textures to sizes that are multiples of 4 as some hardware stretches textures if not.

Danny Parker
  • 1,713
  • 18
  • 30