Gamua / Starling-Framework

The Cross Platform Game Engine
http://www.starling-framework.org
Other
2.84k stars 819 forks source link

Improving distance field style #911

Open neuronix opened 7 years ago

neuronix commented 7 years ago

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! :)

b005t3r commented 7 years ago

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.

JohnBlackburne commented 7 years ago

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.

b005t3r commented 7 years ago

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?

PrimaryFeather commented 7 years ago

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.

neuronix commented 7 years ago

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.

b005t3r commented 7 years ago

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 commented 7 years ago

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.

PrimaryFeather commented 7 years ago

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

Thus a few thoughts:

@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 commented 7 years ago

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.

b005t3r commented 7 years ago

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

PrimaryFeather commented 7 years ago

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.

neuronix commented 7 years ago

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 :(

PrimaryFeather commented 7 years ago

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.

neuronix commented 7 years ago

Do you have any news on this Daniel? :)

PrimaryFeather commented 7 years ago

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:

neuronix commented 7 years ago

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 :)

PrimaryFeather commented 7 years ago

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

neuronix commented 7 years ago

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

PrimaryFeather commented 7 years ago

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

PrimaryFeather commented 7 years ago

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.

neuronix commented 7 years ago

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! :)

PrimaryFeather commented 7 years ago

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. 😎