Hey all,
When adjusting the FilterMode parameter (https://love2d.org/wiki/FilterMode), I can't get it the way I want. The "nearest" filter mode distorts the size of the pixels and the "linear" filter makes the image too blurry. What I'd really like is a linear filter that only interpolates the pixels on the border between the two colors, and doesn't interpolate all the pixels.
For example, imagine a 2x1 grayscale image with one white and one black pixel. It looks like [1,0]. If I try to interpolate that up to be 11 pixels wide, with the nearest filter, there would be a different number of white pixels and black pixels, six of one and five of the other. With the linear filter, there would be an equal number of black and white pixels, but there would also be 5 pixels in the middle at different stages of interpolation between white and black. What I'd like is a filter mode where there are 5 white pixels, 5 black pixels, and only the very middle pixel (on the border between black and white) is a gray value. This preserves the balance between the two colors while also not making the image unduly blurry.
Here are a couple of strategies I've been considering to get around this:
1. Store larger versions of the image. When scaling down, things aren't even close to as bad. Solves the problem perfectly but feels super cludgy. Lmao at storing a 640 by 640 version of a 64 by 64 piece of pixel art simply to get around this issue.
2. Manually scale the image up with the "nearest" filter to a multiple of the image's original size, draw it to a canvas, and then scale that canvas back down using the "linear" filter to reduce it to the size I want. Also feels cludgy, but probably going with this one. But then that's a bunch of boilerplate I have to call every time I load an image...
local scaleFactor = math.ceil(size / img:getHeight())
local canv = love.graphics.newCanvas(scaleFactor * img:getHeight(),
scaleFactor * img:getHeight())
love.graphics.setCanvas(canv)
img:setFilter("nearest")
love.graphics.setColor(1, 1, 1)
love.graphics.draw(img, 0, 0, 0, scaleFactor)
canv:setFilter("linear")
I'm wondering what veterans of LOVE2D would do here, if anything. Also, how easy would it be to submit a PR implementing a new filter that only interpolates on the border? Is that the kind of thing that would get merged, assuming that I wrote it properly?
FilterMode Bluriness and Workarounds
Forum rules
Before you make a thread asking for help, read this.
Before you make a thread asking for help, read this.
Re: FilterMode Bluriness and Workarounds
You can submit it as feature request in LÖVE's issue tracker.
- zorg
- Party member
- Posts: 3468
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: FilterMode Bluriness and Workarounds
There was a thread before that kinda had issues with blockyness and rotation, and there was a few solutions given in there, including a shader one... might be somewhat relevant here as well.
Me and my stuff True 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.
Re: FilterMode Bluriness and Workarounds
You can also upscale instead of downscale. If your zoom magnification factor is F, find floor(F) and upscale the image with the nearest filter using that factor (to a canvas, for example). Then you can scale the image to the target scale by applying a factor of F/floor(F) using the linear filter and you will get the desired effect.
For example, if your image is 64x64 and your zoom factor is 3.6, scale the image by a factor of floor(3.6) = 3 with nearest, obtaining a 192x192 image. Then upscale that with a factor of 3.6 / 3 = 1.2 with linear to obtain the final image.
I've run into this issue too and I thought about making a shader for it, but never got into that; I went with the scaling solution.
For example, if your image is 64x64 and your zoom factor is 3.6, scale the image by a factor of floor(3.6) = 3 with nearest, obtaining a 192x192 image. Then upscale that with a factor of 3.6 / 3 = 1.2 with linear to obtain the final image.
I've run into this issue too and I thought about making a shader for it, but never got into that; I went with the scaling solution.
- slime
- Solid Snayke
- Posts: 3166
- Joined: Mon Aug 23, 2010 6:45 am
- Location: Nova Scotia, Canada
- Contact:
Re: FilterMode Bluriness and Workarounds
This wouldn't go anywhere I think. The filter modes love currently exposes are part of GPU hardware itself, they're fixed-function and there aren't any more of them.Andlac028 wrote: ↑Wed May 03, 2023 2:20 pmYou can submit it as feature request in LÖVE's issue tracker.
Using a shader (or your other approaches if they work) in user code seems like a fine choice though.
Re: FilterMode Bluriness and Workarounds
I also did something like that in 2017 viewtopic.php?f=4&t=84579&p=216914
Nowadays I would do that with shader:
Code: Select all
local image
local shader
local time = 0
local scale = 10
local mult = 1.0 -- in case of pixel graphics
-- it can be meaningful to use mult value about 1.25 - 2.0
-- to make result less-blurry-more-pixelly
-- with 1.0 it's just basic bilinear interpolation
function love.load()
image = love.graphics.newImage("spaceship.png")
image:setFilter("nearest", "nearest")
local code = love.filesystem.read("shader.frag")
shader = love.graphics.newShader(code)
love.window.setMode(1200, 600, { resizable = false, vsync = true, borderless = false })
end
function love.update(dt)
time = time + 0.1 * dt
love.window.setTitle("Nearest vs NearestLinear " .. love.timer.getFPS() .. " fps")
end
function love.draw()
local ox = math.floor(image:getWidth() / 2)
local oy = math.floor(image:getHeight() / 2)
love.graphics.setShader()
love.graphics.draw(image, 300, 300, time, scale, scale, ox, oy)
love.graphics.setShader(shader)
shader:send("size", { mult * scale * image:getWidth(), mult * scale * image:getHeight() })
love.graphics.draw(image, 900, 300, time, scale, scale, ox, oy)
end
Code: Select all
extern vec2 size;
vec4 effect(vec4 vcolor, Image tex, vec2 texcoord, vec2 pixcoord) {
vec2 step = vec2(1.0) / size;
vec2 center = texcoord * size - 0.5;
vec2 coord00 = floor(center) + 0.5;
vec2 coord10 = coord00 + vec2(1.0, 0.0);
vec2 coord01 = coord00 + vec2(0.0, 1.0);
vec2 coord11 = coord00 + vec2(1.0, 1.0);
vec4 texel00 = Texel(tex, coord00 * step);
vec4 texel10 = Texel(tex, coord10 * step);
vec4 texel01 = Texel(tex, coord01 * step);
vec4 texel11 = Texel(tex, coord11 * step);
vec2 lerp = fract(center);
vec4 texel0 = mix(texel00, texel10, lerp.x);
vec4 texel1 = mix(texel01, texel11, lerp.x);
vec4 texel = mix(texel0, texel1, lerp.y);
return vcolor * texel;
}
There are 3 caveats though:
- This is slightly slower than hardware bilinear texture filtering, but that shouldn't be a problem.
- You still need to supply the shader with value based on particular image size, which is "not optimal" when you have a lot of images with different sizes.
- In my example image has 1 pixel transparent borders around it. Without them there will be issues around image rectangle edges. Which is important only in the case of rotating + scaling, not only scaling, I think...
- zorg
- Party member
- Posts: 3468
- Joined: Thu Dec 13, 2012 2:55 pm
- Location: Absurdistan, Hungary
- Contact:
Re: FilterMode Bluriness and Workarounds
By the way, i found the thread i mentioned before in my bookmarks: viewtopic.php?f=5&t=89442
Me and my stuff True 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.
Re: FilterMode Bluriness and Workarounds
There is an interesting idea in that thread: to expand the sprite rect in the vertex shader in order to get rid of border problems, nice!zorg wrote: ↑Fri May 05, 2023 4:57 am By the way, i found the thread i mentioned before in my bookmarks: viewtopic.php?f=5&t=89442
I wonder if there's a way to modify it so that it doesn't rely on the angle parameter supplied by the user, which can be little uncomfortable. There is transform_projection in the vertex shader already, which kind of includes angle info...
Re: FilterMode Bluriness and Workarounds
Is there?
I mean, I thought autobatching screws that up, because the transform is now performed CPU-side, and the shader receives
But if it's really there, you can just transform a unit vector pointing right with the view matrix, and you get the sine and the cosine of the angle.
Re: FilterMode Bluriness and Workarounds
This is quite intriguing...
It appears to be a crucial aspect to consider for anyone planning to develop a vertex shader!
Who is online
Users browsing this forum: Bing [Bot], Google [Bot], Semrush [Bot] and 6 guests