top of page
Search

Getting the most from Textures with SDFs

An old, tried, and tested method for creating crisper textures at lower resolution is alpha thresholding, but what is it? Why does it work better paired with an SDF? Why do we even care? Well,


In games we have a relatively restricted budget for textures still, and we should always use it as effectively as we can. For things like text, foliage and UI often a higher resolution is required, as it's much more obvious when those things are blurred or low resolution. Ideally, we need a simple, cheap method to keep these elements sharp when the player is close up without the expense of heavy textures maps.


This is where our method comes in. But first, some context:


What on god's green earth is a Signed Distance Field?


So! The "field" part tells us that we're working with- voxels, or volumetric data. The distance is what is stored in those voxels- in this case, the distance to the nearest surface. Distance fields return the shortest distance between a point and the nearest surface to that point, which we can see below in the example texture. For a distance field to be 'signed' we would have both positive and negative values assigned to the points in our distance field, with positive values being outside our shape or volume, negative being inside, and a value of 0 at the boundary between the two. SDFs are used in a huge variety of ways both in and outside of gamedev, but we typically see them in rendering with things like ambient occlusion, raymarching or raytracing.


Alpha Thresholding


Alpha Thresholding is a technique used since the dawn of time to separate values based on their current opacity. If we have a radial gradient going from 0 in the centre to 1 at the edge, we can clamp, or 'step' those values to separate regions. The value we use to separate these regions is called the threshold, and as it's usually the information stored in the alpha channel we use for this, the term becomes 'alpha thresholding'.


ree
threshold of 0.5


Using this in Unreal Engine


First we need to generate an SDF by taking the values both inside and outside the texture- the higher your starting resolution, the better the result when we downscale this later. We do it this way so that we have a readable result both inside and outside the supplied icon, for any transformations we may want to do later. Were we to only use the bottom distance that goes 'out', we would only be able to make the icon larger/bolder than it currently is, and so we need to combine it with an 'inward' distance.


ree

For Unreal, the texture compression should be set to 'Greyscale' for best results. As you can see the edges are a little too sharp, as there's no aliasing, but we can solve this by returning a smoothstep of x instead.


ree
float x = Texture2DSample(texObject, texObjectSampler, uv).r; return x >= 0.4;
ree
w/ smoothstep function

And just to prove that there's a point to all this here are two comparisons of a regular texture alongside one utilising the above method.


ree
Non SDF and SDF comparison at 512
ree
Non SDF and SDF comparison at 256
               float x = length(0.5-uv); return x >= 0.5; 

This is in effect the same as using the code above to return a perfect circle as we've done previously, or any other SDF functions for various shapes. The benefit to the above is we can of course use it with any texture without creating it with math first. The threshold we return can give us bolder or thinner text, saving time on creating or reworking additional fonts.


We can then use the generated distance field to power a range of effects with the resultant texture, much like any blending option you would find in Photoshop:


Outline, Glow & Drop Shadow

Other value bands can be selected to create a 'stroke' or outline at any postition on the icon, and we can expand this smoothstep min value to create more of a glow effect around the icon as well:


ree
ree

And then by using a second offset UV coordinate we can also have a drop shadow effect:


ree

One last thing we can do is take the feathered result and put it through a gradient map to get more a interesting color output.


ree

And that's more or less it. A brief overview of how to save on texture budget with some teeny tiny textures and a couple of lines of code. This is for sure not a new thing, but I hope this document in the context of Unreal engine helps someone out on their development journey.


Thanks for reading! 🦆




Additional resources: Valve's texture generation using this method -tried and tested since 2007

https://github.com/Chlumsky/msdfgen - cool Multi-Channel SDF for sharper corners


 
 
 

Comments


© Powered and secured by Wix

bottom of page