
Easy Scalable Text Rendering on the GPU - dfield
https://medium.com/@evanwallace/easy-scalable-text-rendering-on-the-gpu-c3f4d782c5ac
======
pcwalton
Minus antialiasing, the stencil buffer version of this technique has been
described in the Red Book for a long time:
[http://www.glprogramming.com/red/chapter14.html#name13](http://www.glprogramming.com/red/chapter14.html#name13)

~~~
exDM69
Yes, the methods here have been seen before, but there were some tricks that I
haven't come across earlier. Avoiding the stencil buffer (why?) with additive
blending in color buffer is something I haven't heard of before. Similarly,
the implementation details of the antialiasing using color buffer seem like
original work to me.

I think that this was partially motivated by limitations in WebGL. More modern
graphics APIs would allow better control over the render target (no need to
hassle with RGBA8 using arithmetic tricks, just use integers if you need) and
multisampling (gl_SampleMask for MSAA'd "discard" of fragments).

I was familiar with the Loop-Blinn stencil-then-cover trick (and I've worked
with GPU path rendering before) but there were some interesting tidbits in
this article regardless.

~~~
Const-me
> Avoiding the stencil buffer (why?)

GPUs and all drivers do not support independent stencil buffer. If you need a
stencil buffer, then you need to make a Depth=24, Stencil=8 buffer, also
called D24S8.

If you have floating-point 32 bit depth format, sorry, no stencil buffers. If
you don’t have depth at all, stencil buffer would take 4 times more VRAM: for
color buffer, having just single component is fine, e.g. GL_R8UI or
DXGI_FORMAT_R8_UINT.

~~~
exDM69
Yeah, D24S8 is the typical depth-stencil buffer format. That's also the most
common format to have in your "default" framebuffer in OpenGL apps, but in
this case there might be some WebGL-specific limitations that the author wants
to avoid.

WebGL is still based on GLES 2.0 and if you go by the book, you probably need
to account for a 16 bit D16 depth buffer without a stencil present. That's
probably not a very common case in practice (except old mobiles), though....

------
Negative1
What I like most about this technique as opposed to other "draw a font as
curves in a shader" approaches is that it's actually very fillrate friendly.
Instead of drawing a quad and calculating curves (or curve segments) over all
of those pixels/fragments, you draw the font outline in polygons which
restricts where pixel processing occurs. The whole thing actually reminds me
of Carmack's Reverse stencil shadow technique (which grave nice crisp edged
shadows but also stopped pixel processing for shadowed regions).

"The code is open source on GitHub in case it’s useful as a reference"

Thank you!

"it’s written in the Skew programming language"

:-(

Thankfully the shader code is in GLSL as you would expect/hope.

~~~
pcwalton
Well, I'm a bit confused as to how this is fill-rate friendlier than something
like [1]: if you are drawing and checking the winding rule via the stencil
buffer or color buffer, then you're going to overdraw a lot. A technique like
[1], on the other hand, only paints each pixel once.

I would think that, compared to the LUT texture approach, this technique is
lighter in FS load but more expensive in terms of fill rate/ROP.

[1]: [http://wdobbie.com/post/gpu-text-rendering-with-vector-
textu...](http://wdobbie.com/post/gpu-text-rendering-with-vector-textures/)

~~~
Negative1
Stencil check rejections based on overdraw don't hurt as much as you'd think.
Liken it to a clip() (or discard), which is a single instruction. The GPU
pipeline optimizes for this.

The approach you linked too is very well thought out but each font still does
pixel processing for a bezier curve, which is many orders more expensive than
a clip(). Never mind the addition of a dependent read via the LUT and the
tracing step.

~~~
vvanders
A word of caution, not all pipelines are created equal(wrt to
stencil/z-test/etc).

One other downside is this technique requires two drawcalls which can be
pretty painful on some platforms.

Unless you really need large ranges of scale a glyph atlas based solution will
probably be the fastest on a wide range of hardware.

------
danschuller
This is some of the most clear, easy to following writing I've seen when
talking about graphics programming. The illustrations are excellent too - good
stuff!

------
educar
If you want to read on distance field rendering :
[http://blog.qt.io/blog/2011/07/15/text-rendering-in-the-
qml-...](http://blog.qt.io/blog/2011/07/15/text-rendering-in-the-qml-scene-
graph/). This is used in QML's text rendering (not Qt widgets though).

------
leni536
I don't get why zooming up the subpixel antialiased image and looking at the
color fringes makes sense [1]. Why would you want to remove color fringes from
here? That's not what you see, you see [2]. I don't see color fringes in [2].

[1] [https://cdn-
images-1.medium.com/max/1400/1*Uqt60m0luG2S8lm3h...](https://cdn-
images-1.medium.com/max/1400/1*Uqt60m0luG2S8lm3hZRG3A.png)

[2] [https://cdn-
images-1.medium.com/max/1400/1*VoJ6TfORiCHAHy3SN...](https://cdn-
images-1.medium.com/max/1400/1*VoJ6TfORiCHAHy3SNkVWQA.png)

~~~
spyder
You can see the color fringe when you view the character without zooming, here
is an example I have found:

[http://fsrv.dyndns.org/mirrors/dmedia-tutorials-
textrenderin...](http://fsrv.dyndns.org/mirrors/dmedia-tutorials-
textrendering1/index_files/textTut_colorFringes.png)

Make sure the image isn't scaled to see the text as sharp but with color
fringe (on the left side of the image).

The image is from this other great article about sub-pixel font rendering:
[http://fsrv.dyndns.org/mirrors/dmedia-tutorials-
textrenderin...](http://fsrv.dyndns.org/mirrors/dmedia-tutorials-
textrendering1/)

------
dvdplm
Very interesting and very clearly explained for someone with no clue about
these matters like me. I'd love to read a part two! :)

------
wolfgke
Microsoft Research has in the past developed a similar (though not equivalent)
approach: [http://research.microsoft.com/en-
us/um/people/cloop/LoopBlin...](http://research.microsoft.com/en-
us/um/people/cloop/LoopBlinn05.pdf)

~~~
AceJohnny2
As referenced in the article.

------
Keyframe
One downside to this vs SDF is that you would have to distribute font with
your application. With that you have to license/buy that font properly. With
SDF you're creating an image out of which you derive fonts and for that you
don't need to license the font for distribution.

~~~
erikpukinskis
SDFs make my brain happy about the universe. I sense they will only grow in
importance.

------
nice_byte
Can anyone tell me how this compares to SDF in terms of both speed and
quality?

~~~
constexpr
This approach uses much less memory, gives a render that is exact instead of
approximate (it doesn't suffer from corner clipping or grid resolution issues
as you zoom in), and is quicker to do an initial render because there's no
CPU-side preprocessing involved. I haven't profiled both techniques side-by-
side though so I'm not sure which technique is ultimately faster.

------
jtxx000
> Most anti-aliasing techniques do boundary smoothing by treating each pixel
> as a little square and visualizing the fraction of the pixel area that is
> contained inside the outline.

Note that this is a kludge [1].

[1] A Pixel Is _Not_ A Little Square:
[http://alvyray.com/Memos/CG/Microsoft/6_pixel.pdf](http://alvyray.com/Memos/CG/Microsoft/6_pixel.pdf)

~~~
wtallis
That essay is written from a particular perspective and pretends that it is
the clearly _right_ perspective. Whether it's valid to consider pixels to be
little squares depends on whether we're talking about display pixels or image
sensor pixels and whether we're trying to resample or interpolate photographic
data or trying to create data (pixel art, fonts) for a specific display
medium. Sometimes there simply isn't an underlying continuous field to be
point-sampled from. You'll never devise a good way to render text on an LCD
with that article's mindset (not too surprising, since it's from 1995).

~~~
jtxx000
The paper is old, but it's actually a fairly trivial application of signal
processing techniques that have been well-understood since the 40s. For sub-
pixel anti-aliasing, the approach would be to

1\. Construct the underlying continuous field. This is just a function f(x,y)
that returns one if the point is within the text and 0 otherwise.

2\. Convolve f with an anti-aliasing filter. The filter could be tall and
skinny to account for the fact that the horizontal resolution is 3x the
vertical resolution.

3\. Sample the resulting image at sub-pixel positions to produce the red,
green, and blue values.

In the special case where the anti-aliasing filter is a box filter, this is
exactly the same as computing the average for each subpixel. For the technique
proposed in the article, the filter kernel would be the sum of six shifted
impulses (Dirac deltas).

Anyways, I liked the article and wasn't trying to be critical of it. The
convolution approach described above is of theoretical interest, but
implementing it with any non-trivial kernel in real-time is almost certainly
intractable. What I meant was that _every_ implementation of anti-aliased
vector graphics is a kludge, and it's pretty easy to coerce aliasing artifacts
out of all of them using zone plates as inputs.

Edit: your article -> the article

~~~
wtallis
I certainly didn't mean to imply that the signal processing perspective was
untenable with the modern world of actually-rectangular pixels, but what you
describe is really a post-hoc shoehorning of square-pixel thinking into the
signal processing framework. And you still haven't accounted for pixel-
oriented font hinting or pixel-first design of bitmap fonts and graphics that
gives leeway to the underlying shapes in order to maximize legibility when
rendered onto a pixel grid. The signal processing perspective can offer some
valuable insight, but it's a pretty bad choice as an overriding mode of
thought for computer graphics.

(And I'm not the author of the article.)

~~~
jtxx000
Sure, for bitmaps fonts or pixel hinting the signal processing framework
doesn't provide much insight. However, the word aliasing itself refers to a
concept from signal processing, and in my opinion, it's easiest to think of
anti-aliasing from the signal processing perspective.

For example, look at the images in [1] (also a rather old paper). The box
filter results (i.e. where the pixel value is set to the average of covered
area) are less than ideal.

[1] Quadrature Prefiltering for High Quality Antialiasing:
[http://www.cs.northwestern.edu/~jet/Publications/quadfilt95....](http://www.cs.northwestern.edu/~jet/Publications/quadfilt95.pdf)

