Trying to understand why text rendering differently with a background in a canvas

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
ryanzec
Prole
Posts: 11
Joined: Sat May 05, 2018 3:37 pm

Trying to understand why text rendering differently with a background in a canvas

Post by ryanzec »

So I have this code (bear in mind this is just something I threw together as I am evaluating Love2D as an alternative to Unity3d for my current project so the structure is probably not great):

Code: Select all

local inventoryCanvasVertices = {}
local inventoryScreenVertices = {}

local inventoryWidth = 340
local contentHeight = 16000
local viewportHeight = 150
local inventoryContentCanvas = love.graphics.newCanvas(inventoryWidth, contentHeight)
local inventoryViewportCanvas = love.graphics.newCanvas(inventoryWidth, viewportHeight)

function drawInventory()
  love.graphics.setCanvas(inventoryContentCanvas)

  local topOffset = 180

  -- important line 1
  love.graphics.setColor(0, 0, 0, 1)

  inventoryCanvasVertices = getBoxVertices(0, 0, inventoryWidth, contentHeight)
  inventoryScreenVertices =
    getBoxVertices(
    uiManager.sideInfo.points.topLeft.x + uiManager.inventory.positionOffset.x + uiManager.sideInfo.padding,
    uiManager.sideInfo.points.topLeft.y + uiManager.inventory.positionOffset.y + uiManager.sideInfo.padding + topOffset,
    inventoryWidth,
    contentHeight
  )

  -- important line 2
  love.graphics.polygon("fill", inventoryCanvasVertices)

  love.graphics.setColor(1, 1, 1, 1)

  drawItem()

  love.graphics.setCanvas(inventoryViewportCanvas)
  love.graphics.draw(inventoryContentCanvas, 0, 0, 0, 1, 1, 0, uiManager.inventory.scrollOffset)

  love.graphics.setCanvas()

  love.graphics.draw(
    inventoryViewportCanvas,
    uiManager.sideInfo.points.topLeft.x + uiManager.inventory.positionOffset.x + uiManager.sideInfo.padding,
    uiManager.sideInfo.points.topLeft.y + uiManager.inventory.positionOffset.y + uiManager.sideInfo.padding + topOffset
  )
end
Just for context, the basic idea is that I wanted to see how easy it was to create a scrollable ui content area. From my limited knowledge of Love2D, I decide to create 2 canvases, one for the content and 1 for the scrollable viewport and just set an offset when drawing the content in the scrollable viewport (and then just draw the scrollable viewport to the "main canvas"). While I am not sure if this a the best way to do this, lets just ignore that for the time being (unless it is directly related to the text rendering issue).

Now I am drawing the scrollable viewport in an area where I am already drawing a black background however I am drawing another black background in the content canvas which allows me to get the following results:
Screen Shot 2018-05-09 at 4.02.29 PM.png
Screen Shot 2018-05-09 at 4.02.29 PM.png (8.74 KiB) Viewed 5232 times
Initial I did not do this because I think I did not need to however if I remove the black background in the content canvas (the lines I commented as important 1 / 2), I get the following results:
Screen Shot 2018-05-09 at 4.02.42 PM.png
Screen Shot 2018-05-09 at 4.02.42 PM.png (6.72 KiB) Viewed 5232 times
So the main part of my question here is why would the text render different based on the background being nothing (or transparent) vs it explicitly being set as black when it is being drawing on top (a.k.a. after) a black box anyways (though on a different canvas)?

Now I am also opened to any suggestions / critiques people might have about my implementation of the scrollable content outside of you can using library X / Y / Z (part of the allure of using Love2D is that fact it's low level API is much simpler and I think, maybe naively, it is easier to do exactly want you want and nothing more than with something like Unity).
User avatar
zorg
Party member
Posts: 3436
Joined: Thu Dec 13, 2012 2:55 pm
Location: Absurdistan, Hungary
Contact:

Re: Trying to understand why text rendering differently a background in a canvas

Post by zorg »

At first glance, you're not clearing the canvases, which is probably your issue; if you don't do that each frame, you'll accumulate the text color near the edges of the text where the font rasterizer anti-aliases the text needed to be drawn.

0.1+0.1+0.1+ ... will tend towards infinity, or if clamped to a maximum of 1.0, then 1.0.

On the other hand, i'd say that using only one canvas should be enough, you shouldn't need two; just either offset the inventory content canvas by how much you need to, as dictated by your viewport... you could also set up a scissor (although in screen-space, transforms have no effect on that) to basically clip the area you should draw your canvas to.
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.
ryanzec
Prole
Posts: 11
Joined: Sat May 05, 2018 3:37 pm

Re: Trying to understand why text rendering differently a background in a canvas

Post by ryanzec »

zorg wrote: Wed May 09, 2018 9:31 pm At first glance, you're not clearing the canvases, which is probably your issue; if you don't do that each frame, you'll accumulate the text color near the edges of the text where the font rasterizer anti-aliases the text needed to be drawn.

0.1+0.1+0.1+ ... will tend towards infinity, or if clamped to a maximum of 1.0, then 1.0.

On the other hand, i'd say that using only one canvas should be enough, you shouldn't need two; just either offset the inventory content canvas by how much you need to, as dictated by your viewport... you could also set up a scissor (although in screen-space, transforms have no effect on that) to basically clip the area you should draw your canvas to.
Interesting, I though that was the case however when I tested clearing both canvases, I got text that was a lot thinner:

Screen Shot 2018-05-09 at 5.51.48 PM.png
Screen Shot 2018-05-09 at 5.51.48 PM.png (7.47 KiB) Viewed 5212 times

I tried it again but this time only clearing the content canvas but not the viewport canvas and than gave me the expected results. Do you know why clearing the viewport canvas would effect that text like that out of curiosity?

It might not be that big of a deal to figure out as I am going to try to refactor the code to just use 1 canvas like you suggested as it did not dawn on me that I can render stuff outside the the size of the canvas and I just need to offset it (hopefully these things with dawn on my more as I work at this level of code more :)).
ryanzec
Prole
Posts: 11
Joined: Sat May 05, 2018 3:37 pm

Re: Trying to understand why text rendering differently with a background in a canvas

Post by ryanzec »

Actually, if I don't clear the viewport canvas, then the old text never goes away, which make sense, which means I am left with to thin text.

Also when I try to use just one canvas, I get 2 issues:

Screen Shot 2018-05-09 at 6.08.59 PM.png
Screen Shot 2018-05-09 at 6.08.59 PM.png (70.36 KiB) Viewed 5211 times

1. when it is scrolled, it over with the content above (not sure if scissor would help based on your previous comment)
2. The content that should be shown below does not show (there should be a Berry 4 / Berry 5 / etc).

The code I am using is:

Code: Select all

love.graphics.draw(
  inventoryContentCanvas,
  uiManager.sideInfo.vertices[1] + uiManager.sideInfo.padding,
  uiManager.sideInfo.vertices[2] + uiManager.sideInfo.padding + topOffset,
  0,
  1,
  1,
  0,
  uiManager.inventory.scrollOffset
)
Code you maybe provide some pseudo code that might lead to me the solution for using 1 canvas?
User avatar
bartbes
Sex machine
Posts: 4946
Joined: Fri Aug 29, 2008 10:35 am
Location: The Netherlands
Contact:

Re: Trying to understand why text rendering differently with a background in a canvas

Post by bartbes »

The second problem is the blend mode. Love uses alpha blending by default, basically using the alpha channel to determine how to blend with the background. If you do this twice, you'll see that transparent parts (like the edges of characters) are blended into the background twice, leading to the thin text you see. When drawing the canvas to the screen you want to prevent blending from happening again, and you can do this by changing the blend mode to be a so-called "premultiplied" blend mode.
User avatar
pgimeno
Party member
Posts: 3544
Joined: Sun Oct 18, 2015 2:58 pm

Re: Trying to understand why text rendering differently with a background in a canvas

Post by pgimeno »

bartbes wrote: Thu May 10, 2018 9:15 am The second problem is the blend mode. Love uses alpha blending by default, basically using the alpha channel to determine how to blend with the background. If you do this twice, you'll see that transparent parts (like the edges of characters) are blended into the background twice, leading to the thin text you see. When drawing the canvas to the screen you want to prevent blending from happening again, and you can do this by changing the blend mode to be a so-called "premultiplied" blend mode.
That is something that always bothers me. Why is LÖVE not doing associative alpha blending? Is it not possible or too expensive in OpenGL? This forum thread in the OpenGL site has no responses: https://www.opengl.org/discussion_board ... a-blending so I guess that may be the reason, but it would be a relief to get a confirmation.

Edit: I think I've found the answer: https://gamedev.stackexchange.com/quest ... link-82996

It seems that working with premultiplied alpha is the best approach, but it's not too easy. Text must be drawn with "alphamultiply" mode on a *black* transparent canvas for it to work, if you want to obtain a premultiplied result. Black is the premultiplied value for a transparent background; clearing the canvas to some other colour will give wrong results, even if fully transparent.

Edit 2: OK, I've done the math now. Bartbes was right: drawing in alphamultiply mode to a canvas with premultiplied alpha gets you a canvas with premultiplied alpha, which you can draw to the screen in premultiplied mode to get the same effect as if you drew the objects directly to the screen. The only catch is that if you clear the canvas with a transparent colour other than black, you don't get a premultiplied canvas as a starting point, and the result won't be right. But the point of this second edit is to note that it's not necessary to work with premultiplied everything. Fixed my first edit.
Last edited by pgimeno on Fri May 11, 2018 11:17 am, edited 2 times in total.
User avatar
raidho36
Party member
Posts: 2063
Joined: Mon Jun 17, 2013 12:00 pm

Re: Trying to understand why text rendering differently with a background in a canvas

Post by raidho36 »

You can run a loop over the image pixels when you load it, multiplying color channels by the alpha channel. You can also convert any standard graphics to premultiplied graphics right within the shader:

Code: Select all

vec4 color = texel ( image, coords );
color.rgb *= color.a;
It's one extra operation in the shader, but the performance hit is pretty marginal, so it's an OK substitute. It's still best that you don't do this in real time, instead doing it at export or import stage where the time spent doing it is basically irrelevant.

It kinda baffles me that people think GPUs should bend backwards over their misunderstanding of rendering mechanisms.
Post Reply

Who is online

Users browsing this forum: Ahrefs [Bot] and 54 guests