## Texture Matrix

Apprentice
Posts: 7
Joined: 2008.08
Post: #1
I'm new to OpenGL and I'm thoroughly confused by the texture matrix.

I'd like to use 'sprite sheets' in my game, where more than one sprite is stored in a power-of-two texture and then displayed using its x/y offsets. It's easiest for the designer to specify the sprite's offsets in the sprite sheet using pixel values, so I'm trying to transform the texture matrix so I can simply use these values as my texture coordinates.

I think I almost have it working. I have a 256x256 texture with four 128x128 sprites:

Code:
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glOrtho(0, 256, 0, 256, -1, 1);
glMatrixMode(GL_MODELVIEW);

GLint coordinates[] = { 0, 0,
128, 0,
0, 128,
128, 128 };

GLint vertices[] = { x, y, 0,
x+128, y, 0,
x, y+128, 0,
x+128, y+128, 0};

glVertexPointer(3, GL_INT, 0, vertices);
glTexCoordPointer(2, GL_INT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

glMatrixMode(GL_TEXTURE);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);

Instead of getting the top left sprite (the one at 0, 0) I get the entire texture scaled into the triangle strip! But if I change the texture coordinates to 0,64, etc. (i.e., scale all values by 0.5) I get the expected behavior. So I added a glScalef(0.5f, 0.5f, 1.0f) after glOrtho and everything works as expected, but this can't be right, can it?

Best,

Art

iDrum for iPhone
Sage
Posts: 1,199
Joined: 2004.10
Post: #2
Unless you're using a RECT texture, your coordinates are incorrect. In GL TEXTURE_2D coords are expressed as normalized floats. You probably want something like:

Code:
GLfloat coordinates[] = { 0, 0,
0.5, 0,
0, 0.5,
0.5, 0.5 };
Apprentice
Posts: 7
Joined: 2008.08
Post: #3
TomorrowPlusX Wrote:Unless you're using a RECT texture, your coordinates are incorrect. In GL TEXTURE_2D coords are expressed as normalized floats. You probably want something like:

Code:
GLfloat coordinates[] = { 0, 0,
0.5, 0,
0, 0.5,
0.5, 0.5 };

I understand that's how they're normally expressed, but I'm hoping to use the texture matrix to express them as something other than 0.0f-1.0f. Are you saying this is not possible?
Luminary
Posts: 5,143
Joined: 2002.04
Post: #4
Why glOrtho rather than glScalef?
Moderator
Posts: 3,584
Joined: 2003.06
Post: #5
Seconding on the "why glOrtho" and also that you should probably be using GL_TEXTURE_2D.

If you already understand how they're normally expressed, then you should also know that using 0 to 1 for tex coords has nothing to do with using pixel dimensions or texture matrix transforms. Moving 128 pixels over in a 256 pixel wide texture is 128/256 == 0.5f. That's trivial.

You don't need to scale, just translate over to where you want (again with something like 128/256, or whatever) if you have your texCoords already set up (as you do)  whoops, I mean as TomorrowPlusX showed [/edit].

P.S. You'll have to use GL_FLOAT for your tex coord pointer. Also might want to glLoadIdentity right after you've switched into matrix mode and after the glPushMatrix(), so you can be absolutely certain you have a blank transform slate to work with. You don't have to load identity every time, but just keep it in mind in case things aren't working as expected.
Apprentice
Posts: 7
Joined: 2008.08
Post: #6

Thanks, that was exactly it.
Apprentice
Posts: 7
Joined: 2008.08
Post: #7
AnotherJake Wrote:P.S. You'll have to use GL_FLOAT for your tex coord pointer.

From glTexCoordPointer:

Code:
type       Specifies the data type of each texture coordinate.
Symbolic constants GL_SHORT,    GL_INT,    GL_FLOAT, or
GL_DOUBLE are accepted. The initial value is
GL_FLOAT.
Moderator
Posts: 3,584
Joined: 2003.06
Post: #8
Right. Which one is most appropriate between 0.0f and 1.0f ?
Luminary
Posts: 5,143
Joined: 2002.04
Post: #9
He's not passing coordinates between 0 and 1... he's scaled out to pixels.

That said, not using GL_FLOAT for that data is likely to shove you hard off whatever fast paths exist.
Moderator
Posts: 3,584
Joined: 2003.06
Post: #10
OneSadCookie Wrote:He's not passing coordinates between 0 and 1... he's scaled out to pixels.

Yes, I read his code snippet. I was suggesting using normalized tex coords, and if he does so, he will need to use GL_FLOAT. I wasn't *that* subtle was I?

 ... thinking to myself: "hmm... I guess I must've been too subtle about it because they *both* missed it. must make effort to be clearer next time I guess. or something."
Apprentice
Posts: 7
Joined: 2008.08
Post: #11
OneSadCookie Wrote:He's not passing coordinates between 0 and 1... he's scaled out to pixels.

That said, not using GL_FLOAT for that data is likely to shove you hard off whatever fast paths exist.

As I pointed out, I'm fairly new to OpenGL and game programming, so I really appreciate all the help. Clearly I didn't fully understand glOrtho before posting here. I'm bound to ask more stupid questions as I get my bearings, though I really do try to RTFM before I do.

As for my choice to use GLuint (actually GLshort--my posted code was an iteration) In my case, I'm targeting an OpenGL ES hardware platform (PowerVR MBX) that specifically recommends against floats for vertices when possible, citing memory bandwidth constraints. Furthermore, for my project, any client-side x_coord/256.f type calculations to normalize would be happening in software since the target platform doesn't have an fpu.

The pixel scaling is just a convenience thing, I can take it or leave it. It was just clear in trying to implement it that I didn't understand something (glOrtho, texture transforms), so I thought I'd chance joining this community and see if someone could help.

Thanks!

Art
Moderator
Posts: 3,584
Joined: 2003.06
Post: #12
We're glad you joined us. You're doing fine with your questions, don't worry about that. A little confusion popped up because of the tiny little details in the approaches with OpenGL. It's normal, don't worry about that.

If it's the same platform I'm using OpenGL ES with, then I've found that you really don't need to freak out too much about float calcs for texture coordinates with sprites. I'm passing thousands and thousands of floats around every frame and getting away with it no problem. Vertex shorts are great, but I'm also passing floats and it seems to be swallowing those without much problem too. And further, I've followed and tested all of the recommendations for the particular platform I'm talking about and many of them have little to no practical gain for the amount of effort put into implementing them. That's just my tested take on it, YMMV

Keeping memory bandwidth usage low by passing shorts instead of floats or ints will help you on just about any OpenGL platform though, and that's true here too. How much it helps depends on the situation of course. In my experience, for sprites, eh, prolly not gonna help much to optimize prematurely, but again, it's up to you how crazy you wanna get I suppose. I mean, you'll already be screwing up performance pretty bad by having to use blending (assuming you're using any transparency with your sprites, as I do), so why worry too much about a little float?

Here's an example which better illustrates the point of view that I might take with a texture translate. You can certainly do it however works best for you. This is just what I'm saying you can do with a single translate instead of scale and translate. But scaling is fine too -- whatever works for you.

Code:
GLfloat coordinates[] = { 0, 0,
0.5, 0,
0, 0.5,
0.5, 0.5 };
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glTranslatef(spriteSheetX * 0.00390625, spriteSheetY * 0.00390625, 0.0); // 0.00390625 is 1.0 / 256.0
glPopMatrix();
glMatrixMode(GL_MODELVIEW);

GLshort vertices[] = { x, y,
x+128, y,
x, y+128,
x+128, y+128};

glVertexPointer(2, GL_SHORT, 0, vertices);
glTexCoordPointer(2, GL_FLOAT, 0, coordinates);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

 It is worth noting that if you're *really* worried about performance, you probably shouldn't be using transforms for your texture coordinates in the first place. Instead, it would make sense to pre-calculate your texture coordinates for each sprite location on your sprite sheet (or atlas, or whatever you'd like to call it) so you can pass those directly, without incurring the extra overhead of a matrix transform and associated calls. I pre-calc my tex coords, but I don't see anything terribly impractical about doing texture transforms either. Profiling your code is the best way to find out which way is best for you.
Apprentice
Posts: 7
Joined: 2008.08
Post: #13
AnotherJake Wrote:If it's the same platform I'm using OpenGL ES with, then I've found that you really don't need to freak out too much about float calcs for texture coordinates with sprites. I'm passing thousands and thousands of floats around every frame and getting away with it no problem. Vertex shorts are great, but I'm also passing floats and it seems to be swallowing those without much problem too. And further, I've followed and tested all of the recommendations for the particular platform I'm talking about and many of them have little to no practical gain for the amount of effort put into implementing them. That's just my tested take on it, YMMV

Great stuff, thanks for sharing your experience.

Quote: It is worth noting that if you're *really* worried about performance, you probably shouldn't be using transforms for your texture coordinates in the first place. Instead, it would make sense to pre-calculate your texture coordinates for each sprite location on your sprite sheet (or atlas, or whatever you'd like to call it) so you can pass those directly, without incurring the extra overhead of a matrix transform and associated calls. I pre-calc my tex coords, but I don't see anything terribly impractical about doing texture transforms either. Profiling your code is the best way to find out which way is best for you.

Excellent advice... pre-calculating each sprite's coords on texture load is the right thing to do, especially now that I know using floats isn't the end of the world.
Sage
Posts: 1,234
Joined: 2002.10
Post: #14
Some good tips in this thread, but also a bit of misinformation.

In general:
1) hardware natively processes some element types, like FLOAT, or SHORT. Some hardware can not accept certain types, and will require a (slow) conversion.
2) smaller data = less bandwidth = faster.

Given that the OpenGL API does not expose the natively supported types (all types must be supported), the only way you can tell if a type (or combination of types, when multiple attributes are enabled) is "fast" is to benchmark it yourself. If you suspect vertex submission is a bottleneck for your app, you should write a benchmark for it and experiment with different types.

For those of you developing for the iPhone, I encourage you to read the guidelines in the documentation. In this case, 2) generally outweighs 1).
Member
Posts: 87
Joined: 2006.08
Post: #15
artgillespie Wrote:Furthermore, for my project, any client-side x_coord/256.f type calculations to normalize would be happening in software since the target platform doesn't have an fpu.

The target platform does have a FPU. Use it.

artgillespie Wrote:The pixel scaling is just a convenience thing, I can take it or leave it. It was just clear in trying to implement it that I didn't understand something (glOrtho, texture transforms), so I thought I'd chance joining this community and see if someone could help.

The results of the matrix multiply should be the particular texture subset you are interested in, in the normalized coordinate space (ie, for the lower left quadrant of your texture: 0,0 to 0.5,0.5). The inputs can be whatever you want, as long as they transform to the right place. What you are trying to do is completely possible. You'll probably only need to use glTranslatef and glScalef to prepare the texture matrix appropriately in this case.

This kind of trick is really preferable if you have large arrays of texture coordinates: you no longer need to ever modify the bulk data, so you can place it in a STATIC_DRAW VBO. This can be a big help on some architectures. For the numbers of vertices you are dealing with in a sprite engine, it probably doesn't matter (but it definitely doesn't hurt to know how to set it up!).