Stretched background textures

Godot Version

4.4.1

Question

How do I get rid of this weird stretching present on the texture as it’s drawn?

I put in a tilemap to serve as a general-purpose background (mainly for the lighter VRAM usage), but I noticed the dither pattern is stretched to where it’s not uniform.

Here’s what the background looks like as a tilemap:

For comparison, here’s the same background as a sprite, for what the background should look like:

If anyone’s interested, here’s the shader code used for the tilemap:

shader_type canvas_item;

// The texture used by the shader.
uniform sampler2D fadeLevel: repeat_enable, filter_nearest;
// Controls which part of the texture is displayed.
uniform vec2 fadeAtlas;
// Centers UV coordinates based on number of subtextures.
uniform vec2 fadeAtlasUV;

void fragment() {
	vec4 baseTexture = texture(fadeLevel, vec2(fadeAtlas.x + (UV.x * fadeAtlasUV.x), fadeAtlas.y + (UV.y * fadeAtlasUV.y)));
	COLOR = baseTexture;
}

Is that sprite a single sprite? With or without repeat texture?

My guess would be the whole sprite is mapping from 0..1 for its UVs, while each tile is 0..1 for the tilemap, so the tiles are “zoomed way out” and you’re seeing the resulting roundoff distortion.

The sprite is a single sprite, with texture repeat disabled as inherited from root.

Ok, so the UVs on that will be from 0,0 in one corner to 1,1 in the diagonally opposite corner.

Your tiles are going to be 0,0 to 1,1 corner to corner in each tile.

I suspect you’d see the same thing on your sprite if you made it the size of one of your tiles.

[…]if you made [the sprite] the size of one of your tiles.

What do you mean by that?

Your sprite was screen-sized, right? And your tiles are presumably 64x64 or something like that?

Try making the sprite 64x64.

edit: I think your shader isn’t dealing well with the scale you’re trying to render at.

Yes, the sprite is screen-sized (640x400 with 10 rows). Tiles are 16x16.

The image below has the sprite resized to the tilemap’s cell dimensions, and…


…it produces a different effect.

Right, but the point is, your shader isn’t handling that scale well. It’s the shader you need to fix.

How would I fix the shader?

The core of this is taking the UV for the given fragment and using that to sample from the fadeLevel texture, restricting it to a subsection by adding an offset (fadeAtlas) and scaling the UV down (* fadeAtlasUV).

First of all, filter_nearest on the texture is going to give you noise like this when it’s pulling a small number of fragments out of a larger image. “nearest” in this sense means “round off to the nearest texel center”, so if the UV winds up dead in the middle between a black texel and a white texel it’ll pick one of them rather than giving you grey. Enabling mipmaps would probably help a lot too.

Second, I’m assuming the source atlas texture for this is fairly large? You might try making a source texture whose atlas region is closer to the size you’re actually drawing.

Though, third, if this is a tilemap background, unless you’re planning on scaling this or something you can just bake the image you want into a tile and fill the tilemap with that; a shader is kind of overkill here unless you’re planning on doing something funky with animated textures or something.

First of all, filter_nearest on the texture is going to give you noise like this when it’s pulling a small number of fragments out of a larger image. “nearest” in this sense means “round off to the nearest texel center”, so if the UV winds up dead in the middle between a black texel and a white texel it’ll pick one of them rather than giving you grey. Enabling mipmaps would probably help a lot too.

Yeah, that makes a lot of sense; although removing filter_nearest results in a different (albeit relatively minor) problem, due to the paletteSwap shader used:

Second, I’m assuming the source atlas texture for this is fairly large? You might try making a source texture whose atlas region is closer to the size you’re actually drawing.

The texture used for the shader is about one tile wide and ten tiles tall:
genericTile
I guess it is quite tall for a 16x16 tile…

Though, third, if this is a tilemap background, unless you’re planning on scaling this or something you can just bake the image you want into a tile and fill the tilemap with that; a shader is kind of overkill here unless you’re planning on doing something funky with animated textures or something.

I suppose I could either A.) update the texture used in the shader or B.) fill the tilemap with the appropriate tiles.

I tried updating the texture used in the shader, and found that the problem is the texture is far too small for the tiles to be drawn like the sprite. This will need to be baked into the tileset itself.

That seems like the best answer.

I’d imagine you could fix the “too small” problem by tweaking fadeAtlasUV, but honestly I think it makes more sense to just bake a tile.

I would like to try one thing before I bake the fade levels into the tileset, though…


The thin lines are where the tiles meet, so if I could make those as thin as possible and make the pattern appear seamless like in the sprite, then it should be good to go. I may need to swap out the texture that’s currently being used to aid with this.

thanks for this i need to know about this

I swapped the texture used to one that has a red/green bounding box on it, and it turns out…


…that the textures, as drawn by the shader, don’t quite fit into the tiles.

That explains why the textures distort the way they do, at least…

I modified my shader to allow for UV manipulation:

shader_type canvas_item;

// The texture used by the shader.
uniform sampler2D fadeLevel: repeat_enable, filter_nearest;
// Controls which part of the texture is displayed.
uniform vec2 fadeAtlas;
// Centers UV coordinates based on number of subtextures.
uniform vec2 fadeAtlasUV;
// Shifts UVs by a certain amount.
uniform vec2 UVOffset;
// Scales UVs by a certain amount.
uniform float UVScale;

void fragment() {
	vec4 baseTexture = texture(fadeLevel, vec2((UVOffset.x * fadeAtlasUV.x) + fadeAtlas.x + (UV.x * fadeAtlasUV.x * UVScale), (UVOffset.y * fadeAtlasUV.y) + fadeAtlas.y + (UV.y * fadeAtlasUV.y * UVScale)));
	COLOR = baseTexture;
}

Then after toying with UVOffset and UVScale on the CPU side for a bit, I landed with the respective values of (−0.0625,−0.0625) and 1.125, leaving me with this:

1 Like

I’m glad that I got this resolved, but with how the texture goes past the cell boundaries, I can’t help but feel the solution I came up with is esoteric.

It makes me wonder if there’s a way to align the texture with the cell boundaries…