Skip to content

78. Shaders – tiling images

June 6, 2013

This is about a fantastic shader that helps you tile images across large surfaces.

As I have pointed out in my series of 3D posts, it can be very difficult to tile a texture on a mesh (ie repeat the image as many times as needed), especially if the mesh isn’t a simple rectangle. The problem is that the normal texture mapping only goes from 0 to 1, so if we need to use the same image 10 times to cover our mesh, we will need to define 10 separate rectangles, one for each copy of the image.

This shader (by spacemonkey) makes a clever one line change to the standard fragment shader to solve the problem. If you do much mesh work, this is extremely useful, so I recommend understanding it.

An example probably explains it best, and I’ll explain it carefully, because it took me some time to understand how it works.

Suppose you have a rectangular area 1000 x 600 pixels wide, and an image 80 x 40 pixels wide. We need to repeat it 12.5 times along the x axis (1000/80) and 15 times along the y axis (600/40). That means we would need 12 x 15 full size rectangles and 15 half size rectangles.

However, think about this. If I am at point x=425, y=510, which point in my image texture do I need to draw there?

x axis: 425 / 80 = 5.312
so at point 425, we have already drawn 5 complete copies of the image, and we are 0.312 through the sixth. So we need to draw the part of the image at x=0.312.

y axis: 510/40 = 12.75
so at point 510, we have already drawn 12 complete copies of the image, and we are 0.75 through the 13th. So we need to draw the part of the image at x=0.75.

These x and y values are just the fractional part of :

pixel position / image width (or height)

So if the fragment shader somehow knew what these values were, it could draw the correct part of the image texture at each pixel, without needing dozens of different rectangles.

In Codea

So – suppose that you treat the whole area as just one (yes, that’s 1) rectangle, with two triangles and six vertices as normal.

When you define the texture mappings – you know, the (0,0), (0,1), (1,1) etc – you make one change. Instead of 1, you put this:

for x: area width/image width
for y: area height/image height

so in our example above, they would be

x = 1000/80 = 12.5
y = 600/40 = 15
and if our texture mappings would look like this

(0,0) would stay (0,0)
(1,0) would become (12.5,0)
(0,1) would become (0,15)
(1,1) would become (12.5,15)

In the fragment shader

Now let’s think about happens in the shaders when we do this, using the example of the pixel we used above, x=425, y=510.

Note we are only worried about the texture position, ie what part of the image will be used to colour the pixel.

The vertex shader will interpolate the texture position and pass the fraction to the fragment shader. The vertex shader knows that x runs from 0 to 1000, and it pro rates the texture position for pixel x=425 like this

x texture position = (currentX – startX) * (maxTexture – minTexture) + minTexture

= (425 – 0) x (12.5 – 0) + 0 = 5.312

Similarly, the y texture position will be 12.75

These are exactly the same answers we got above for this position. And the vertex shader will pass these values through to the fragment shader in the variable vTexCoord.

Now, I’m not sure what the fragment shader would do if we didn’t change anything, because it expects to receive values in the range 0-1. So we need to make a small change.

Remember we figured out that the texture position we want to use is the fractional part of the numbers above.

So what the fragment shader does is take mod 1 of the Codea results, which leaves us with the fraction of the image at the current pixel position, and that is the texture position we want to use.

lowp vec4 col = texture2D( texture, vec2(mod(vTexCoord.x,1.0), mod(vTexCoord.y,1.0))) * vColor;

This one line change is very insightful and elegant.

The code here demonstrates how no matter what the size, the shader tiles the image perfectly.

Tiling terrain

Where this shader is absolutely essential is in tiling complex meshes like terrain. The problem there is that we can’t just define it as one big rectangle, because it is bumpy. We need to define a whole lot of triangles/rectangles at different heights, eg every 20 pixels or so.

So suppose we have terrain of 500×500 pixels, split into squares of 20×20, with different height values for all the vertices (which is why we need so many squares). Suppose also that we want to tile the terrain with a grass image of 133×133 pixels, which has been designed to tile seamlessly, ie if you lay the image next to itself you can’t see a join. But fitting it to 20×20 squares is just a nightmare, because I don’t want the image to start from 0,0 in each of those squares. I want to tile the whole image, but it will be spread over several squares, and finish in the middle of a square, making normal texture coordinates impossible.

The tiling shader makes things much easier. All you do is set the texture mappings equal to (x value / image width, z value / image height), for each vertex.

So if we have a vertex where x=228, and z=56, then your texture mapping (using the figures from my example above) would be (228/133,56/133), and the tile shader will take care of the rest, tiling your image over the whole terrain without worrying about all the little squares.

I can’t think of any other way to tile an image over terrain, so this shader is invaluable. Credit again to spacemonkey.


From → Shaders

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: