Stitching
I’m currently fighting vertex “stitching” in my game. Here’s what that looks like with just scaling, look for the flickering black lines between tiles.
This happens when either scaling or translating the camera (inversely, the grid of tiles) by fractional values.
Two “solutions” recommended around the web that I’ve already implemented:
- Pre-multiplied alpha
GL_NEAREST
min and mag filtering
I believe the cause of the issue is floating-point imprecision.
From https://stackoverflow.com/a/39960932:
OpenGL (and all other hardware rasterizers) only guarantees gapless rendering of the edge between two triangles if the edges exactly match. And that means you can’t just have one triangle next to the edge of another. The two triangles must have identical vertices on the shared edge between them.
The other thing you have to do is make certain that the two shared vertices between the two triangles are binary identical. The gl_Position output from the vertex shader needs to be the exact same value.
So where does the imprecision in my code creep in? I believe it’s this:
in vec2 aPosition; // either -0.5 or 0.5
in mat3 iTransform; // vertex transform, only translation in the video
uniform mat3 uView; // camera transform, only scaling in the video
vec3 pos = uView * iTransform * vec3(aPosition, 1.0);
gl_Position = vec4(pos.xy, 0.0, 1.0);
Ok so how can I fill in the gaps and still support fractional scaling?
The tiles in my game are stored in a texture atlas. If tiles rendered next to each other don’t share “binary identical” vertices, OpenGL is forced to sample outside of the tile’s UV coordinates within the atlas, reaching into the transparent space.
Let’s see what happens when the empty space in the tile atlas is filled with red.
Before | After |
---|---|
Great! So the gaps CAN be filled. I think the solution here is to bleed the textures.
Before | After |
---|---|
Completely fixed! …right?
Unfortunately, no. Walking far enough around the world reveals some additional, very infrequent stitching.
Because the lines are black and not red, I’m not sure that this is just fractional differences between vertices. I think the distance between some vertices is sometimes greater than 1, causing real gaps.
I know that if I clamp the camera offset to integer values, the gaps disappear. This introduces two problems: the map now has a minor snapping effect, and the player is now always fractionally offset from the map, causing the player to jitter. I can solve the player jitter by also rounding the player position.
I think for now I’m just going to accept the black line flickering. The background is dark enough that the lines will probably never be noticed.