Advertisement

Texture bleed when using tiling + mipmaps

Started by September 29, 2022 01:31 AM
6 comments, last by alikim 2 years, 1 month ago

I’m trying to tile a geometry with a patch from an atlas texture. It works perfectly w/o mipmaps but with mipmaps, even at a very close distance I see 1px bleed of what seems to be the average color of the whole texture.

I have substantial guards but it doesn’t help.

I thought this issue might occur at a large distance when small mipmaps might bleed due to averaging of pixels but it appears at a small distance where large mipmaps should be used and the bleed can’t be bigger than the guards.

I do it for WebGL / JavaScript, so I have an online live example with two buttons that switch textures with and w/o mipmaps to illustrate the issue:

https://jsfiddle.net/tfoller/x9botL7c/

Seems not related to filtering, but to UVs, maybe.

I managed to make the border go away with adding a clamp:

vec2 muv = beg + mod((vuv - beg) * uvmap[2].xy, uvmap[1].xy);
    muv = clamp(muv, vec2(0.6), vec2(0.75));

Maybe this would work if tweaking the values properly.
But it shouldn't be needed, and i don't see what's wrong with your math or which edge case could cause the problem. Rounding? One off? div by 0? idk.

It might help to add color bands to the background so you can see better what's going on.
Trying texture warping modes might also have an unexpected effect eventually.

Advertisement

If you clamp or restrict it to a very large degree it will stop eventually but it shouldn't be happening, that's what I don't get, a few pixels should be enough to fix this problem.

I tried to set different texture wrapping modes, they don't have any effect as you'd expect.

Colorized borders do not show up directly, this one pixel gap is filled with some weirdly averaged color, this is what happens if I add a blue region but only horizontally:

horizontal borders between tiles are filled with blue green vertical gradient, instead of flat green.

JoeJ said:
Rounding? One off? div by 0?

I don't see any reason for it to happen in this simple code.

I have no idea either, but when zooming in we see the problem always only is one pixel wide.

Maybe it is related to some hardware detail, like UV derivatives calculated per pixel quad. They would end up wrong due to discontinuous UV space near the tiling. And maybe the error propagates to mip map, while it does not effect the highest detail for some reason.

But this does not really make sense to me either. There surely is some guy somewhere who would instantly know what's going on…

JoeJ said:
There surely is some guy somewhere who would instantly know what's going on…

That's the hope ;-)

Found a post confirming my derivatives assumption. Maybe it helps: https://community.khronos.org/t/repeat-tile-from-texture-atlas/104500

Advertisement

@JoeJ Thanks, that sent me on quite a long derivative trip but finally I fond the solution. In terms of the approach mentioned in the kronos forum link, it looks like this:

uv = beg + size * fract(uv * tile);

vec2 smooth_uv = size * tile * vuv;
vec4 duv = vec4(dFdx(smooth_uv), dFdy(smooth_uv));

vec3 clr = textureGrad(map, uv, duv.xy, duv.zw).rgb;

I think the solution on that forum misses size passed to dF functions which forces lower mipmap level than it should be.

Here is a small testbed: https://jsfiddle.net/tfoller/v35z8taq/

This topic is closed to new replies.

Advertisement