Quads have borders from neighbours (Tileset)

Questions about the LÖVE API, installing LÖVE and other support related questions go here.
Forum rules
Before you make a thread asking for help, read this.
Post Reply
MadShadow
Prole
Posts: 3
Joined: Sat Sep 07, 2019 7:32 pm

Quads have borders from neighbours (Tileset)

Post by MadShadow »

Hi,

I am trying to cut a tileset into its pieces. For some reason if I cut a tile, the colors from the tile next to it are mixed into my main tile, like if the pixels of the image got interpolated.
In the original tileset there is a clear border:
originalTileset.PNG
originalTileset.PNG (15.5 KiB) Viewed 6380 times
In Love2D however, the brown color seems to be mixed into the left border:
borders.PNG
borders.PNG (22.82 KiB) Viewed 6378 times
(the same quad placed next to each other two times)

I used the following commands to obtain the quad

Code: Select all

img = love.graphics.newImage("myImage.png")
quad = love.graphics.newQuad(x, y, tileWidth, tileHeight, xDimension, yDimension),
So how can I avoid this effect?
Would appreciate help, thanks in advance.
(I did some research on google and on the forum about "interpolation" and "tilesets" with the engine, but I didn't find anything helpful.)
Attachments
originalTileset.PNG
originalTileset.PNG (15.5 KiB) Viewed 6378 times
originalTileset.PNG
originalTileset.PNG (15.5 KiB) Viewed 6380 times
originalTileset.PNG
originalTileset.PNG (15.5 KiB) Viewed 6393 times
User avatar
Sasha264
Party member
Posts: 131
Joined: Mon Sep 08, 2014 7:57 am

Re: Quads have borders from neighbours (Tileset)

Post by Sasha264 »

Welcome! :awesome:

Here is a simple solution: set "nearest" filter for your texture https://love2d.org/wiki/FilterMode

Code: Select all

img:setFilter("nearest", "nearest")
Look at 2) option on the attached screenshot.
If it is just what you want, then no need to read further :3

But if you absolutely need :death: linear interpolation then things becames a lot more complicated, because linear interpolation needs data outside the quad that you specified. Here is multiple ways to provide that data:
  • Add one pixel rows and columns in graphic redactor outside the target quad. But it is painfull method.
  • Use wrap mode provided by love textures https://love2d.org/wiki/WrapMode. But that effects happens only at the edge of texture, not at the edge of quad, so you need to crop your image in redactor or in love "phisically" (which is not preffered, but I did for clarity)
Note, if you draw cropped images with linear filtering you will still have visible line between two drawed images. Look at 3) option. To avoid that you can draw all tiled area with one quad that extends image borders. Also it will minimize draw calls count as a bonus.

Code: Select all

function love.load()
	x = 9
	y = 0
	w = 40
	h = 33
	scale = 5
	love.window.setMode(w * scale * 2, (h * 4 + 3) * scale)

	-- 1)
	linear_img = love.graphics.newImage("myImage.png")
	linear_quad = love.graphics.newQuad(x, y, w, h, linear_img:getDimensions())

	-- 2)
	nearest_img = love.graphics.newImage("myImage.png")
	nearest_img:setFilter("nearest", "nearest")
	nearest_quad = love.graphics.newQuad(x, y, w, h, nearest_img:getDimensions())

	-- 3)
	local croppedData = love.image.newImageData(w, h)
	croppedData:paste(love.image.newImageData("myImage.png"), 0, 0, x, y, w, h)
	croppedImage = love.graphics.newImage(croppedData)

	-- 4)
	local croppedData2 = love.image.newImageData(w, h)
	croppedData2:paste(love.image.newImageData("myImage.png"), 0, 0, x, y, w, h)
	croppedImage2 = love.graphics.newImage(croppedData2)
	croppedImage2:setWrap("repeat")
	extendedQuad = love.graphics.newQuad(0, 0, w * 2, h, croppedImage2:getDimensions())
end

function love.draw()
	-- 1)
	love.graphics.draw(linear_img, linear_quad, 0, 0, 0, scale, scale)
	love.graphics.draw(linear_img, linear_quad, scale * w, 0, 0, scale, scale)

	-- 2)
	love.graphics.draw(nearest_img, nearest_quad, 0, (h + 1) * scale, 0, scale, scale)
	love.graphics.draw(nearest_img, nearest_quad, scale * w, (h + 1) * scale, 0, scale, scale)

	-- 3)
	love.graphics.draw(croppedImage, 0, (h + 1) * scale * 2 , 0, scale, scale)
	love.graphics.draw(croppedImage, scale * w, (h + 1) * scale * 2, 0, scale, scale)

	-- 4)
	love.graphics.draw(croppedImage2, extendedQuad, 0, (h + 1) * scale * 3, 0, scale, scale)
end
Attachments
tiled-textures.png
tiled-textures.png (384.3 KiB) Viewed 6366 times
MadShadow
Prole
Posts: 3
Joined: Sat Sep 07, 2019 7:32 pm

Re: Quads have borders from neighbours (Tileset)

Post by MadShadow »

Hello Sasha,

thank you so much for you warm welcoming and this detailed answer. Solutions 3) and 4) seem to deliver the result I would like to obtain, since I want to keep the linear interpolation.
Sasha264 wrote:[...] you need to crop your image in redactor or in love "phisically" (which is not preffered, but I did for clarity)
The tileset has a lot of tiles, hence it would be an incredible amount of work to manually crop every tile. You say doing it with the loeve2D engine is not preferred - why is that and can I just do it anyways?

My goal later is to crop a lot of tiles (let's say 200+) and then display them - would the crop method lead to performance issues?
And if so, I have not much Idea of 2D games, but aren't tilesets a common use and shouldn't there be a good working solution for this?
I am asking because I am wondering if I do things the right way or if I miss out on something important.
User avatar
Sasha264
Party member
Posts: 131
Joined: Mon Sep 08, 2014 7:57 am

Re: Quads have borders from neighbours (Tileset)

Post by Sasha264 »

You say doing it with the loeve2D engine is not preferred - why is that and can I just do it anyways?
I can think about 3 reasons here:
  • Double VRAM consumption: cropped area will be stored in separate texture, and it will be stored with others in common texture (sheet).
  • The simple code in my example loads all sheet (image that contains many tiles) then crops it and stores only one tile, and drops other loaded information. So, if for example, sheet contains 100 tiles and you want to crop every tile then you must load entire sheet 100 times, which will be a long operation. So it affects loading time. This can be avoided if you store loaded sheets in some "cache" during loading process, but this requires some amount of work.
  • Third is a general advice: not to use single tiles when you can use sheets + quads. Because it can be drawn with single sprite batch https://love2d.org/wiki/SpriteBatch But if you don't use sprite batches and draw calls inside one frame not exceed lets say ... 1000, then this should not be a problem.
And if so, I have not much Idea of 2D games, but aren't tilesets a common use and shouldn't there be a good working solution for this?
I am asking because I am wondering if I do things the right way or if I miss out on something important.
You are absolutely right. Tilesets (or texture atlas https://en.wikipedia.org/wiki/Texture_atlas) (or sprite sheets) is a good and common tool for 2D (and 3D) games. For example when you draw characters with animation contains a lot of frames. And in many many other cases. But in case when 1) you need linear interpolation 2) and you need "tiled tiles", not just "regular sprites" 3) and you already have a lot of tilesheets with no space around really tiled tiles — here comes described problem.
Last edited by Sasha264 on Mon Sep 09, 2019 11:20 am, edited 3 times in total.
User avatar
Sasha264
Party member
Posts: 131
Joined: Mon Sep 08, 2014 7:57 am

Re: Quads have borders from neighbours (Tileset)

Post by Sasha264 »

Hmmm...
Googled and found this: https://gamedev.stackexchange.com/quest ... ture-atlas
It is not about Love2d, but the problem is common for computer graphics at all =)
They suggests to move quads coordinates to the center of the pixels, not to the borders.
Not a perfect solution for me (because as far as I can see it leads to the variant-3-like smaller artifacts from my screenshot), but it can work... Some kind of.

And another one solution described in that link: use 3d textures for tiles. That fits good if you have many tiled tiles with the same size. Lucky in Love2d v11.x we have 3d textures https://love2d.org/wiki/love.graphics.newArrayImage :awesome:
Love2d Wiki wrote:...It can be thought of similarly to a texture atlas or sprite sheet, but it doesn't suffer from the same tile / quad bleeding artifacts that texture atlases do – although every sub-image must have the same dimensions.
Looks like perfect solution, but if 1) tiled tiles have the same size, or can be grouped in several groups with same sizes 2) you are ready to re-prepare tilesets to this format.
MadShadow
Prole
Posts: 3
Joined: Sat Sep 07, 2019 7:32 pm

Re: Quads have borders from neighbours (Tileset)

Post by MadShadow »

Nice thanks for your research and help. So Texture Bleeding is the phenomenon I tried to describe. I'll update if I get a working solution. Will experimente with the sprites now also.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Quads have borders from neighbours (Tileset)

Post by raidho36 »

Texutre bleeding is a side effect of the way 3D GPUs work; it's fundamentally unsolvable in a clean way.

Your options are:
* Rendering at integer coordinates, so texture coordinates would never end up out of tile bounds. Only non-transformed rendering is allowed, using any sort of scaling, rotating or skewing will immediately break sharp edges.
* Not using texture smoothing, so GPU will never try to sample pixels outside of exact edges of the tile. Tiles will look pixellated under transformations.
* Padding all tiles with 1 row of pixels of duplicate tile edge colors. Pixels should be duplicated from the opposite edge of the texture if it's meant to be tiled repeatedly rather than stand as a single tile. Tiles will always look properly. Requires marginal amount of extra effort. Most texture atlas generators have a simple checkbox to create 1-pixel paddings - because texture bleeding effect exists.

Note that using a single tile per texture has the same visual effect as padding all tiles by 1 pixel: when GPU tries to sample outside of texture, it will use the same color as was on the edge of the texture (on the opposite edge if texture repeat is enabled). This however comes at a very severe performance penalty, as pretty much every single tile requires its own GPU draw call, which are in short supply. Drawing half a screen worth of tiles this way can easily bottleneck a top-end system.
User avatar
zorg
Party member
Posts: 3436
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Quads have borders from neighbours (Tileset)

Post by zorg »

@raidho36
Or they can just use array images that don't suffer from bleeding artifacts, as was stated above, with the only downside of all tiles needing to be the same size. :v
Me and my stuff :3True Neutral Aspirant. Why, yes, i do indeed enjoy sarcastically correcting others when they make the most blatant of spelling mistakes. No bullying or trolling the innocent tho.
Post Reply

Who is online

Users browsing this forum: No registered users and 49 guests