I’m really new to hlsl and I’ve been experimenting with it lately. I use a custom pixel font in my projects with multiple outlines, as seen below. I’m curious if it’s possible to recreate these examples using a hlsl pixel shader on a single spritebatch.drawstring result. The outlines are now drawn using many spritebatch.drawstring calls which is not so optimal. A shader would probably be faster, especially in the second example.
Some problems:
I saw that each character is drawn as its own texture. I would have to draw pixels outside the texture box of characters. I don’t know if this is possible.
The texture size of each string/character would be needed to check the color of neighbouring pixels. (to create the outline(s), as far as I know)
I need to know how many pixels actually represent one “font pixel”.
Some outlines need to be layered on top of others (see example 2).
Is a shader like this possible?
Thanks in advance.
In my opinion it is better to write own font preprocessing or find font with outline than doing this realtime. However shader is definitely possible, basically what you would be doing is convolution (same approach that is used for instance to do blur effect).
Edit: Quick search shows that there were few sprite font preprocessors for XNA that added outline to the font, however I didn’t found any that I would think would still work or was downloadable from reliable source.
I’ve looked at the demo. Thanks for sharing, I learned something new today.
Are SDF fonts not overkill for my effect?
I would have to create specific .png images and write my own .txt files for the font because of the double outlines/shadows. So I can’t just generate it with a tool, or is there another method/way?
imho SDF fonts are not only overkill, but you won’t get the effect you’re trying to get.
SDF looks absolutely stunning but you have no control over how the shadow will look other than a float value used to decide the width of the border (but not the shape, in example you could get the border of the letter R ok, but get the border of the letter O slightly rounded)
While this is usually desirable for fonts, you’re probably going for a ‘pixel look’ font, and SDF borders may not work for that.
While I love distance field fonts, even their best variant still have issues when rendering at very small sizes. It is great for use in world space text like nameplates and dynamic assets, but for GUI, especially gui with small/fine fonts it is still quite problematic.
Btw that’s case even for superior multi channel signed distance field fonts.
Also sorry, but that example actually starts to fall apart at large sizes as well.
There is example with mutli channel signed fonts: https://github.com/roy-t/MSDF however, while providing better quality, it creates interesting “font background” artifact at lower sizes. So I agree with you, I think it is overkill for your case and if you really want to do it realtime and you are not afraid about performance (honestly unless rest of the game is performance hungry or you draw ridiculous amount of text with overdraw) then use normal sprite fonts and drag it through one pass 3x3 kernel, provided you need just single pixel outline (which can be ofc upscaled up for pixel art look).
I’m just about to head to bed but I figured I’d post this and let you play with it. I’ve never tried this with text before… lol. Anyway, I created this shader for putting an outline around sprites. In theory, it should work just fine on text. If you want to add shadow, you’d have to adapt it.
Pass in as parameters the colour of the outline (xOutlineColour) and the size of the texture it will process (xTextureSize). I’m actually not sure what this will be for text rendering. You might actually have to render all your text to a RenderTarget layer and then run this over it? Is this the problem you were running into in your original post?
Thanks for the shader, but the problem is that each character in a drawstring operation gets drawn in a separate draw rectangle. An example of a shader that transforms all transparant pixels to red pixels can be seen below to show this.
I’m also thinking about something like this. I can’t seem to create the desired effect with just a drawstring call and a hlsl shader.
It sounds like it’s a performance trade-off between multiple sprite batch renders per text to achieve your effect, or rendering to a full screen render target to do some post processing.
I’m curious as to which ends up performing better!
@Jelle If you rendered first all the outlines and afterwards the text, would you achieve the desired effect? If so, that’d probably be only 2 drawcalls with SpriteBatch.Deferred
The number of drawstrings is probably not relevant, only the stuff put inside the spritebatch, but I can’t confirm that because DrawString was the first XNA function I replaced with my custom renderer. Hate at first sight
@Jelle If you rendered first all the outlines and afterwards the text, would you achieve the desired effect? If so, that’d probably be only 2 drawcalls with SpriteBatch.Deferred
Probably. I’m still looking for an optimal solution
I’m using a full screen rendertarget like you said. I draw the text to the rendertarget and apply a custom hlsl shader to it.
Haven’t tested the fps on both methods yet.
EDIT, full shader:
I will test the shader against the drawstring method tomorrow. Feel free to ask for the shader if you want, I don’t know if it’s very efficient though.
EDIT 2: Fast test showed that the hlsl shader & full screen rendertarget method is faster. I’ll post more details later.
I used a small resolution for the shader tests (1100 x 900) and nothing else gets drawn to the screen except the font, shader and tile texture. I usually get around 500 fps when the screen is full of tiles (~33k) on a 3440 x 1440 resolution though.