scaling causes artifacts

Started by
4 comments, last by haegarr 13 years, 2 months ago
Hello guys,

i'm working on an opengl based tile game. the game map is formed by many tiles.
The game looks fine, but somebody thought the tiles are too small.

i therefore want to enlarge the game map simply by applying a scaling matrix before drawing these tiles.

but then i started to see cracks between tiles.

this is how i draw the tiles:



[font=monospace]glMatrixMode(GL_MODELVIEW);glPushMatrix();glScalef(scale,scale,1.0);map.draw();glPopMatrix();[/font]




this is how it looks like without scaling (scale==1.0)


prob1.png



this is when scale=1.5


prob2.png



as you can see between the tiles, there are cracks. cracks don't always happen, it happens when the tile's coordinates has odd number.

for example, if the tile's coordinates are (3,3), you will see the problem, but if the coordinates are (4,6) you won't see the cracks.

i first thought this problem may be a driver issue. but i have tried on both ubuntu and mac os, same problem.

what's wrong?

Thanks.
Advertisement
another thing is that those cracks are all horizontal. between two vertically continues tiles. crack doesn't happen between two horizontally adjacent tiles.

quad: 121,117,40,40

quad: 121,157,40,40




these are the coordinates, width and height of two adjacent tiles. i can't see any problem with these numbers, given that they renders perfectly without scaling.

i guess this has something todo with the opengl rasterization method.

if the position coordinate is, say, 31.5,

the opengl rasterization method may round it to 31 or 32.
You should avoid using glScale.




Try to modify the vertex positions, thus resizing the mesh yourself.

There are two good reasons to do so:

- glScale is used every frame meaning your code is resized every frame (not very efficient)
- glScale messes up the normals of a mesh. (Alright, you're not using normals (yet) so this argument might be useless)





assainator

"What? It disintegrated. By definition, it cannot be fixed." - Gru - Dispicable me

"Dude, the world is only limited by your imagination" - Me

In OpenGL a vertex position x in view (!) gets mapped onto pixel position y, if y < x <= y+1. The center of a pixel is hit if frac(x) == 0.5.

A problem arises at pixel borders where frac(x) must be 0! If, after the transformation (inclusive the scaling) the frac(x) lies just a hundredth above 0, then the next pixel will be used. For meshes, especially 3D meshes, the problem isn't apparent because (1) the vertices are shared, and (2) who cares whether the mesh is shifted by 1 pixel in its entirety.

Hence, for pixel perfect drawing, ensure that the vertex positions in view have a fraction close to 0.5. For example: Assume a face that ranges (in horizontal direction) from l=1.5 to r=21.5. The l gets mapped to pixel column 1, and r gets mapped to pixel column 21. Notice that this covers all pixel columns between 1 and 21 inclusive (!), what means 21 pixels. Assume that we would draw a face just to the right of the former one, i.e. it starts at21.5. This means that OpenGL would draw the pixel at column 21 once with the former face and once with the latter face. That would be a problem (flickering in dependence of the rendering order) and waste of performance. Hence OpenGL suppresses rendering of the rightmost pixel in each line and the bottommost pixel in each row when rendering faces. Hence the actual amount of pixels drawn from the former face ranges from 1 to 20, making 20 pixels.


BTW: Texels have a similar issue: If you want to map pixel-perfect texture mapping, ensure that the texel positions (when multiplied with the count of pixels in that dimension) in use will have a fractional part of 0.5, too. Doing so hits the center of the texel and hence suppresses filtering or nearest neighbour mismatch.

You should avoid using glScale.

The artifact will probably go away when translating the tile by the magic amount of 0.5 pixel units. E.g. (using column vectors as is typical for OpenGL)
T( 0.5, 0.5 ) * S( s, s ) * v
where v is the vertex position in pixel units using whole numbers, s is a small whole-numbered scaling factor and S its scaling matrix, and T a translation matrix made from its 0.5 valued arguments. With v and S having numbers close to integer values, also S * v will do so. Hence adding 0.5 will result in numbers with fractional parts close to 0.5.

This topic is closed to new replies.

Advertisement