I am testing some font rendering with Multi-channel signed distance fields (MSDF). Everything looks perfect until I move further away from the rendered text, and it starts having weird artefacts (pixelated, some parts disappear).

The first image shows how the rendered text looks up close. As you can see, it is very smooth (open image in new tab).
But as I am moving further, it starts getting more artefacts (images are zoomed in):

Even further:

All MSDF glyphs are located on a texture atlas used in the fragment shader, where each glyph is anti-aliased. In addition, each glyph is rendered on its quad.
The fragment shader (written in wgsl but is similar to glsl, updated with better results shown in the picture below):
var texture: texture_2d<f32>;
var tex_sampler: sampler;
var<uniform> unit_range: f32;
fn screen_px_range(tex_coord: vec2<f32>) -> f32 {
let range = vec2<f32>(unit_range, unit_range)/vec2<f32>(textureDimensions(texture));
let screen_tex_size: vec2<f32> = vec2<f32>(1.0, 1.0)/fwidth(tex_coord);
return max(0.5 * dot(range, screen_tex_size), 1.0);
}
fn fragment(tex_coords: vec2<f32>) -> [[location(0)]] vec4<f32> {
let s = textureSample(texture, tex_sampler, tex_coords).rgb;
// Acquire the signed distance
let d = median(s.r, s.g, s.b) - 0.5;
// Convert the distance to screen pixels
let screen_pixels = screen_px_range(in.tex_pos) * d;
// Opacity
let w = clamp(d/fwidth(d) + 0.5, 0.0, 1.0);
let bg_color: vec4<f32> = vec4<f32>(0.3, 0.2, 0.1, 0.0);
let fg_color: vec4<f32> = vec4<f32>(0.6, 0.5, 0.4, 1.0);
return mix(bg_color, fg_color, w);
}
median()
function sorts the input and gets the middle number.
unit_range
variable represents the pixel range in the generated atlas (more info).
I copied this snippet from the official msdfgen repo, created by Viktor Chlumsk'y, who came up with his Multi-channel signed distance fields font generating and rendering technique.
So, what is the cause for these artefacts, and how do I fix it. For example, should I use supersampling, or is there a more efficient way to fix this in the fragment shader?
I programmed this in Rust with wgpu, but it doesn't really matter.
Edit 1:
After more testing, I found that using texture mipmaps makes the text look worse. Also, making the text thicker (in the fragment shader) removes some artefacts.
Now, my rendered text looks something like this from further away:

Different from before, you can see that it keeps most of its details Also, I'll test supersampling and bring more updates. Finally, I'll leave this question unanswered if someone finds a better solution.
A similar problem was issued here.
Edit 2:
Finally, I came back to MSDF rendering and created my own GitHub repo, where I'm adding as much useful info as possible about this topic and MSDFs in general. There you can find many shader examples where MSDFs are used with additional modifications like thickness mods, outlines or shadows.
The best shader I have found so far to solve this problem looks something like this:
#version 330
uniform sampler2D tex;
uniform float pxRange; // set to distance field's pixel range
in vec2 uvCoord;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
float screenPxRange() {
vec2 unitRange = vec2(pxRange)/vec2(textureSize(tex, 0));
vec2 screenTexSize = vec2(1.0)/fwidth(uvCoord);
return max(0.5*dot(unitRange, screenTexSize), 1.0);
}
void main() {
vec4 texel = texture(tex, uvCoord);
float dist = median(texel.r, texel.g, texel.b);
float pxDist = screenPxRange(tex) * (dist - 0.5);
float opacity = clamp(pxDist + 0.5, 0.0, 1.0);
gl_FragColor = vec4(1.0, 1.0, 1.0, opacity);
}
Check out Chlumsky's msdfgen utility repo.