Kim
Kim

Reputation: 33

GLSL Fragment Shader to intelligently tile texture

I am creating a game where I have platforms of different sizes - but always in multiplications of 8 pixels and always 8 pixels high.

All platforms use the same texture like the one here:

Tiling texture image with description

The texture is divided up into three 8x8 sections. I want to be able to "tile" this texture via the fragment shader so that the first 8 pixels are applied only at the left end - the middle 8 pixels tiles for as many pixels as the width of the geometry minus the last 8 pixels where the "end" tile is rendered. Something like this geometry which is 32 pixels wide and 8 pixels high:

Tiled platform

The framework I use does not allow setting the vertices and UVs (just a simple textured quad) so I cannot fix this in the geometry.

I get the following uniforms in the fragment shader:

.. and this varying:

It is possible to add other uniforms via code.

Can someone point me in the direction of how to create the fragment shader?

Upvotes: 2

Views: 2730

Answers (2)

Odney
Odney

Reputation: 865

I tried to solve your problem so I wrote a simple fragment shader. It is probably not the cleanest solution so you can try to optimize it.

The first thing you need is to calculate the number of tiles in the object (only vertically):

float verticalTiles = u_sprite_size.x / 8.0;

After that you have to calculate the tile you are currently processing and the UV for this tile. To do this you multiply the uv coordinates with the amount of vertical tiles and floor the value because you want only whole number. Then you also need to fract the value to get uv coordinates:

float tile = floor(verticalTiles * v_tex_coord.x);
float tileUV = fract(verticalTiles  * v_tex_coord.x); // [0; 1] for each tile

Now you want to apply the first 8 pixels of the texture only to the first tile and last 8 pixels to the last tile. Other tiles would have the middle 8 pixels. It can be sampled using a simple if statement. We are only calculating the U value because V won't change anyway:

float resultU; 

if(tile == 0.0){ 
    // If this is first tile, then we want the UV coordinates of the first 8 pixels.
    // We divide tileUV by 3 to get UV coordinates [0; 0.3333].
    resultU = tileUV / 3.0;

}else if(tile == verticalTiles - 1.0){
    // If this is last tile we do the same thing but add two thirds
    // to the coordinates to get last 8 pixels [0.6666; 1]
    resultU = tileUV / 3.0 + (2.0 / 3.0);

}else{
    // Else we want the middle 8 pixels [0.3333; 0.6666]
    resultU = tileUV / 3.0 + (1.0 / 3.0);
}

Then use this value to sample from the texture:

gl_FragColor = texture2D( u_texture, vec2(resultU, v_tex_coord.y));

Upvotes: 1

Joseph Rosson
Joseph Rosson

Reputation: 354

As you probably already know, the typical method for achieving this is to send UV coordinates along with the vertices. data. But Just to be safe, I'll reiterate. In order for texture mapping to occur texture coordinates need to be passed from the vertex shader, into the fragment shader for interpolation. Since you cannot supply the properly mapped UV coordinates, you will need to calculate them on the fly in the fragment shader which is convoluted and error prone unless done correctly. That said, consider that normalized UV coordinates range from 0-1 so that your 8x8 tile is actual {0-1}x{0-1}. For random geometry, you really need to know the width, height and world offset to successfully auto generate the UV's (without distortion).

On another hand, and if possible, you're best bet is to use object instancing so that you draw one square tile of any equal width and height so that you have four known points that you can hard code to map to UV [0-1]x[0-1]. Them just instance the same tile in lines using Model matrix offsets. Basically it's cloning the one known square geometry, and tiling the same object as much as possible. Consider all types of square (left, middle , and right), as a separate instance collection. Then the new problem will be in generating model matrices that offset the tiles in lines one after another.

This is also a very efficient way of generating a large world with relatively few triangles passed to the GPU.

Upvotes: 0

Related Questions