# Cinder-SdfText: Initial Release (WIP)

**URL:** <https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171>\
**Category:** CinderBlocks\
**Created:** [July 22, 2016, 7:26pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171 "2016-07-22T19:26:05Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 22, 2016, 7:26pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/1 "2016-07-22T19:26:05Z")

</div>

Hey Everyone,

Just pushed Cinder-SDF: [https://github.com/chaoticbob/Cinder-SdfText](https://github.com/chaoticbob/Cinder-SdfText)

**Why Cinder-SDF?**

- Texture fonts can pixelate in VR, so the standard practice so far has been to use SDF.
- Nice to have a cross platform SDF solution. This release is Windows only - I will be adding the support files for Linux and OS X later this weekend or next week. Or you could do it! 🙂
- Wanted an SDF solution that could handle corners.

**How does it work?**

Cinder-SDF is a `gl::TextureFont` like interface wrapped around [msdfgen](https://github.com/Chlumsky/msdfgen). The SDF data is generated using FreeType curves. Cinder-SDF also uses FreeType to handle both font and glyph metrics for the basic layout operations that it has. This will be extended to whatever makes sense as far as coverage goes.

**What are the current limitations?**

- The SDF generator params need to be tweaked so that the render output is smoother.
- Currently, the SDF uses roughly 32x32 tiles for its data. This may change to 64x64 as params are tweaked.
- Windows sometimes blocks registry access, so the font scanner fails. Not entirely sure why this happens - probably a Windows policy issue. This manifests itself as a crash at startup.
- You may also experience some issues with system fonts where the file names are failing in the UTF16 to UTF8 path conversion. I’m looking into why this is happening.

Give it ago, tell me what you think! Submit PRs for other platforms! Submit PRs for tweaks for improved rendering! File any bugs!

Thanks,  
Hai

Obligatory Screenshot:

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/818768a250802e201a46d4d7d69488d75d6f5695.png)

---

<div class="post-metadata">

**Author:** ![eight\_io](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/eight_io/32/57_2.png) [@eight\_io](https://discourse.libcinder.org/u/eight_io)\
**Post date:** [July 22, 2016, 7:48pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/2 "2016-07-22T19:48:31Z")

</div>

Is iOS somewhere on the roadmap?

Thanks.

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 22, 2016, 7:58pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/3 "2016-07-22T19:58:34Z")

</div>

Not currently. But I’m sure some clever person can make it work on iOS.

---

<div class="post-metadata">

**Author:** ![eight\_io](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/eight_io/32/57_2.png) [@eight\_io](https://discourse.libcinder.org/u/eight_io)\
**Post date:** [July 22, 2016, 8:47pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/4 "2016-07-22T20:47:20Z")

</div>

Once you add OS X, I am going to try to compile it for iOS.

–8

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 23, 2016, 11:38am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/5 "2016-07-23T11:38:17Z")

</div>

Very interesting, Hai!

When I read about msdfgen, I was eager to try for myself, but I haven’t been in the mood for coding lately so you beat me to it. This implementation is a nice first step, although I do see quite a few issues with it in the current version. Please allow me to name a few.

First of all, regarding the ideal font size to create the texture for: while experimenting with my own SDF text stuff, I noticed that a font size of at least 50 pixels (roughly equivalent to 50pt) yields the best results, as this captures most fonts’ details. Only fonts with very thin features, like an elegant handwritten font, need larger font sizes. A size of 24 pixels, which is the default in your sample application, almost never yields great results.

I also noticed that the text still shows a bunch of aliasing, due to a naive shader implementation. Here is what it looks like by default:

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/fe4fa5895570977bae557c21bfd206a806d769eb.png)

By changing the way `opacity` is calculated, it looks like this:

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/74f84663d9395f5fec4f0da52e1bb289c1ee7c3b.png)

`float sigDist = median( sample.r, sample.g, sample.b ); float w = fwidth( sigDist ); float opacity = smoothstep( 0.5 - w, 0.5 + w, sigDist );`

Especially note the difference in quality of the capital “A”. Kerning is not great, the “O” in “Philosophic” for instance seems to float a bit. But that is probably a limitation of FreeType. If not, it might be a good idea to change the way the mesh is created. But I believe that’s something on Cinder’s TODO list, not necessarily yours. Although this one seems to be a bug:

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/ac1b87cd34029e1b2df7b7395fa1976e8788ee4e.png) 

Some fonts exhibit incorrect mapping:

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/ddfad96a7b19acdc951c2e0b114d05ce402731fc.png)

Finally, there seems to be a blending issue: note the grey outlines in the overlapping white text. I enabled pre-multiplied alpha blending in the sample app, but this did not have the desired effect.

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/30e512c10bd554bb22dbd651a4c45231207fdeff.png)

If you’re interested, I created a [cinderblock.xml](https://gist.github.com/paulhoux/9ca37e8882011933d735570682fb2df3) that turns your code into a proper Cinder Block. The only manual change required after creating the project, is to add a `FT2_BUILD_LIBRARY` pre-processor define for all targets. Sadly, this is something TinderBox can not do for you.

-Paul

---

<div class="post-metadata">

**Author:** ![rich.e](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/rich.e/32/1025_2.png) [@rich.e](https://discourse.libcinder.org/u/rich.e)\
**Post date:** [July 23, 2016, 4:54pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/6 "2016-07-23T16:54:50Z")

</div>

> [@paul.houx](#):
>
> First of all, regarding the ideal font size to create the texture for: while experimenting with my own SDF text stuff, I noticed that a font size of at least 50 pixels (roughly equivalent to 50pt) yields the best results, as this captures most fonts’ details. Only fonts with very thin features, like an elegant handwritten font, need larger font sizes. A size of 24 pixels, which is the default in your sample application, almost never yields great results.

Not too long ago, it was brought to my attention that fonts layout their glyphs differently depending on the size. So, if you create a font atlas at size 50 and use it to draw text at size 12, it will look different compared to if you generated the font atlas at size 12. I haven’t tested this myself, but seems worth considering for the design. Of course, there are situations where having a ‘one size fits all’ texture atlas is great, like that it uses less gpu resources, drawing the text in a 3D scene, etc.

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 23, 2016, 5:43pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/7 "2016-07-23T17:43:42Z")

</div>

Interesting, I never realized that. Unless you mean _font hinting_. But I think it’s correct to assume that, if the text looks great at its original size of 50 points, it still looks great if you simply draw it at a scale of 20% (apart from [font hinting](https://en.wikipedia.org/wiki/Font_hinting) that improves readability at small sizes). The fact that signed distance fields actually encode the glyph shapes, rendering text at reduced scale creates new pixels (it does not throw away existing pixels) and looks better than rendering bitmap fonts at reduced scale. See also [this link](http://www.java-gaming.org/index.php?topic=33612.0).

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 23, 2016, 10:15pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/8 "2016-07-23T22:15:51Z")

</div>

Anyway, I should add that using a font size of 12pt for the texture is way too small to capture any details like accents or thin lines.

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 24, 2016, 1:59am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/9 "2016-07-24T01:59:55Z")

</div>

@paul.houx - Thanks for the great feedback. There’s still a ways to go with this. The primary intent was to have something in VR that didn’t require having a ridiculous amount of gl::Texture fonts hanging around. I’ll incorporate that change to the shader right away.

Font size 24 was probably a bad choice for a 32x32-ish SDF field. Just based on what I know of how the font rasterizers work, I don’t think rendering a font size that’s far under the SDF field is wise. The sampling would break down and cause all kinds of anti-aliasing issues.

msdfgen, by default, an SDF based on a font size of 32. You can change this by adjusting the render scale. Based on what I’ve seen, it scales linearly. This is exposed in gl::SdfText::Format.

I think that kerning issue is related to how the bounding box is measured currently.

@rich.e - Do you happen to remember which font it was that had different glyph information at different sizes?

Cinder-SDF calculates the layout information by using the glyph metrics **at the font size that you tell it to**. Not at the font size that the SDF is rendered at.

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 24, 2016, 12:01pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/10 "2016-07-24T12:01:39Z")

</div>

> [@chaoticbob](#):
>
> Font size 24 was probably a bad choice for a 32x32-ish SDF field. Just based on what I know of how the font rasterizers work, I don’t think rendering a font size that’s far under the SDF field is wise. The sampling would break down and cause all kinds of anti-aliasing issues.

Actually I think this is a misconception. Rendering the text with a scale of 50% and up (compared to the size of the glyph on the texture) should be possible without any degradation in quality. If you go below 50%, under-sampling of the texture may cause the loss of detail, e.g. a thin stroke might break up, but in practice there usually is enough information in the SDF texture to handle scales of 25% or even lower. If strokes do break up, this could be solved by taking 4 samples in the shader (super-sampling), but for performance reason you should only do this if necessary.

What I would suggest for this implementation, is to set the default glyph size to something closer to 64x64 pixels on the texture (or at least 50x50) to capture enough font details, but allow users to change this using the `SdfText::Format`.Then create the mesh by scaling the vertices based on desired font size (e.g. 24pt = 24/64 = 37.5%). This is how I do it in my old SDF text solution. You can then use a single font texture for all text sizes.

-Paul

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 25, 2016, 3:07am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/11 "2016-07-25T03:07:14Z")

</div>

@paul.houx I think you misread what I wrote. The case for upscaling is quite proven with SDF. I don’t think anyone argues that. What I was pointing out was that if you went too far below - you would start to see degradation in the sampling. Nothing to do with SDF, just the interpolation of the the textures. And in the same vein of what you’re suggesting with turning up the samples, a traditional font rasterizers do recommend changes (disabling hinting, subpixel, etc) when the size type is too small.

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 25, 2016, 9:10am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/12 "2016-07-25T09:10:21Z")

</div>

@chaoticbob - I think you and I actually agree and that we both have a firm grasp on the subject. We’re basically discussing the major downside of SDF fonts, which is that you need a large SDF field to capture enough glyph details (I have been advocating at least 50x50 texels), but you need to pay special attention to rendering small font sizes with it. Using a small SDF field for small fonts is not a solution.

Edit: after a [discussion](https://github.com/Chlumsky/msdfgen/issues/22) with msdfgen’s author, I came up with a better shader that greatly improves text rendering at smaller sizes.

Left: current shader implementation. Right: new shader implementation. Text rendered @ 25%.

![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/90583e64b16470f77064343f5c3e34c45b2fd3b0.png)

(uses 64x64 glyphs, mesh is generated for a 24pt font size, so this is equivalent to a 6pt font size.)

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 25, 2016, 10:04pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/13 "2016-07-25T22:04:05Z")

</div>

@paul.houx Agreed!

Paul, these changes on the shader are like night and day! The results are extremely impressive! Some of the thin fonts (Courier New, etc) weren’t remotely legible before, and are now.

I’ve incorporated your changes and upped glyph size of 64x64. Also set the alpha to opacity. I can’t tell if it’s doing the right thing or not yet.

I also fixed the tex coord issue you mentioned. Turned out it was the wrapping on the atlas.

I’m still seeing some weirdness on Kristen ITC. It looks like the ‘G’ and the ‘d’ are generating some unnecessary data that’s causing those artifacts. I can’t tell if it’s the parameters getting fed in or if msdfgen doesn’t like the fonts.

Super good stuff!

Hai

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/6550b3422474c85a7c46b4bc83c02c1a06bcf7e8.png)

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 25, 2016, 11:05pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/14 "2016-07-25T23:05:46Z")

</div>

Nice improvement, Hai!

I am still playing with it myself and was able to fix the alpha blending. I’ll get back to you about that. In the meantime, try rendering the text with a font size of 6:

`mFont = gl::SdfText::Font( "Arial", 6 );`

In my case, the text starts to look very funny. I think this is because you’re using integer values for `advance` and `pen`, where they really should be floats. Could you have a look at it?

-Paul

P.S.: interesting choice of [sample text](https://en.wikipedia.org/wiki/Orthodoxy_(book)), by the way.

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 26, 2016, 12:53pm UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/15 "2016-07-26T12:53:37Z")

</div>

As promised, here is a fully revised shader for the text. It renders the text with the highest quality, even at very small scales. It also performs well when the text is rotated. Speaking about performance: it’s not a cheap shader, but I think it’s worth it.

```auto
vec2 safeNormalize( in vec2 v )
{
   float len = length( v );
   len = ( len > 0.0 ) ? 1.0 / len : 0.0;
   return v * len;
}

void main(void)
{
    // Convert normalized texcoords to absolute texcoords.
    vec2 uv = TexCoord * textureSize( uTex0, 0 );
    // Calculate derivates
    vec2 Jdx = dFdx( uv );
    vec2 Jdy = dFdy( uv );
    // Sample SDF texture (3 channels).
    vec3 sample = texture( uTex0, TexCoord ).rgb;
    // calculate signed distance (in texels).
    float sigDist = median( sample.r, sample.g, sample.b ) - 0.5;
    // For proper anti-aliasing, we need to calculate signed distance in pixels. We do this using derivatives.
    vec2 gradDist = safeNormalize( vec2( dFdx( sigDist ), dFdy( sigDist ) ) );
    vec2 grad = vec2( gradDist.x * Jdx.x + gradDist.y * Jdy.x, gradDist.x * Jdx.y + gradDist.y * Jdy.y );
    // Apply anti-aliasing.
    const float kThickness = 0.125;
    const float kNormalization = kThickness * 0.5 * sqrt( 2.0 );
    float afwidth = min( kNormalization * length( grad ), 0.5 );
    float opacity = smoothstep( 0.0 - afwidth, 0.0 + afwidth, sigDist );
    // Apply pre-multiplied alpha with gamma correction.
    Color.a = pow( uFgColor.a * opacity, 1.0 / 2.2 );
    Color.rgb = uFgColor.rgb * Color.a;
}

```

As you can see, I also chose to use pre-multiplied alpha and got rid of the background color.

-Paul

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 27, 2016, 6:27am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/16 "2016-07-27T06:27:10Z")

</div>

Thanks so much for nailing down that shader! I’ll incorporate here in a bit.

I took a look at the small font issue as well as [the ticket your filed about kerning and position](https://github.com/chaoticbob/Cinder-SDF/issues/2#issuecomment-235448438). Hopefully the latest commit addresses the issues you’re running into. We can pick up the discussion on the ticket.

Here’s where I landed with the fixes:

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/a12d8f8322b86763859022e6f25bfd4b54815828.png)

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 27, 2016, 7:48am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/17 "2016-07-27T07:48:54Z")

</div>

@paul.houx Your shader is incredible! Courier New is now fully legible without question! Great work!

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/63802a89212e20bbbcc1991a091dd9f17e83bc94.png)

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/8d2acdbc6cfe1f680d0e9ffadc8c569236930caf.png)

---

<div class="post-metadata">

**Author:** ![paul.houx](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/paul.houx/32/31_2.png) [@paul.houx](https://discourse.libcinder.org/u/paul.houx)\
**Post date:** [July 27, 2016, 8:22am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/18 "2016-07-27T08:22:02Z")

</div>

And your text layout is incredible! I’d say it looks even better than Photoshop’s! 🙂 With the caveat that bold [still](https://github.com/chaoticbob/Cinder-SDF/issues/1#issuecomment-235504373) doesn’t look **bold**.

By the way: if `textureSize` is not available (e.g. on OpenGL ES or something), you could use a `GL_TEXTURE_RECTANGLE` target for the texture instead and use absolute texture coordinates throughout. I know for a fact that the derivatives (`dFdx`, `dFdy` and `fwidth`) are available on most if not all platforms by enabling the right extension, e.g.: `#extension GL_OES_standard_derivatives : enable`. This means that the shader should run on all platforms, as far as I know.

-Paul

---

<div class="post-metadata">

**Author:** ![chaoticbob](https://avatars.discourse-cdn.com/v4/letter/c/ecc23a/32.png) [@chaoticbob](https://discourse.libcinder.org/u/chaoticbob)\
**Post date:** [July 27, 2016, 8:47am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/19 "2016-07-27T08:47:40Z")

</div>

Getting to the style stuff. 🙂 Tomorrow hopefully.

@petros asked for a Unicode sample that used non-Latin characters. There’s some artifacts on Vietnamese - maybe on some other languages. The Chinese font was the second choice, the first one had overlap on the strokes.

 ![](https://canada1.discourse-cdn.com/flex030/uploads/libcinder/original/1X/e52cd9fcaa36f154e0bed86639b5d83fc0090422.png)

---

<div class="post-metadata">

**Author:** ![petros](https://yyz2.discourse-cdn.com/flex030/user_avatar/discourse.libcinder.org/petros/32/26_2.png) [@petros](https://discourse.libcinder.org/u/petros)\
**Post date:** [July 27, 2016, 8:54am UTC](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171/20 "2016-07-27T08:54:47Z")

</div>

Layout and rendering quality looks super already… Kudos to both of you gents!

EDIT: Greek looks good to me !

[Next page](https://discourse.libcinder.org/t/cinder-sdftext-initial-release-wip/171.md?page=2)
