Closed Kuranes closed 8 years ago
I just tested the gl_FragCoord.w
trick you mentioned, looks way better. :+1:
I will look into derivatives over the coming days to see what can be done there.
great :) Make sure to read/discuss on http://www.essentialmath.com/blog/?p=111 That should should help and clarifies
You can use the derivatives, like this:
https://github.com/behdad/glyphy/blob/master/demo/demo-fshader.glsl#L49
In WebGL, you have to enable them using an extension.
Thanks guys. I've been looking into this a bit more, and found a good article on the subject:
His aastep()
looks basically the same as Glyphy's. Now I'm comparing the following two options:
//A
float afwidth = smooth * length(vec2(dFdx(dst), dFdy(dst))) * SQRT2_2;
//B
float afwidth = smooth * SQRT2 / (2.0*gl_FragCoord.w);
The rest of the shader:
#define THRESHOLD 0.5
#define SQRT2 1.4142135623730951
#define SQRT2_2 0.70710678118654757
...
float alpha = smoothstep(THRESHOLD-afwidth, THRESHOLD+afwidth, D);
gl_FragColor = vec4(color, opacity * alpha);
My crude and unscientific findings:
LinearMipMapLinearFilter
is not a good thing for SDF font texturesgl_FragCoord.w
generally leads to the best results at small sizes, but this could be due to the smooth
uniformgl_FragCoord.w
causes the least amount of "flickering" when text scrolls at an angledFdx, dFdy
leads to very crisp anti-aliasing compared to other approachesI've also tried Gustavson's explicit texel interpolation, but have a hard time seeing any differences.
For this particular library I may expose some or all of these options with #ifdefs, and document some of the issues with mipmapping.
The problem with the vec2(dFdx(dst), dFdy(dst)) approximation is that Gustafson is assuming that the gradient of a distance field is always unit length, so vec2(dFdx(dst), dFdy(dst)) is that unit length vector multiplied by the local inverse transform. Mathematically, that's correct, but I've found that dFdx() and dFdy() aren't always that great at computing the gradient at the edges of glyphs, so with an identity transform vec2(dFdx(dst), dFdy(dst)) isn't always unit length. This might be why you see some artifacts with this approach. Skia explicitly normalizes the gradient and multiplies it by the Jacobian because of this. That may be overkill for what you're trying to do, though.
Note that in GLyphy I don't do vec2(dFdx(dst), dFdy(dst)), but vec2(dFdx(texCoord), dFdy(texCoord)) and from that divide by texture size, to get to the pixel size. It's similar, but different.
This module now uses standard derivatives in the SDF shader, and only uses gl_FragCoord.w
as a fallback (rarely needed).
For reference: https://github.com/Jam3/three-bmfont-text/blob/8cec2386e7778e3fb5d0a578e6f64290ceb5c687/shaders/sdf.js#L44-L51
Thanks again for this discussion.
Started here https://twitter.com/tuan_kuranes/status/576412830964031488 , for aa text more independent of transformations:
then
Adding:
The better the scale info, the more precise the smoothing and more readable the text upon distance to camera Scale from view camera scale, depth position, linearized gl_FragCoord.z, anything that give a precise transform scale applied to the text should help there ?
Also in the very particular case of that demo, effect intended can be checked/simulated using gl_FragCoord (just tested quickly that in firefox shader debugger)
somehow works because perspective projection here (smooth might need some change too to adapt the sdf texture input/scale)
But imho, for a library you might want to implement the most robust method possible, which means handling all cases, including non-uniform scale (think text on clothes (real time physically deformed or on scale animated character, etc.) and for that you might want use the whole derivatives thing from the slides.