Page 1 of 1

love.graphics.rectangle pixel snapping

Posted: Sat Dec 25, 2021 9:51 am
by JayKyburz
Hey, I've been prototyping a new game. I've noticed that when I draw rectangles to the screen the coordinates are snapped to the nearest whole pixel.

This causes slow animations to look jerky.

Is there a way to disable this behavior. The rects will have soft edges, but the animation should be smooth.

Re: love.graphics.rectangle pixel snapping

Posted: Sat Dec 25, 2021 3:39 pm
by pgimeno
No, but there are a few things you can do.
  1. Frame the rectangle with another rectangle in line mode, setting love.graphics.setLineWidth(2) so that there's one whole pixel to each side of the line. Reduce the inner rectangle by 2 pixels to compensate for the 2 added pixels (one at each border).
  2. Use an image with an alpha border instead of a rectangle. It doesn't need to be stored on disk, you can create it at load time.
  3. Use a shader to draw the rectangle borders. Draw the rectangle as an image.
Each method has its drawbacks.
  • Method 1 is a bit more expensive in terms of CPU.
  • Method 2 is a bit more expensive in terms of video memory. It needs one image per rectangle size.
  • Method 3 is a bit more expensive in terms of GPU and programmer effort, because it needs the size of the rectangle to be passed to the shader.
Here's a comparison. To me, method 3 looks best, but that's a subjective appreciation. Assumes 800x600, so if you try this on a mobile you may need to make adjustments.

Code: Select all

-- Size of all rectangles is 200x100.

-- line width for method 1
love.graphics.setLineWidth(2)

-- image for method 2
local rectimg200x100 = love.graphics.newImage(love.image.newImageData(202, 102,
  "rgba8", ("\0\0\0\0"):rep(202) 
        .. ("\0\0\0\0" .. ("\255\255\255\255"):rep(200) .. "\0\0\0\0"):rep(100)
        .. ("\0\0\0\0"):rep(202)))
rectimg200x100:setFilter("linear", "linear")

-- shader for method 3
local shaderect = love.graphics.newShader[[
  uniform float xsize;
  uniform float ysize;

  vec4 effect(vec4 clr, Image tex, vec2 texpos, vec2 scrpos)
  {
    float upixels = texpos.x * xsize;
    float vpixels = texpos.y * ysize;
    clr.a = min(min(upixels < 1.0 ? upixels : xsize - upixels,
                    vpixels < 1.0 ? vpixels : ysize - vpixels),
                1.0);
    return clr * Texel(tex, texpos);
  }
]]

-- image for method 3
local rectimg1x1 = love.graphics.newImage(love.image.newImageData(1, 1,
  "rgba8", "\255\255\255\255"))

local x = 5
local y = 0
local a = 0

function love.update(dt)
  x = x + dt / 2 -- 1 pixel every 2 seconds
  y = math.sin(a) * 10
  a = a + dt / 3 -- 1 radian every 3 seconds
  if a >= math.pi * 2 then a = a - math.pi * 2 end
end

function love.draw()
  -- method 1
  love.graphics.rectangle("line", x+1, 101 + y, 198, 98)
  love.graphics.rectangle("fill", x+1, 101 + y, 198, 98)
  -- method 2
  love.graphics.draw(rectimg200x100, x, 250 + y, 0, 1, 1, 1, 1)
  -- method 3
  love.graphics.setShader(shaderect)
  shaderect:send('xsize', 200)
  shaderect:send('ysize', 100)
  love.graphics.draw(rectimg1x1, x, 400 + y, 0, 200, 100)
  love.graphics.setShader()
end

Re: love.graphics.rectangle pixel snapping

Posted: Sat Dec 25, 2021 5:38 pm
by grump
The simplest solution that doesn't require any additional code or tricks and saves draw calls: enable MSAA with 2 or more samples, either for all rendering or just for a Canvas. See https://love2d.org/wiki/love.window.setMode or https://love2d.org/wiki/love.graphics.newCanvas

Re: love.graphics.rectangle pixel snapping

Posted: Sat Dec 25, 2021 8:58 pm
by JayKyburz
Hey thanks to you both for taking the time to answer.

I have played around with all 4 methods you suggested above. I agree with pgimeno that the shader method does look best, even with msaa 4 I can see a slight jitter, but for the sake of simplicity, I'm just going to use msaa for now.

My game wont have lots of very slow animation, its just something I noticed and was bugging me.

Re: love.graphics.rectangle pixel snapping

Posted: Sat Dec 25, 2021 10:25 pm
by grump
The shader method is slow if you draw a lot of boxes, because sending uniforms for every box prevents batching. That can be avoided with instanced rendering, but it's quite a bit of code.

Re: love.graphics.rectangle pixel snapping

Posted: Wed Dec 29, 2021 11:57 pm
by pgimeno
MSAA didn't work for me. It's one thing I tried but since I didn't get any result, I didn't recommend it.