7 ms·
Easy Scalable Text Rendering on the GPU
- pcwalton 10y agoMinus 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
- deleted 10y ago[deleted]
- jacobolus 10y agoAlso cf. the Loop/Blinn chapter in GPU Gems 3 http://http.developer.nvidia.com/GPUGems3/gpugems3_ch25.html http://http.developer.nvidia.com/GPUGems3/gpugems3_ch25.html
- drudru11 10y agoIs there any reference to where this technique originated? I think it is much more elegant than the traditional point-in-poly ray-cast rasterization technique.
- exDM69 10y ago> I think it is much more elegant than the traditional point-in-poly ray-cast rasterization technique. This is pretty similar to the point-in-poly raycasting (this is especially clear if you use the stencil buffer, but for some reason the author actively avoids that). You might also see some similarities with stencil shadow volumes (as seen in Doom 3). There's two tricks used here: stencil-then-cover for the "bulk" of the shape and the Loop-Blinn method for doing quadratic Bezier curves in a triangle. You can find plenty of resources on both.
- drudru11 10y agoAgreed, but my cg education (many moons ago), usually covered point in poly with a specific section of code to do ray intersection with segments. None that I can recall used the stencil trick, which is so much more clever and simple than solving line equations. I am just wondering... who came up with that first? I looked for the original Warnock paper online, but it is behind the ACM paywall. That would at least tell me if they knew of that in the mid 80's. I will check out the Doom 3 stuff. I had that on my reading list from a while back. Thanks for your answer.
- exDM69 10y agoYou should understand that conceptually stencil-then-cover is the point-in-poly algorithm using line segment intersections. It's just that the GPU triangle rasterizer solves the line equations (or half plane equations) and accumulates the per-pixel result in the stencil buffer. The Doom 3 shadow volume algorithm is just the same, extended into 3d and using a little bit of depth buffer and front/back face culling to make some edge cases work (self-shadowing, shadow volume caps). If you want to go read the original paper (please share a link!), you can circumvent the paywall with sci-hub.io if you don't mind a little piracy.
- exDM69 10y agoYes, 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.
- vanderZwan 10y ago> Avoiding the stencil buffer (why?) Maybe there is a software patent he tries to avoid?
- Const-me 10y ago> 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 10y agoYeah, 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....
- educar 10y agoIf you want to read on distance field rendering : http://blog.qt.io/blog/2011/07/15/text-rendering-in-the-qml-scene-graph/ http://blog.qt.io/blog/2011/07/15/text-rendering-in-the-qml-.... This is used in QML's text rendering (not Qt widgets though).
- anon4 10y agoThe one downside is that you need a 64x64 texture for each glyph, whereas using polygons, you can probably do it in much less memory. As a quick calculation, assuming you're using an 8-bit distance field, memory for one glyph is 6464 bytes, whereas if you store the points as 2D coordinates in float16 precision, it's (16+16)num_points, or one 64x64 texture occupies the same space as 128 points.
- Negative1 10y agoWhat 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 10y agoWell, 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-textures/ http://wdobbie.com/post/gpu-text-rendering-with-vector-textu...
- Negative1 10y agoStencil 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 10y agoA 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.
- jtxx000 10y ago> 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 10y agoThat 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 10y agoThe 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
- wolfgke 10y agoMicrosoft Research has in the past developed a similar (though not equivalent) approach: http://research.microsoft.com/en-us/um/people/cloop/LoopBlinn05.pdf http://research.microsoft.com/en-us/um/people/cloop/LoopBlin...
- AceJohnny2 10y agoAs referenced in the article.
- dvdplm 10y agoVery interesting and very clearly explained for someone with no clue about these matters like me. I'd love to read a part two! :)
- danschuller 10y agoThis 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!
- nice_byte 10y agoCan anyone tell me how this compares to SDF in terms of both speed and quality?
- constexpr 10y agoThis 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.
- leni536 10y agoI 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*Uqt60m0luG2S8lm3hZRG3A.png https://cdn-images-1.medium.com/max/1400/1*Uqt60m0luG2S8lm3h... [2] https://cdn-images-1.medium.com/max/1400/1*VoJ6TfORiCHAHy3SNkVWQA.png https://cdn-images-1.medium.com/max/1400/1*VoJ6TfORiCHAHy3SN...
- spyder 10y agoYou 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-textrendering1/index_files/textTut_colorFringes.png http://fsrv.dyndns.org/mirrors/dmedia-tutorials-textrenderin... 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-textrendering1/ http://fsrv.dyndns.org/mirrors/dmedia-tutorials-textrenderin...
- Keyframe 10y agoOne 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 10y agoSDFs make my brain happy about the universe. I sense they will only grow in importance.