You nailed it. You may need to adapt to zoom here
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.
By playing with the parameters mostly
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.
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.
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.
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.
Yeah that was what I think we ended up doing.
GREAT FEATURE! GG @Deltakosh ![]()
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 ![]()
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:
- Group 0 renders: backPlane2 and frontPlane write to depth.
- Depth buffer is cleared before group 1.
- Group 1 renders: only backPlane1 writes to depth.
- 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!

