Starling-Framework icon indicating copy to clipboard operation
Starling-Framework copied to clipboard

Improving distance field style

Open neuronix opened this issue 8 years ago • 22 comments

Hi Daniel,

First of all, thanks for the work on distance field fonts, they are great!

I would however like to suggest a modification to the shader (if technically possible).

Currently the shader seems to draw each letter of the font then apply the outline and move on. This causes an ugly overlapping of the outline on the previous letter. So you either have to keep a outline smaller than the minimum spacing of letters or do with the artefact.

It would be great if the outline could be applied after rendering all the letters so that whatever the thickness of the outline, there is no overlapping.

Here is an illustration, top is current, bottom is expected result:

test

Thanks! :)

neuronix avatar Oct 07 '16 12:10 neuronix

I'm guessing this is not possible. You can (or could, with the previous Starling version, I haven't tried the new one yet) have similar results with drop shadow filter and some over the scale settings (alpha greater than 1 I think), but this would generate additional draws, a new texture, etc.

b005t3r avatar Oct 07 '16 12:10 b005t3r

About the simplest way I can think is make a shader which takes two texture inputs which it combines the alpha channels of with a max, then proceeds as it does now to do its effects.

To generate the two input textures you alternate letters, so one contains T s and the other e t, but with the spaces matching the missing letters. You could draw these into rendertextures, or even one rendertexture.

A lot of work, probably more in the logic of keeping track of the letters than in the shader, but it should work. And provided your text does not change too often the performance overhead should not be an issue.

JohnBlackburne avatar Oct 07 '16 13:10 JohnBlackburne

Hmm, with two draw passes you don't have to alternate anything - you can draw the outlines in one pass and the actual letters in the other pass. You can probably even do it without any modifications by creating two text fields, and using one to draw the outlines (setting outline color to the same value as the letter color) and the other to draw just the letters above the first one. One additional call, so not a huge overhead.

Doing this internally in the text field should not be a huge deal, what do you think Daniel?

b005t3r avatar Oct 07 '16 13:10 b005t3r

Mhm, as was already described (thanks!), it's not possible to avoid this overlapping, at least not with the MeshStyle itself.

The nice thing about the DistanceFieldStyle is that it can draw both fill and outline in one fragment shader; but since fragment shaders are executed for each pixel of the text's glyphs (quads), the overlapping is just part of this process and can't be avoided.

However, as Łukasz mentioned, an outline can be very easily achieved with a GlowFilter. For example, the following code ...

textField.filter = new GlowFilter(0x0, 40, 1, 1.0);

... produces this output:

simple outline

When you then call filter.cache(), this will be super fast (also just one draw call), at least if the text doesn't change too often.

Alternatively, the idea of using two TextFields behind each other is even better: just use a different threshold for the two layers (the outline would get something close to 1.0). Don't use setupOutline for them! Then, they will even be batched together, so you will end up with just one draw call (given that batchable is activated on each TextField).

Please try that out! If you need any help, let me know.

PrimaryFeather avatar Oct 07 '16 14:10 PrimaryFeather

Thanks for the explanation.

I'm using outlines on all the textfields of the app so I don't like the idea of a filter knowing it creates extra textures and the double textfield idea doesn't feel right at such a scale (would be ok for an isolated textfield). I have found a setting where overlapping is minimal so i'll roll with it.

Shader noob here, but just thinking, as you write the glyphs and outline to the texture, would it be possible as you apply the outline to each concerned pixel, first read the existing value and not change it if the pixel value is already white ? (or the .color of the textfield). This would fix the overlap without adding an extra pass even if the process would require more computing.

neuronix avatar Oct 07 '16 14:10 neuronix

I think that the most convenient solution would be introducing a new mode (2_PASS_OUTLINE?) and overriding effect's render() method to call drawTriangles two times for the same geometry. Having two text fields would give the same result, but I think it's unnecessarily CPU-heavy as there're twice as many triangles to batch.

b005t3r avatar Oct 07 '16 15:10 b005t3r

Also, another idea :)

It should be possible with this approach to easily add styles like bold and probably even italic. Bold would do something very similar to stroke, but only on the x-axis and italic would draw letters normally, but the closer the currently rendered line would be to the top of the texture the bigger x-offset (or U-offset) it would subtract when calculating UVs (the effect would look like top lines are pushed to the right, so the letter would be skewed).

What do you think, Daniel? Italic would probably requite a bit bigger letter textures (few pixels wider), but bold should work for almost any font.

b005t3r avatar Oct 10 '16 10:10 b005t3r

Those are definitely great ideas for future extensions of the distance field style, thanks a lot for the suggestions!

Thus a few thoughts:

  • I think for a bold style to work, we'd need to switch to multi-channel distance field textures (as suggested here, so that it's possible to limit the effect to the x-axis. But that would make sense, anyway!
  • Italic is a little harder, because the glyphs do not have equal height, and the style itself doesn't know that it's used on text. Some of the setup work would have to be done by the bitmap font then.

@neuronix

but just thinking, as you write the glyphs and outline to the texture, would it be possible as you apply the outline to each concerned pixel, first read the existing value and not change it if the pixel value is already white ?

That's not possible, unfortunately, because the fragment shaders are executed on the polygon-level. I don't have any information about the pixel that was output by the polygon (glyph) below the current one. Sorry!

PrimaryFeather avatar Oct 13 '16 08:10 PrimaryFeather

I added the "Feature" tag because I want to keep this issue open as a list of suggestions about what can be done to enhance the DistanceFieldStyle in the future.

PrimaryFeather avatar Oct 13 '16 08:10 PrimaryFeather

About those multi-channel DFs, can those be generated just like the standard ones? I mean, do tools support it?

b005t3r avatar Oct 13 '16 12:10 b005t3r

I know only about this command-line tool, unfortunately. It would make a lot of sense for others to add that feature, too. Especially on Stage3D, where there are no single-channel textures: it's a waste of memory to use just one.

PrimaryFeather avatar Oct 13 '16 14:10 PrimaryFeather

Is there a chance you could add the "2_PASS_RENDER" to the current implementation in the near future? The current state of outlines isn't optimal even if it works. When mockups look beautiful, the final in app result is really poor due to the hacky spacing :(

neuronix avatar Oct 25 '16 15:10 neuronix

I'm currently on a vacation, but I can look into that again when I'm back. I can't promise anything yet, but it sounds manageable.

PrimaryFeather avatar Oct 29 '16 05:10 PrimaryFeather

Do you have any news on this Daniel? :)

neuronix avatar Dec 14 '16 14:12 neuronix

I'm really sorry for the huge delay ... when I returned from my vacation, I had to focus most of my time on the upcoming Starling book. I haven't gotten around to implementing this feature, but it's definitely coming!

The only workaround I can recommend right now is that GlowFilter I mentioned above. :hushed:

PrimaryFeather avatar Dec 14 '16 19:12 PrimaryFeather

Hi Daniel, don't want to be nagging and I'm sure there are more pressing matters but I'm very much looking forward to these improvements so please keep them on your list and fit them in whenever you can :)

neuronix avatar Feb 20 '17 10:02 neuronix

By all means: don't stop reminding me about it!! :smile: I promise you, though, it's still high on the list.

PrimaryFeather avatar Feb 20 '17 10:02 PrimaryFeather

I'm kindly reminding you of this request as instructed :D

neuronix avatar May 22 '17 14:05 neuronix

Thanks for the reminder – now's the perfect time for that. 👍

PrimaryFeather avatar May 22 '17 16:05 PrimaryFeather

I just started playing around with this — I still think it's possible with the current MeshStyle architecture, though challenging. MeshEffect.render must actually make two draw calls, which is a first (I designed it to be responsible for just one).

As a proof of concept, I'll probably implement this as an extension (e.g. DistanceFieldStylePlus) that always renders two passes. The current DistanceFieldEffect class is already quite complex as it is. :wink:

Even if I succeed, there will be a side-effect, though. If you've got several TextFields that use the style, and they overlap each other, the outline / drop shadow / glow will be behind all of the glyphs. You won't be able to correctly move them on top of each other (except if you break batching by adding a different layer in-between).

Nevertheless, I'd like to experiment with the exact font you are using, and try how far one can come with the existing style. Can you send me that font somehow? Thanks in advance!

... and just to make that clear: I can't promise anything yet — if I run into problems and this takes too long, I'll have to delay this and work on other things instead. Many developers are waiting for Starling 2.2, and I don't think this feature is a must-have for that release.

PrimaryFeather avatar May 30 '17 12:05 PrimaryFeather

Hi Daniel,

First of all, thanks for looking into this! I'm using the Dimbo font (http://www.dafont.com/fr/dimbo.font). I have also attached my Hiero settings (rename the file to .hiero, github doesn't support the format): dimbo.txt

I'm using the effect on all the Textfields so it's pretty intensive. If improving the effect means 2 draw calls I think it won't be worth while on a app-wide scale, so it would be limited to rare situations. On my side, I have managed to find some reasonable settings and I think i'll spend some time adding kerning values (which Dimbo does not include..) to fix some of remaining overlaps.

If you feel this isn't going to work out nicely I'd say we drop the idea, Starling 2.2 is of course more urgent! :)

neuronix avatar May 30 '17 13:05 neuronix

Wow, that's a really beautiful font! :smile:

Okay, I see why this issue came up with this font in particular; the characters are very close to each other, and that limits the outline width, of course. The maximum outline width seems to be somewhere between 0.2 and 0.3, according to my tests — which still looks really nice, but I can understand that more would be even better. :wink:

However, if you managed to find reasonable settings, I think you will be better off with those, anyway: every solution that saves you draw calls is a win, normally (as long as you are happy with the visual outcome).

In that case, I will postpone this feature request for now! Thanks a lot for your understanding — I'll rather make full speed so that you can release your project with a stable v2.2 release of Starling. 😎

PrimaryFeather avatar May 30 '17 13:05 PrimaryFeather