I'm trying to render a tile map world on a single sprite mesh (instead of one sprite per tile.) This significantly reduces the array vertex bus labor for extremely large worlds:
In order to do this, you pass two textures to the shader: the sprite sheet itself, and a texture containing tile IDs.
Sprite map (I know some tiles repeat but ignore that, this could be any spritesheet):
Below: Entire tile world represented by a 128 x 128 texture map, where each RGB value holds actual Tile ID for that location on the map. There are more efficient ways to represent the actual tile ID. Here, for the sake of simplicity, it is stored in the R component.
Putting it all together:
The right-most square is the screen output. On the image I just repeat texture... but what should actually happen is a tile world rendered from tiles on the sprite sheet.
I wrote the shader that is supposed to do this, but I'm stuck. I can render the sprite sheet itself, and even scale it on the sprite mesh, but I can't seem to get the tile IDs render at the proper location, matching the tile world data.
I do think I am really close and there are just a couple of modification needed to the shader -- so what am I doing wrong?
The following shader is what I have so far:
tilemap.vs
#version 330 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 color;
layout (location = 2) in vec2 texCoord;
out vec2 TexCoord;
uniform sampler2D ourTexture;
uniform sampler2D ourTilemapDataTexture;
uniform mat4 Model;
uniform mat4 View;
uniform mat4 Projection;
void main()
{
gl_Position = Projection * View * Model * vec4(position, 1.0);
TexCoord = texCoord;
}
tilemap.frag
#version 330 core
in vec2 TexCoord;
uniform sampler2D ourTexture;
uniform sampler2D ourTilemapDataTexture;
void main() {
// size of the tile world (128 by 128 tiles on one sprite)
float cols = 128.0f;
float rows = 128.0f;
float tilesPerSide = 8.0f;
// figure out which tile we´re in from the 3D coordinates
vec2 tile = vec2(floor(TexCoord.x)/cols, floor(TexCoord.y)/rows);
// get tile´s texture ID from tile ID texture map
float tex = texture2D(ourTilemapDataTexture, tile).r;
vec2 UV = vec2(tex + fract(TexCoord.x), fract(TexCoord.y));
color = texture2D(ourTexture, UV);
if (color.a == 0) // remove transparent areas
discard;
}
PS: I looked at every tutorial I could find on Google, and tried to re-implement it in my shader to no effect. This is as far as I got so far.
Can anyone point out what I'm doing wrong?
Update #1 -- I was able to get position sampling to work.
I painted a bit on the world map:
It's not perfect but definitely a step forward:
Adjustments made to the fragment shader:
float cols = 128.0f;
float rows = 128.0f;
float tex = texture2D(ourTilemapDataTexture, TexCoord).r;
float tey = texture2D(ourTilemapDataTexture, TexCoord).g;
vec2 UV = vec2( (TexCoord.x * (cols) / (tex * 32.0f)),
(TexCoord.y * (rows) / (tey * 32.0f)) );
color = texture2D(ourTexture, UV);
Sure it's not 100% correct but at least map indexing works now :)