3

Suppose you have a simple Tileset for a game, like this:

enter image description here

Now, when dealing with a simple GUI framework such as .NET, it would be rather easy to load that image, select portions of it, and then draw it tile-by-tile. However, when using OpenGL, this process seems a little more...erm, unique. I can easily load that image into OpenGL and bind it to some geometry, however, when it comes to "selecting" certain tiles(Without unbinding said texture) it seems to require a different kind of Maths than the traditional x,y,width,height approach.

If I wanted to select the Tile at 64,128(coordinates in pixel-space)for the current tile, I would use texture coordinates that mirrored that ideal instead of these weird fractions that I have seen people suggesting on other websites.

It seems that OpenGL does not use pixel-space at all when binding textures, or perhaps I am misunderstanding some basic concepts here; I am not sure.

Here is a simple way to render a 32 by 32 tile at some arbitary location(Will remain at 0,0 for this example):

            int spriteX = 0;
            int spriteY = 0;
            int spriteWidth = 32;
            int spriteHeight = 32;
            GL.BindTexture( TextureTarget.Texture2D , texture );
            GL.Begin( PrimitiveType.Quads );
            {
                GL.TexCoord2( 0 , 1 );
                GL.Vertex2( spriteX , spriteY );
                GL.TexCoord2( 1 , 1 );
                GL.Vertex2( spriteX + spriteWidth , spriteY );
                GL.TexCoord2( 1 , 0 );
                GL.Vertex2( spriteX + spriteWidth , spriteY + spriteHeight );
                GL.TexCoord2( 0 , 0 );
                GL.Vertex2( spriteX , spriteY + spriteHeight );
            }
            GL.End();

Before anyone complains about immediate mode: I am fully aware that it is deprecated; I do not plan on using it for any kind of finished product.

Instead of mapping the drawn quad with the entire texture(which the above code is doing) how would I instead, tell it to only map a region of the image, say: X 64, Y 128,Width 32, Height 32.

genpfault
  • 51,148
  • 11
  • 85
  • 139
Krythic
  • 4,184
  • 5
  • 26
  • 67

1 Answers1

6

The most direct approach is to use what you call "these weird fractions". They're not really that weird. They're just... fractions.

Say if your whole texture atlas is 1024x1024, and you want a texture with your specified dimensions (X 64, Y 128, Width 32, Height 32), the texture coordinates are:

left: X / 1024 = 64.0f / 1024.0f
right: (X + Width) / 1024 = 96.f / 1024.0f
top = Y / 1024 = 128.0f / 1024.0f
bottom = (Y + Height) / 1024 = 160.0f / 1024.f

An alternative is that you specify a transformation for the texture coordinates, which in this case would be a scaling transformation. With the fixed pipeline, it would look like this:

glMatrixMode(GL_TEXTURE);
glScalef(1.0f / 1024.0f, 1.0f / 1024.0f, 1.0f);
glMatrixMode(GL_MODELVIEW);

Then you can specify your texture coordinates using the pixel positions within the texture atlas.

With the programmable pipeline, the above is obsolete. But you can easily apply the same type of scaling by passing a uniform value to the shader, that you then multiply with the texture coordinates.

In the vertex shader, it could look like this:

uniform vec2 TexCoordScale;
in vec2 TexCoord;
out vec2 FragTexCoord;
...
    FragTexCoord = TexCoordScale * TexCoord;

Then in the fragment shader, you have the matching FragTexCoord in variable, and use it for your texture sampling operation.

In the client code, you set the uniform:

GLint texCoordScaleLoc = glGetUniformLocation(program, "TexCoordScale");
glUniform2f(texCoordScaleLoc, 1.0f / 1024.0f, 1.0f / 1024.0f);

and set up the vertex attribute for the texture coordinates just like you normally would, except that you can now use pixel coordinates for them instead of "weird fractions".

Reto Koradi
  • 53,228
  • 8
  • 93
  • 133
  • If I use the scaling transformation(Which works like a dream) will there be any noticeable performance penalties? – Krythic Jan 04 '15 at 04:58
  • I doubt that there will be a measurable difference. – Reto Koradi Jan 04 '15 at 05:16
  • 1
    @Krythic: You should just be aware that the texture matrix - as the rest of the matrix stack and the fixed function pipeline as a whole - is as deprecated as the immediate mode. However, porting this to a programmable shader is straight-forward - you wouldn't even need the overhead of a full-blown 4x4 matrix, a simple 2D vector multiplication is enough. – derhass Jan 04 '15 at 16:42
  • @derhass If you find time do you think you could write an example of this? I am very interested. – Krythic Jan 04 '15 at 17:39
  • @Krythic: Well, in what context? How familiar are you already with the programmable pipeline and GLSL shader programming? Adding this scale factor boilds down to add `uniform vec2 texScale;` and later when sampiling the texture apply it by simply multiplying the texcoords by that vector. On client side, you just set the uniform to `(1.0/width,1.0/height)`. With a reasonably modern GLSL version, you might also query the texture size directly in the shader, so that this can be done completely without uniforms. – derhass Jan 04 '15 at 17:48
  • @derhass That's pretty much what I was trying to say in the last paragraph. I guess I could have added more detail. – Reto Koradi Jan 04 '15 at 18:40
  • I added some more detail about how this looks with the programmable pipeline. – Reto Koradi Jan 04 '15 at 22:01
  • @derhass I know the basics of GLSL; I have not really explored the shader side of OpenGL due to the seriously immeasurable lack of tutorials on it. I learn best through "working" snippets. – Krythic Jan 04 '15 at 22:02