Starling-Framework
Starling-Framework copied to clipboard
Improving distance field style
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:
Thanks! :)
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.
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.
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?
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:
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.
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.
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.
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.
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!
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.
About those multi-channel DFs, can those be generated just like the standard ones? I mean, do tools support it?
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.
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 :(
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.
Do you have any news on this Daniel? :)
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:
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 :)
By all means: don't stop reminding me about it!! :smile: I promise you, though, it's still high on the list.
I'm kindly reminding you of this request as instructed :D
Thanks for the reminder – now's the perfect time for that. 👍
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.
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! :)
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. 😎