Skip to content
Tags

65. 3D Tiling images across an object for realistic effects

May 25, 2013

This is a really exciting and important post. It shows how to get images to draw on meshes without distortion, to get very realistic effects. This is not easy – unless you know how.

The problems

Suppose I draw a house wall, and I choose a brick image from the Internet. I face a number of problems in getting it to look right:

  • the bricks aren’t the right size for my wall
  • the image is smaller than my wall so I need to tile it
  • tiling is hard, and sometimes it produces fuzzy results
  • when I tile the image, the joins are visible, and it looks bad

.
Scaling

We can deal with the first problem, the size of the bricks, by scaling our image. This simply means applying a reduction factor to the image.

Suppose we experiment and find that if we reduce the size of our brick image by 2/3, the bricks look the right size. Another way of saying this is that we want our image to fit into a space 1/3 of the image size.

My 3D world provides a scaling factor for every image, allowing you to choose whether to shrink or expand your images. I’ll show you how I use it when we bring everything together, further below.

Tiling

Suppose you have a wall 100 pixels long and 30 pixels high.

You want to tile it with a brick image which is 230 pixels by 120 pixels, reduced by 90% in size ( ie it will only take up 10% of its full size, or 23 x 12 pixels).

Tiling with vertices
So to tile our wall, we need to repeat the image 4.34 times along the x axis (100 / 23), and 2.5 times along the y axis (30 / 12), creating triangles for each tile (and the fractional tile on the end). This can be done – and I have done it – but it is a bit tricky, involving loops and code to deal with the fractional tiles.

It gets much harder still if you have to tile a non rectangular area, such as the triangular ends of a house roof. Also, the more vertices you create, the slower Codea seems to run (to be fair, I’m talking about a lot of vertices for a large scene).

Tiling an image
I thought I had found a neat alternative, which was to create a monster image the size of my whole wall, and tile that image with my brick image (bonus – you don’t need to worry about the fractional parts, because Codea doesn’t mind if you draw past the end of an image). Then, using the monster image, I just needed two triangles for my whole wall.

However, the problem with this is that you can get quite fuzzy results, especially if you are shrinking your brick image down. The other problem is that Codea can’t handle images bigger than 2048 pixels wide or tall.

Tiling with shaders
Time for some black magic.

I got some help from spacemonkey who wrote a shader that solved the problem for me. The unfortunate thing about shaders is that you need a lot of knowledge to be able to understand and use them effectively. I certainly don’t have that knowledge, so I won’t be writing shader tutorials any time soon.

So I’m using the shader like a black box, and I’ll share it with you. What it does is to alter the way Codea tiles images.

if you use this special shader, then you can just split your wall (or whatever you are drawing) into just two triangles, and define your six vertices normally. You then need to tell Codea what part of the image applies at each vertex – with an important difference.

Normally, when you define a vertex, you tell Codea which part of the image applies at that point, as a fraction of 1. So if your vertex is at the bottom left hand corner of the image, that would be x,y values of 0,0, whereas if your vertex was at the top right of your image, that would be 1,1.

However, when you use this shader, instead of putting 1 to mean the right hand side, or top, of the image, you put N instead, where N is the number of tiles (ie repetitions of the brick image) you want to fit into the wall. The shader then arranges the tiling for you, and does a great job.

Let’s look at the example I used earlier. Suppose you have a wall 100 pixels long and 30 pixels high. You want to tile it with a brick image which is 230 pixels by 120 pixels, reduced by 90% ( ie it will only take up 10% of its full size, or 23 x 12 pixels).

So we need to repeat the image 4.34 times along the x axis (100 / 23), and 2.5 times along the y axis (30 / 12). I will define two variables to hold these values, like so

nx = wall width / (image width * scale) = 100/(2300.1) = 4.34
ny = wall height / (image height * scale) = 30/(120
0.1). = 2.5

where scale = 0.1, the image scaling factor

We define six vertices as follows – this is the same code I have been using in previous posts, except I use nx and ny instead of 1, for the texture mappings.

    v[#v+1]=vec3(x,y,z)  t[#t+1]=vec2(0,0)
    v[#v+1]=vec3(x+w,y,z-d)  t[#t+1]=vec2(nx,0)
    v[#v+1]=vec3(x+w,y+h,z-d)  t[#t+1]=vec2(nx,ny)
    v[#v+1]=vec3(x+w,y+h,z-d)  t[#t+1]=vec2(nx,ny)
    v[#v+1]=vec3(x,y+h,z)  t[#t+1]=vec2(0,ny)
    v[#v+1]=vec3(x,y,z)  t[#t+1]=vec2(0,0)

NB it may seem a little confusing that x,y, and z are changing in this code, but remember that only one of w or d will be non zero, depending on which way the wall is facing, so the nx applies to either the x or z axis (it works because the wall is the same length either way), and ny always applies to the height.

The final step is to tell the mesh to use the shader, which is a standard line of code that never changes.

    m.shader = shader(autoTilerShader.vertexShader, autoTilerShader.fragmentShader)

The shader itself is a few lines of code that I would keep in a separate tab.

Seamless tiling

The final problem I referred to initially, was that sometimes when you tile an image, you can see the joins too clearly, or perhaps it is just obvious that the same image is being repeated.

What you need to do is pick an image that is “seamless” (google for this), ie drawn so that no matter if you tile it up or down, all the edges fit together nicely.

The other thing to watch for is where your tiling image has an obvious mark or anything that stands out. If you tile it, the repetition of the mark will show up as an obvious pattern. Look for images which are relatively even in texture, so it’s not so obvious if you tile them over a surface.

Demo

You can see all this at work in the code below. Typically, I use a scaling factor of about 10% for most images like these, but see what you think.

Code is here.

Image folder is here:

From → 3D

8 Comments
  1. Dreamdancer permalink

    Hello, I am looking at your code and i miss the shaders. Where are they?

  2. Dreamdancer permalink

    It works-:) but how will the picture like if you do not use this shader. When do I need this shader that is not clear to me.
    (I think I have missed something-:))

    • Suppose you want to put grass on a big rectangle, and your grass image is much smaller than the rectangle. How will you do it? Have a think about it….

      • Dreamdancer permalink

        Oke i know now what tiling is 🙂
        I have seen that you use tiling shader in your 3d world. But sometimes i see white lines on the ground and blue and yellow lines at corners of buildings Has that something to do with the tiling shader. Is there something you can do about that. I have only played with your examples. When i have the time i like to use your functions to make a world of my own. I am now looking at your code to make it my own:-)

      • On a couple of the buildings, I have added a colour tint which means you can use a white plaster image for the walls, then add some transparent colour so you can create all sorts of extra colours. I may not have explained this. Search for AddTint, which is the function that does this.

        The problem with this is that I am adding another layer of wall, and I can’t put it in exactly the same place as the plaster wall, or OpenGL will start flickering. So I have to leave a little gap. The same goes for doors, windows etc, I have to leave a little gap. So when you get up close, you can see the gap.

        But I can’t really make the gap any smaller. If you walk up the hill and look down at the buildings, you will sometimes see the white plaster behind the colour tint.

        If you leave out the tint, you will have fewer problems like that.

  3. Dreamdancer permalink

    Ok I will keep this in mind when go to play with your 3d functions. an other question: the sky is now white is there a function or a special way to color the sky say blue.

    • look in the draw function for the background command, you can change the values in there to suit yourself. The colour values are all multiplied by the dayLight value (which is what makes everything go dark if you want it to).

Leave a Reply

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

WordPress.com Logo

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

Google photo

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

Twitter picture

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

Facebook photo

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

Connecting to %s

%d bloggers like this: