MSDF Text renderer

You nailed it. You may need to adapt to zoom here

1 Like

I feel like we should almost discard around the edges of the quad within a certain threshold just to prevent any overdraw of the sdf.

image

So there really is no way to get rid of these edge artifacts?

By playing with the parameters mostly

1 Like

I’m just confused because out of other msdf solutions I have never seen these artifacts before. I want to use this one because its highly efficient, but having to juggle props to prevent rendering artifacts that should not be there seems rather ehhhhh. Maybe ill take a look at the shader code and see if there is something that can be done.

Its looking like its the thickness that is causing this. I might be able to fix this actually with props you are right. Just cant have it as bold as wanted. Looks like anything over 0.1 will cause this.

UPDATE Also using a larger msdf font image also cleaned up some artifacts.

1 Like

Yeah we may have bugs / improvements in the shaders so please do ;D

Hi!

I’m trying to use it to display scores within the game.

Is there a way to update the text?

I think the only way to achieve this right now is to dispose the renderer and create a new one. As long as you are using the font assets you already have created disposing and recreating happens very quickly. Just make sure to wait for the new text to be rendered before deleting the old text or else you will have flickering. Here is what that approach looks like with many rapid updates in my project: Bar Chart Race

It would be more ideal if there was official clearParagraph method or something like that.

2 Likes

Were you able to get around the artifacts? I tried with 1024 res but even then I see artifacts around the text as you move away from it…

Yeah, I ended up packing my own text file and had to do some shader hacks to get everything clean.

I think we ended up going back to the BJS shader after some work with the distance file. Let me look into this for you a bit sorry I just now noticed your comment.

1 Like

Thanks! I got around this by just using bigger font files and putting them into the MSDF converter, for my use case this alone got rid of the artifacts with an Inter font.

1 Like

Yeah that was what I think we ended up doing.

1 Like

GREAT FEATURE! GG @Deltakosh :wink:

1 Like

EricLengyel/Slug: Reference code for the Slug Algorithm. Yohane on X: ā€œPorted Threejs Slug Text into TSL WebGPU. I have been struggling to perfectly sync dom and canvas text using MSDF due to texture resolution and text aliggment. Finally I have the solution using Slug. Credit to : https://t.co/0jedzjMdYz ThreeJS forum : https://t.co/MeoELhUcbL https://t.co/jZHtKWQIhZā€ / X Slug Font Rendering Three.js

Hi everyone, I stumbled upon something interesting (and quite useful) by accident:

Another way to manage renderingGroupId is to use a separate mesh as a background for your text. Changing the background’s renderingGroupId also affects the text rendered in front of it.

You can see it in action by uncommenting line 29 here:

I’m not entirely sure why it behaves this way, but I’m sure there’s a logical explanation behind it :slightly_smiling_face:

Hello, following up on my previous message, what initially seemed useful actually turns out to be problematic.

In some cases, it causes the ignoreDepthBuffer parameter to be ignored. You can see this behavior in this playground:
https://playground.babylonjs.com/#6RLCWP#91

  • The text on the right correctly ignores the depth buffer

  • The text on the left does not as expected

However, if you uncomment line 40 and assign renderingGroupId = 1 to the background plane, all rendered text behaves as if it ignores the depth buffer. This is not expected and creates issues for my use case.

I took a look at the TextRenderer implementation but couldn’t identify the cause.

Happy to help investigate further or contribute to a fix if needed. Curious to hear your thoughts.

Not a bug:)

In your PG the text is drawn from scene.onAfterRenderObservable, i.e. after the whole scene has been rendered. By default, Babylon’s RenderingManager auto‑clears the depth+stencil buffer between rendering groups.

So when you set backPlane1.renderingGroupId = 1:

  1. Group 0 renders: backPlane2 and frontPlane write to depth.
  2. Depth buffer is cleared before group 1.
  3. Group 1 renders: only backPlane1 writes to depth.
  4. onAfterRender fires: both text renderers draw.

At that point the depth buffer only contains backPlane1 (right side, x = 6). The left text (x = –6) has nothing in the depth buffer at its pixels, so its depth test passes everywhere and it looks like ignoreDepthBuffer is on but depth testing is actually still enabled, there’s just nothing to occlude it. TextRenderer.render correctly calls engine.setDepthBuffer(false/true) around its draw (textRenderer.ts:288‑343); ignoreDepthBuffer itself is not being ignored.

Thanks a lot for the detailed explanation, I didn’t know the depth buffer was cleared between each renderingGroupId, so the behavior I observed now makes sense.

To achieve what I’m looking for, I ended up using a utility layer. Here is the result:

LEFT: text + plane with depth
RIGHT: text + plane without depth
https://playground.babylonjs.com/#6RLCWP#92

I also needed to differentiate the text rendering calls using onAfterCameraRenderObservable or onAfterRenderObservable, so that it follows with the utilityLayer addition.

Do you think this is the right approach, or maybe there is a better way to handle this? Ideally we would link text rendering with the utilityLayer rendering because as soon as you add a renderingGroupId on any object, it stops working.

this looks alright to me!