Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

better smoothing upon scale #1

Closed
Kuranes opened this issue Mar 14, 2015 · 7 comments
Closed

better smoothing upon scale #1

Kuranes opened this issue Mar 14, 2015 · 7 comments

Comments

@Kuranes
Copy link

Kuranes commented Mar 14, 2015

Started here https://twitter.com/tuan_kuranes/status/576412830964031488 , for aa text more independent of transformations:

derivatives gives you gradient, to always get correct smoothstep see @cthulhim slides http://www.essentialmath.com/GDC2015/VanVerth_Jim_DrawingAntialiasedEllipse.pdf
or in skia code https://github.com/google/skia/blob/master/src/gpu/effects/GrDistanceFieldTextureEffect.cpp#L87

then

If you're only using uniform scale, you could do smoothstep w/afwidth = sqrt(2)/(2*scale). Needs a uniform, though.
It may be better to compute 1/scale from the view matrix in the vertex shader and pass afwi
The aim is to correct distance upon transformation, if it's a uniform scale, just getting scale should be enough

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)

float smooth2 = smooth*sqrt(2.)/(2.*gl_FragCoord.w);
float alpha = smoothstep(0.5 - smooth2, 0.5 + smooth2, dst);

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.

@mattdesl
Copy link
Contributor

I just tested the gl_FragCoord.w trick you mentioned, looks way better. 👍

I will look into derivatives over the coming days to see what can be done there.

@Kuranes
Copy link
Author

Kuranes commented Mar 16, 2015

great :)
Make sure to read/discuss on http://www.essentialmath.com/blog/?p=111
That should should help and clarifies

@behdad
Copy link

behdad commented Mar 16, 2015

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.

@mattdesl
Copy link
Contributor

Thanks guys. I've been looking into this a bit more, and found a good article on the subject:

2D Shape Rendering (code)

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);

demo

My crude and unscientific findings:

  • LinearMipMapLinearFilter is not a good thing for SDF font textures
  • gl_FragCoord.w generally leads to the best results at small sizes, but this could be due to the smooth uniform
  • gl_FragCoord.w causes the least amount of "flickering" when text scrolls at an angle
  • at larger sizes dFdx, dFdy leads to very crisp anti-aliasing compared to other approaches

I'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.

@jvanverth
Copy link

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.

@behdad
Copy link

behdad commented Mar 18, 2015

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.

@mattdesl
Copy link
Contributor

mattdesl commented Dec 7, 2015

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.

@mattdesl mattdesl closed this as completed Dec 7, 2015
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants