Sign in to follow this  
billconan

OpenGL scaling causes artifacts

Recommended Posts

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:


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



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


[img]http://dl.dropbox.com/u/3315090/prob1.png[/img]



this is when scale=1.5


[img]http://dl.dropbox.com/u/3315090/prob2.png[/img]



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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
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

Share this post


Link to post
Share on other sites
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.

Share this post


Link to post
Share on other sites
[quote name='assainator' timestamp='1298103668' post='4776245']
You should avoid using glScale.
[/quote]
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)
[b]T[/b]( 0.5, 0.5 ) * [b]S[/b]( s, s ) * [b]v[/b]
where [b]v[/b] is the vertex position in pixel units using whole numbers, s is a small whole-numbered scaling factor and [b]S[/b] its scaling matrix, and [b]T[/b] a translation matrix made from its 0.5 valued arguments. With [b]v[/b] and [b]S[/b] having numbers close to integer values, also [b]S[/b] * [b]v[/b] will do so. Hence adding 0.5 will result in numbers with fractional parts close to 0.5.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this