Overv / WebCraft

Minecraft clone written in Javascript.
zlib License
387 stars 186 forks source link

Rendering bug: Texture bleeding #39

Open cymno opened 6 years ago

cymno commented 6 years ago

25 (gif in reply 2), #26, likely #38, and possibly even #4

all reference the same rendering bug. (@ficopang @Klem3n @Jim-JZ-Chen @TeamTallos)

Description Because of the bug, block edges, are rendered in the wrong color or transparently. This means that blocks near the camera have a 1px outline, and in the distance the ground is very discolored in red or blue depending on the direction. Here are some pictures

Cause The bug is caused by an incorrect texturing of the faces. The neighbouring squares to the grass block in the texture atlas "terrain.png", lava and the transparent background of a sapling and a flower, are used on the edges. This causes the blue (sky shining through) and red discolourings. It is also possible to see the different colors of the lava texture, from orange to red, on the 1px red line when looking closely. Because of the constant effect width, the bug gets worse farther away from the camera.

When the bug occurs I get the bug on both multiplayer and singleplayer, on my slightly modified copy as well as a fresh clone from this repository, and on the multiplayer server mentioned in #25, on linux as well as on a virtual windows machine, on edge, on firefox, and on chromium browsers. I'm using a gtx1060 here. The same bug can be seen in several images from other people, see the links at the top of this post.

However, on my 10 year old laptop (also linux) with intel graphics I do not get the bug. In firefox the framerate is so low that the camera is pretty much uncontrollable, the player clips through blocks and the ground, and can jump several blocks high. In chromium it is almost playable, but in both browsers the bug is not visible.

The interesting thing to note here is that the laptop does not support WebGl 2 (you can check your support here). (Feedback on whether your machine supports it and if you get the bug would be appreciated!)

Now WebCraft doesn't use WebGL 2, and changing the "webgl-experimental" context (basically webgl 1, and necessary for Microsoft's browsers) to "webgl2" doesn't change anything about the bug. But I think it's possible that some error in the programming leads to undefined behaviour, which by hardware capable of supporting WebGL 2 is interpreted differently than by one only able to support WebGL 1. The bug was likely part of the application since the beginning, and didn't surface at first due to older hardware.

Possible mitigations Since the bug is dependent on the texture atlas, a switch to a 2d texture array would likely fix it. But this would require WebGL 2, which would mean less support for users (e.g. old hardware, Edge/Internet Explorer, Safari).

As a test, I made a spot in the texture atlas with 9 times the grass texture in a square and set that as block-top texture. This meant the bug went away on short range, but when looking into the farthest side of the map, there was still a noticeable discolouration in the texture atlas average colour.

The bug might have something to do with mipmaps, when they get incorrectly used far away the texture is so small it basically smears to 1 pixel with an averaged colour. But I thought we turned mipmaps off here? And it also does not explain the bug on short range. Is this maybe a separate bug, maybe a rounding or half-pixel error? I am too much of a beginner in OpenGL to understand this.

Or maybe it is a limitation of the hardware. But it doesn't make sense for 10 year old integrated graphics to work whereas a current gaming card doesn't. Maybe some variable is handled as a lower precision float when not explicitly specified?

frankarendpoth commented 4 years ago

Looks like the renderer is sampling pixels from outside the source rectangle you use to cut the individual textures out of the source image. I think you can modify your fragment shader to fix this or perhaps somehow tell the sampler to edge clamp to the source rectangle. By default it probably clamps to the boundaries of the source image.