HiveBrain v1.2.0
Get Started
← Back to all entries
patterncMinor

2D OpenGL tile atlas fragment shader

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
shaderatlasopenglfragmenttile

Problem

I am currently using the following glsl code to retrieve separate textures from a tile atlas:

#version 330 core
in vec2 TexCoord;
out vec4 color;

uniform sampler2D image;

void main()
{
    //width and height in tiles of atlas
    int width = 8;
    int height = 8;
    //x and y position of a tile in the atlas
    int x = 1;
    int y = 4;

    float scalarX = 1.0 / width;
    float scalarY = 1.0 / height;

    color = vec4(1.0) * texture(image, vec2((TexCoord.x + x) * scalarX,  (TexCoord.y * scalarY) + y * scalarY));
}


The tile atlas is made up of: 88 sprites each with dimensions of: 1616 pixels

Is this the best method to use when carrying out this task and could my code be improved / made more efficient?

Solution

Why don't you simply set the texture coordinates properly and have a "pass-thru" shader that just samples the texture and nothing else? For example, if you're using glVertexAttribPointer() with an array of vertices and texture coords, make sure that the texture coordinates you send have the proper tile offset. Something like this:

const int numXTiles = 8;
const int numYTiles = 8;
const int numXPixelsPerTile = 16;
const int numYPixelsPerTile = 16;
const int atlasWidth = numXTiles * numXPixelsPerTile;
const int atlasHeight = numYTiles * numYPixelsPerTile;
const double pixelXDelta = 1.0 / atlasWidth;
const double pixelYDelta = 1.0 / atlasHeight;
typedef struct vertex {
    Point3D position;
    Point2D texCoord;
} vertex;
// Assume you're drawing a square and want to texture it with tile 3,4
vertex square[] = {
    { {-1.0, -1.0, 0.0 }, { 3.0 * numXPixelsPerTile * pixelXDelta, 4.0 * numYPixelsPerTile * pixelYDelta } },
    { { 1.0, -1.0, 0.0 }, { 4.0 * numXPixelsPerTile * pixelXDelta, 4.0 * numYPixelPerTile * pixelYDelta } },
    { { 1.0, 1.0, 0.0 }, { 4.0 * numXPixelsPerTile * pixelXDelta, 5.0 * numYPixelPerTile * pixelYDelta } },
    { { -1.0, 1.0, 0.0 }, { 3.0 * numXPixelsPerTile * pixelXDelta, 5.0 * numYPixelPerTile * pixelYDelta } }
};
glBufferData(GL_ARRAY_BUFFER, 4, square, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, sizeof(vertex), 0); // Position
glVertexAttribPointer(1, 2, GL_FLOAT, sizeof(vertex), (GLvoid*)sizeof(Point3D)); // Texture Coordinate


This assumes your vertex shader has attribute 0 as position and attribute 1 as texture coordinates. Then your fragment shader just becomes:

#version 330 core
in vec2 TexCoord;
out vec4 color;

uniform sampler2D image;

void main()
{
    color = texture(image, TexCoord);
}


NOTE: I may be off by 1 on the right and top side. You might need to subtract 1 pixelXDelta and 1 pixelYDelta from those coordinates - I can never remember.

Code Snippets

const int numXTiles = 8;
const int numYTiles = 8;
const int numXPixelsPerTile = 16;
const int numYPixelsPerTile = 16;
const int atlasWidth = numXTiles * numXPixelsPerTile;
const int atlasHeight = numYTiles * numYPixelsPerTile;
const double pixelXDelta = 1.0 / atlasWidth;
const double pixelYDelta = 1.0 / atlasHeight;
typedef struct vertex {
    Point3D position;
    Point2D texCoord;
} vertex;
// Assume you're drawing a square and want to texture it with tile 3,4
vertex square[] = {
    { {-1.0, -1.0, 0.0 }, { 3.0 * numXPixelsPerTile * pixelXDelta, 4.0 * numYPixelsPerTile * pixelYDelta } },
    { { 1.0, -1.0, 0.0 }, { 4.0 * numXPixelsPerTile * pixelXDelta, 4.0 * numYPixelPerTile * pixelYDelta } },
    { { 1.0, 1.0, 0.0 }, { 4.0 * numXPixelsPerTile * pixelXDelta, 5.0 * numYPixelPerTile * pixelYDelta } },
    { { -1.0, 1.0, 0.0 }, { 3.0 * numXPixelsPerTile * pixelXDelta, 5.0 * numYPixelPerTile * pixelYDelta } }
};
glBufferData(GL_ARRAY_BUFFER, 4, square, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, sizeof(vertex), 0); // Position
glVertexAttribPointer(1, 2, GL_FLOAT, sizeof(vertex), (GLvoid*)sizeof(Point3D)); // Texture Coordinate
#version 330 core
in vec2 TexCoord;
out vec4 color;

uniform sampler2D image;

void main()
{
    color = texture(image, TexCoord);
}

Context

StackExchange Code Review Q#92156, answer score: 6

Revisions (0)

No revisions yet.