distance field fragment shader problem
I am using the fragment shader available in the demos distance-field-2.frag.
When scaling down glyphs, I observed some pixels on the boundaries are displayed as the inside of the glyphs when they should be displayed almost like the outside. I think this is due to the derivative of the distance approaching 0, which makes the smooth step function being 0 even for small values of the distance. A quick and dirty hack was to change
float width = fwidth(dist);
into
float width = max(fwidth(dist), 0.2);
There must be something better to do.
Thanks for the report. Could you post some screenshots because I'm not sure I visualize the problem you mention.
This is before the modification:
and after:
Some examples that I don't have at hand anymore were even more striking.
Thanks. Could you make a PR ?
I'll try to find a better hack in the next few days. Then I'll submit a PR.
For minification, if you want to consider a "full-blown" hack, I've made one in python:
The idea is to use different interpolation depending on the glyph screen size. I used a bicubic filter but I guess it is ok with bilinear if you don't want to code it.
I think I took the idea from this thread (but I'm not even sure):
http://www.java-gaming.org/topics/signed-distance-field-fonts-look-crappy-at-small-pt-sizes/33612/msg/315893/view/topicseen.html#msg315893
This code:
float dist = texture2D(u_texture, gl_TexCoord[0].st).r;
float width = fwidth(dist);
float alpha = smoothstep(0.5-width, 0.5+width, dist);
looks wrong to me. width should be the size of a pixel. That is, something like fwidth(gl_TexCoord[0].st), adjusted for dimensions. I think fwidth() might not do that. In GLyphy I use this instead:
/* isotropic antialiasing */
vec2 dpdx = dFdx (p);
vec2 dpdy = dFdy (p);
float m = length (vec2 (length (dpdx), length (dpdy))) * SQRT2_2;
where p is the texture coordinate essentially.
I'm not sure how it works currently.
Ok I see how fwidth(dist) is supposed to work. Still, I think my solution is more correct, exactly when dist is small.
Thanks @behdad ! @hrouault, does this fix the problem ?
It introduces new problems. Here is the fragment shader I use for the texture gradient method:
#version 330 core
uniform sampler2D u_texture;
in vec2 UV;
in vec4 FrontColor;
out vec4 fragcolor;
float contour(in float d, in float w) {
return smoothstep(0.5 - w, 0.5 + w, d);
}
float samp(in vec2 uv, float w) {
return contour(texture(u_texture, uv).r, w);
}
void main(void)
{
float dist = texture(u_texture, UV).r;
vec2 dpdx = dFdx (UV);
vec2 dpdy = dFdy (UV);
float w = length (vec2 (256 * length (dpdx), 256 * length (dpdy))) * 0.707;
float alpha = contour( dist, w );
fragcolor = vec4(FrontColor.rgb, alpha);
}
This is the result I obtain for the texture gradient:
and for the supersampling:

I think the use of fwidth bring some robustness: noise in the distance is compensated.
I'll add a pull request for the supersampling, without bicubic interpolation for magnification.
Looking at the distance-field-2.c example, I still see some imperfections:
(look at the "p" and "v" in particular). They seem to be easy to correct since the spurious traces are far from the glyph. Do you know what causes these and how to correct them?
This is the result I obtain for the texture gradient: texture_gradient and for the supersampling: supersampling
How is the e rendered? So, e looks fine, d doesn't? Then I suggest you debug that.
I think the use of fwidth bring some robustness: noise in the distance is compensated.
Meh.
I'll add a pull request for the supersampling, without bicubic interpolation for magnification.
I suggest you get to the root of the problem, instead of averaging multiple number to get "better" output.
How is the e rendered? So, e looks fine, d doesn't? Then I suggest you debug that.
Well, this is not trivial... The supersampling does debug that.
I suggest you get to the root of the problem, instead of averaging multiple number to get "better" output.
This is the root of the problem: correct the interpolation when minifying. I was just using the library in a project and noticed these problems. I am now satisfy with the supersampling solution. If you don't, please feel free to reject the PR.
If you don't, please feel free to reject the PR.
I'm not the developer for freetype-gl; Nicolas is. I'm interested in figuring out what the correct way is. Thanks for your contribution :).
I suspect the trace may comes from surrounding glyphs in the texture. There is a parameter in the texture atlas to add some space between glyphs, it might be worth a try to check if this fix the problem.
Also, it seems the top of the O and the T are cut in your example.
Adding some space around the glyphs does fix the spurious traces problem but doesn't fix the first problem (but I need to check more). Thanks! For reference, here is were to change the glyph spacing: https://github.com/rougier/freetype-gl/blob/0c6da62d03832f42c52c25d8cee56b68554971e0/texture-font.c#L610-L613