As is the rule in WebGL, something that looks as if it needs to be easy is definitely fairly difficult. Drawing strains, debugging shaders, textual content rendering… they’re all rattling arduous to do properly in WebGL.
Isn’t that bizarre? WebGL does not have a built-in perform for rendering textual content. Though textual content looks as if probably the most primary of functionalities. When it comes down to truly rendering it, issues get difficult. How do you account for the immense quantity of glyphs for each language? How do you’re employed with fixed-width, or proportional-width fonts? What do you do when textual content must be rendered top-to-bottom, left-to-right, or right-to-left? Mathematical equations, diagrams, sheet music?
Abruptly it begins to make sense why textual content rendering has no place in a low-level graphics API like WebGL. Textual content rendering is a fancy downside with a number of nuances. If we need to render textual content, we have to get inventive. Fortuitously, a number of good of us already got here up with a variety of methods for all our WebGL textual content wants.
Desk of Contents
A fast be aware on textual content exterior of WebGL
Though this text is all about textual content inside WebGL, the very first thing it is best to take into account is whether or not you may get away with utilizing HMTL textual content or canvas overlayed on prime of your WebGL canvas. The textual content cannot be occluded with the 3D geometry as an overlay, however you may get styling and accessibility out of the field. That is all you want in a number of circumstances.
One of many frequent methods to render textual content is to construct the glyphs with a collection of triangles, very similar to an everyday mannequin. In spite of everything, rendering factors, strains and triangles are a energy of WebGL.
When making a string, every glyph is made by studying the triangles from a font file of triangulated factors. From there, you could possibly extrude the glyph to make it 3D, or scale the glyphs by way of matrix operations.
Common font illustration (left) and font geometry (proper)
Font geometry works greatest for a small quantity of textual content. That’s as a result of every glyph accommodates many triangles, which causes drawing to turn out to be problematic.
Rendering this precise paragraph you might be studying proper now with font geometry creates 185,084 triangles and 555,252 vertices. That is simply 259 letters. Write the entire article utilizing a font geometry and your pc fan may as properly turn out to be an airplane turbine.
Though the variety of triangles varies by the precision of the triangulation and the typeface in use, rendering a number of textual content will in all probability at all times be a bottleneck when working with font geometry.
The best way to create a font geometry from a font file
If it have been as simple as selecting the font you need and rendering the textual content. I would not be writing this text. Common font codecs outline glyphs with Bezier curves. On the flip aspect, drawing these in WebGL is extraordinarily costly on the CPU and can be difficult to do. If we need to render textual content, we have to create triangles (triangulation) out of Bezier curves.
I’ve discovered just a few triangulation strategies, however not at all are any of them excellent they usually might not work with each font. However no less than they will get you began for triangulating your personal typefaces.
Methodology 1: Automated and straightforward
In case you are utilizing ThreeJS, you go your typeface by way of FaceType.js to learn the parametric curves out of your font file and put them right into a .json file. The font geometry options in ThreeJS maintain triangulating the factors for you:
const geometry = new THREE.FontGeometry(“Good day There”, )
Alternatively, if you’re not utilizing ThreeJS and needn’t have real-time triangulation. You could possibly save your self the ache of a guide course of through the use of ThreeJS to triangulate the font for you. Then you may extract the vertices and indices from the geometry, and cargo them in your WebGL utility of alternative.
Methodology 2: Handbook and painful
The guide choice for triangulating a font file is extraordinarily difficult and convoluted, no less than initially. It will require an entire article simply to clarify it intimately. That stated, we’ll rapidly go over the steps of a primary implementation I grabbed from StackOverflow.
The implementation mainly breaks down like this:
Add OpenType.js and Earcut.js to your undertaking.
Get Bezier curves out of your .tff font file utilizing OpenType.js.
Convert Bezier curves into closed shapes and type them by descending space.
Decide the indices for the holes by determining which shapes are inside different shapes.
Ship all the factors to Earcut with the opening indices as a second parameter.
Use Earcut’s end result because the indices to your geometry.
Yeah, it is quite a bit. And this implementation might not work for all typefaces. It’s going to get you began nonetheless.
Utilizing textual content geometries in ThreeJS
Fortunately, ThreeJS helps textual content geometries out of the field. Give it a .json of your favourite font’s Bezier curves and ThreeJS takes care of triangulating the vertices for you in runtime.
var loader = new THREE.FontLoader();
var textual content = “Good day World”
var loader = new THREE.FontLoader();
loader.load(‘fonts/helvetiker_regular.typeface.json’, perform (helvetiker)
font = helvetiker;
var geometry = new THREE.TextGeometry(textual content,
It’s simply extruded to create 3D strings.
Scaling is made simpler with matrix operations.
It offers nice high quality relying on the quantity of triangles used.
This does not scale properly with massive quantities of textual content because of the excessive triangle rely. Since every character is outlined by a number of triangles, even rendering one thing as transient as “Good day World” leads to 7,396 triangles and 22,188 vertices.
This does not lend itself to frequent textual content results.
Anti-aliasing is dependent upon your post-processing aliasing or your browser default.
Scaling issues too large may present the triangles.
Demo: Fading Letters
Within the following demo, I took benefit of how simple it’s to create 3D textual content utilizing font geometries. Inside a vertex shader, the extrusion is elevated and decreased as time goes on. Pair that with fog and normal materials and also you get these ghostly letters coming out and in of the void.
Discover how with a low quantity of letters the quantity of triangles is already within the tens of 1000’s!
Textual content (and canvas) textures
Making textual content textures might be the only and oldest means to attract textual content in WebGL. Open up Photoshop or another raster graphics editor, draw a picture with some textual content on it, then render these textures onto a quad and you might be executed!
Alternatively, you could possibly use the canvas to create the textures on demand at runtime. You’re in a position to render the canvas as a texture onto a quad as properly.
Apart for being the least difficult strategy of the bunch. Textual content textures and canvas textures take pleasure in solely wanted one quad per texture, or given piece of textual content. In the event you actually needed to, you could possibly write your entire British Encyclopedia on a single texture. That means, you solely should render a single quad, six vertices and two faces. In fact, you’d do it in a scale, however the concept nonetheless stays: You possibly can batch a number of glyphs into identical quad. Each textual content and canvas textures expertise have points with scaling, significantly when working with a number of textual content.
For textual content textures, the person has to obtain all of the textures that make up the textual content, then hold them in reminiscence. For canvas textures, the person does not should obtain something — however now the person’s pc has to do all of the rasterizing at runtime, and it’s good to hold monitor of the place each phrase is situated within the canvas. Plus, updating an enormous canvas will be actually gradual.
The best way to create and use a textual content texture
Textual content textures do not have something fancy occurring for them. Open up your favourite raster graphics editor, draw some textual content on the canvas and export it as a picture. Then you may load it as a texture, and map it on a aircraft:
// Load texture
let texture = ;
const geometry = new THREE.PlaneBufferGeometry();
const materials new THREE.MeshBasicMaterial(map: texture);
In case your WebGL app has a number of textual content, downloading an enormous sprite sheet of textual content won’t be excellent, particularly for customers on gradual connections. To keep away from the obtain time, you may rasterize issues on demand utilizing an offscreen canvas then pattern that canvas as a texture.
Let’s commerce obtain time for efficiency since rasterizing a number of textual content takes greater than a second.
perform createTextCanvas(string, parameters = )
let texture = new THREE.Texture(createTextCanvas(“That is textual content”));
Now you should use the feel on a aircraft, just like the earlier snippet. Or you could possibly create a sprite as an alternative.
In its place, you could possibly use extra environment friendly libraries to create texture or sprites, like three-text2d or three-spritetext. And if you’d like textual content multi-line textual content, it is best to try this superb tutorial.
This offers nice 1-to-1 high quality with static textual content.
There’s a low vertex/face rely. Every string can use as little as six vertices and two faces.
It’s simple to implement texture on a quad.
It’s pretty trivial so as to add results, like borders and glows, utilizing canvas or a graphics editor.
Canvas makes it simple to create multi-line textual content.
Seems to be blurry if scaled, rotated or remodeled after rasterizing.
On-non retina, textual content appears to be like crunchy.
You need to rasterize all of the strings used. Plenty of strings means a number of information to obtain.
On-demand rasterizing with canvas will be gradual when you hold consistently updating the canvas.
Demo: Canvas texture
Canvas textures work properly with a restricted quantity of textual content that doesn’t change typically. So I constructed a easy wall of textual content with the quads re-using the identical texture.
Each font geometries and textual content textures expertise the identical issues dealing with a number of textual content. Having a million vertices per piece of textual content is tremendous inefficient, and creating one texture per piece of textual content does not actually scale.
Bitmap fonts remedy this problem by rasterizing all distinctive glyphs right into a single texture, known as a texture atlas. This implies you may assemble any given string at runtime by making a quad for every glyph and sampling the part of the feel atlas.
This implies customers solely should obtain and use a single texture for all the textual content. It additionally means you solely must render as little as one quad per glyph:
A visible of bitmap font sampling
Rendering this complete article could be roughly 117,272 vertices and 58,636 triangles. That is three.1 occasions extra environment friendly in comparison with a font geometry rendering only a single paragraph. That a large enchancment!
As a result of bitmap fonts rasterize the glyph right into a texture, they undergo from the identical downside as common photos. Zoom in or scale and also you begin seeing a pixelated and blurry mess. If you would like textual content at a unique measurement, it is best to ship a secondary bitmap with the glyphs on that particular measurement. Or you could possibly use a Signed Distance Discipline (SDF) which we’ll cowl within the subsequent part.
The best way to create bitmap fonts
There are a number of instruments to generate bitmaps. Listed here are a number of the extra related choices on the market:
Angelcode’s bmfont – That is by the creators of the bitmap format.
Hiero – It is a Java open-source instrument. It’s similar to Anglecode’s bmfont, nevertheless it means that you can add textual content results.
Glyphs Designer – It is a paid MacOS app.
ShoeBox – That is an instrument for coping with sprites, together with bitmap fonts.
We’ll use Anglecode’s bmfont for our instance as a result of I believe it is the best one to get began. On the backside of this part, you’ll find different instruments when you suppose it lacks the performance you might be on the lookout for.
If you open the app, you may see a display screen filled with letters that you could choose to make use of.The great factor about that is that you’ll be able to seize simply the glyphs you want as an alternative of sending Greek symbols.
The app’s sidebar means that you can select and choose teams of glyphs.
The BmFont utility
Able to export? Go to Choices → Save bitmap as. And executed!
However we’re getting somewhat forward of ourselves. Earlier than exporting, there are just a few necessary settings it is best to verify.
Export and Font Choice settingsFont settings: This allow you to select the font and measurement you need to use. A very powerful merchandise right here is “Match char peak.” By default, the app’s “measurement” choice makes use of pixels as an alternative of factors. You will see a considerable distinction between your graphics editor’s font measurement and the font measurement that’s generated. Choose the “Match char peak” choices if you’d like your glyphs to make sense.
Export settings: For the export, ensure the feel measurement is an influence of two (e.g. 16×16, 32×32, 64×64, and many others.). Then you’ll be able to make the most of “Linear Mipmap linear” filtering, if wanted.
On the backside of the settings, you may see the “file format” part. Selecting both choice right here is okay so long as you may learn the file and create the glyphs.
In case you are on the lookout for the smallest file measurement. I ran a extremely non-scientific take a look at the place I created a bitmap of all lowecase and uppercase Latin characters and in contrast every choice. For Font Descriptors, probably the most environment friendly format is Binary.
Font Descriptor Format
Uncooked Textual content
PNG is the smallest file measurement for Textual content Texture.
In fact, it is somewhat extra difficult than simply file sizes. To get a greater concept of which choice to make use of, it is best to look into parsing time and run-time efficiency. If you need to know the professionals and cons of every codecs, try this dialogue.
The best way to use bitmap fonts
Creating bitmap font geometry is a little more concerned than simply utilizing a texture as a result of we’ve got to assemble the string ourselves. Every glyph has its personal peak and width, and samples a unique part of the feel. We’ve got to create a quad for every glyph on our string so we can provide them the proper UVs to pattern it is glyph.
You should use three-bmfont-text in ThreeJS to create strings utilizing bitmaps, SDFs and MSDFs. It takes care of multi-line textual content, and batching all glyphs onto a single geometry. Observe that it must be put in in a undertaking from npm.
var createGeometry = require(‘three-bmfont-text’)
var loadFont = require(‘load-bmfont’)
loadFont(‘fonts/Arial.fnt’, perform(err, font) )
Relying every time your textual content is drawn as as full black or full white, use the invert choice.
It’s quick and easy to render.
It’s a 1:1 ratio and determination unbiased.
It might probably render any string, given the glyphs.
It’s good for many textual content that should change typically.
It’s works extraordinarily properly with a restricted variety of glyphs.
It’s contains help for issues like kerning, line peak and word-wrapping at run-time.
It solely accepts a restricted set of characters and kinds.
It requires pre-rasterizing glyphs and further bin packing for optimum utilization.
It’s blurry and pixelated at massive scales, and will also be rotated or remodeled.
There’s just one quad per rendered glyph.
Interactive Demo: The Shining Credit
Raster bitmap fonts work nice for film credit as a result of we solely want just a few sizes and kinds. The disadvantage is that the textual content isn’t nice with responsive designs as a result of it’ll look blurry and pixelated at bigger sizes.
For the mouse impact, I’m making calculations by mapping the mouse place to the dimensions of the view then calculating the space from the mouse to the textual content place. I’m additionally rotating the textual content when it hits particular factors on the z-axis and y-axis.
Signed distance fields
Very like bitmap fonts, signed distance area (SDF) fonts are additionally a texture atlas. Distinctive glyphs are batch right into a single “texture atlas” that may create any string at runtime.
However as an alternative of storing the rasterized glyph on the feel the way in which bitmap fonts do, the glyph’s SDF is generated and saved as an alternative which permits for a excessive decision form from a low decision picture.
Like polygonal meshes (font geometries), SDFs characterize shapes. Every pixel on an SDF shops the space to the closest floor. The signal signifies every time the pixel is inside or exterior the form. If the signal is unfavorable, then the pixel is inside; if it’s constructive, then the pixel is exterior. This video illustrates the idea properly.
SDFs are additionally generally used for raytracing and volumetric rendering.
As a result of an SDF shops distance in every pixel, the uncooked end result appears to be like like a blurry model of the unique form. To output the arduous form you’ll must alpha take a look at it at zero.5 which is the border of the glyph. Check out how the SDF of the letter “A” compares to it is common raster picture:
Raster textual content beside of a uncooked and an alpha examined SDF
As I discussed earlier, the massive advantage of SDFs is having the ability to render excessive decision shapes from low decision SDF. This implies you may create a 16pt font SDF and scale the textual content as much as 100pt or extra with out dropping a lot crispness.
SDFs are good at scaling as a result of you may virtually completely reconstruct the space with bilinear interpolation, which is a elaborate means of claiming we will get values between two factors. On this case, bilinear interpolating between two pixels on an everyday bitmap font offers us the in-between colour, leading to a linear blur.
On an SDF, bilinear interpolating between two pixels offers the in-between distance to the closest edge. Since these two pixel distances are much like start with, the ensuing worth does not lose a lot details about the glyph. This additionally means the larger the SDF, the extra correct and fewer info is misplaced.
Nonetheless, this course of comes with a caveat. If the speed change between pixels just isn’t linear — like within the case of sharp corners — bilinear interpolation offers out an inaccurate worth, leading to chipped or rounded corners when scaling an SDF a lot increased than its authentic measurement.
SDF rounded corners
Other than bumping the feel aspect, the one actual answer is to make use of multi-channel SDFs, which is what we’ll cowl within the subsequent part.
If you wish to take a deeper dive into the science behind SDFs, try the Chris Inexperienced’s Grasp’s thesis (PDF) on the subject.
They keep crispness, even when rotated, scaled or remodeled.
They are perfect for dynamic textual content.
They supply good high quality to the dimensions ratio. A single texture can be utilized to render tiny and large font sizes with out dropping a lot high quality.
They’ve a low vertex rely of solely 4 vertices per glyph.
Antialiasing is cheap as is making borders, drop shadows and every kind of results with alpha testing.
They’re smaller than MSDFs (which we’ll see in a bit).
The may end up in rounded or chipped corners when the feel is sampled past its decision. (Once more, we’ll see how MSDFs can forestall that.)
They’re ineffective at tiny font sizes.
They will solely be used with monochrome glyphs.
Multi-channel signed distance fields
Multi-channel signed distance area (MSDF) fonts is a little bit of a mouthful and a reasonably latest variation on SDFs that’s able to producing near-perfect sharp corners through the use of all three colour channels. They do look fairly thoughts blowing at first however do not let that put you off as a result of they’re simple to make use of than they seem.
A multi-channel signed distance area file can look somewhat spooky at first.
Utilizing all three colour channels does lead to a heavier picture, however that’s what offers MSDFs a much better quality-to-space ratio than common SDFs. The next picture reveals the distinction between an SDF and an MSDF for a font that has been scaled as much as 50px.
The SDF font leads to rounded corners, even at 1x zoom, whereas the MSDF font retains sharp edges, even at 5x zoom.
Like an everyday SDF, an MSDF shops the space to the closest edge however adjustments the colour channels every time it finds a pointy nook. We get the form by drawing the place two colour channels or extra agree. Though there is a bit extra approach concerned. Try the README for this MSDF generator for a extra thorough clarification.
They help a better high quality and area ratio than SDFs. and are sometimes the higher alternative.
They keep sharp corners when scaled.
They might comprise small artifacts however these will be averted by bumping up the feel measurement of the glyph.
They requires filtering the median of the three values at runtime which is a bit costly.
They’re solely appropriate with monochrome glyphs.
The best way to create MSDF fonts
The quickest technique to create an MSDF font is to make use of the msdf-bmfont-web instrument. It has many of the related customization choices and it will get the job executed in seconds, all within the browser. Alternatively, there are a selection of Google Fonts which have already been transformed into MSDF by the oldsters at A-Body.
In case you are additionally trying to generate SDFs or your typeface, it requires some particular tweaking due to some problematic glyphs. The msdf-bmfont-xml CLI offers you a variety of choices, with out making issues overly complicated. Let’s check out how you’d use it.
First, you may want to put in globally it from npm:
npm set up msdf-bmfont-xml -g
Subsequent, give it a .ttf font file along with your choices:
msdf-bmfont ./Open-Sans-Black.ttf –output-type json –font-size 76 –texture-size 512,512
These choices are value digging right into a bit. Whereas msdf-bmfont-xml offers a number of choices to fine-tune your font, there are actually only a few choices you may in all probability must appropriately generate an MSDF:
There are different instruments that assist generate MSDF and SDF fonts:
msdf-bmfont-web: An internet instrument to create MSDFs (however not SDFs) rapidly and simply
msdf-bmfont: A Node instrument utilizing Cairo and node-canvas
msdfgen: The unique command line instrument that every one different MSDF instruments are based mostly from
Hiero: A instrument for producing each bitmaps and SDF fonts
The best way to use SDF and MSDF fonts
As a result of SDF and MSDF fonts are additionally glyph atlases, we will use three-bmfont-text like we did for bitmap fonts. The one distinction is that we've got to get the glyph our of the space fields with a fraction shader.
Right here’s how that works for SDF fonts. Since our distance area has a worth better than .5 exterior our glyph and fewer than zero.5 inside our glyph, we have to alpha take a look at in a fraction shader on every pixel to ensure we solely render pixels with a distance lower than zero.5, rendering simply the within of the glyphs.
const fragmentShader = `
uniform vec3 colour;
uniform sampler2D map;
various vec2 vUv;
const vertexShader = `
various vec2 vUv;
let materials = new THREE.ShaderMaterial(
Equally, we will import the font from three-bmfont-text which comes with antialiasing out of the field. Then we will use it immediately on a RawShaderMaterial:
let SDFShader = require('three-bmfont-text/shaders/sdf');
let materials = new THREE.RawShaderMaterial(MSDFShader());
MSDF fonts are somewhat completely different. They recreate sharp corners by the intersections of two colour channels. Two or extra colour channels should agree on it. Earlier than doing any alpha texting, we have to get the median of the three colour channels to see the place they agree:
const fragmentShader = `
uniform vec3 colour;
uniform sampler2D map;
various vec2 vUv;
float median(float r, float g, float b)
vec4 texColor = texture2D(map, vUv);
// Solely render the within of the glyph.
float sigDist = median(texColor.r, texColor.g, texColor.b) - zero.5;
float alpha = step(zero.5, sigDist);
gl_FragColor = vec4(colour, alpha);
if (gl_FragColor.a < zero.0001) discard; `; const vertexShader = ` various vec2 vUv; void predominant `; let materials = new THREE.ShaderMaterial( fragmentShader, vertexShader, uniforms: )
Once more, we will additionally import from three-bmfont-text utilizing its MSDFShader which additionally comes with antialiasing out of the field. Then we will use it immediately on a RawShaderMaterial:
let MSDFShader = require('three-bmfont-text/shaders/msdf');
let materials = new THREE.RawShaderMaterial(MSDFShader());
Demo: Star Wars intro
The Star Wars drawl intro is an efficient instance the place MSDF and SDF fonts work properly as a result of the impact wants the textual content to return in a number of sizes. We will use a single MSDF and the textual content at all times appears to be like sharp! Though, sadly, three-bm-font doesn’t help justified textual content but. Making use of left justification would make for a extra balanced presentation.
For the sunshine saber impact, I’m raycasting an invisible aircraft the dimensions of the aircraft, drawing onto a canvas that’s the identical measurement, and sampling that canvas by mapping the scene place to the feel coordinates.
Bonus tip: Producing 3D textual content with peak maps
Other than font geometries, all of the methods we’ve coated generate strings or glyphs on a single quad. If you wish to construct 3D geometries out of a flat texture, your best option is to make use of a peak map.
A peak map is a method the place the geometry peak is bumped up utilizing a texture. That is usually used to generate mountains in video games, nevertheless it seems to be helpful rendering textual content as properly.
The one caveat is that you just’ll want a number of faces for the textual content to look easy.
Completely different conditions name for various methods. Nothing we noticed here's a silver bullet they usually all have their benefits and drawbacks.
There are a number of instruments and libraries on the market to assist profit from WebGL textual content, most of which really originate from exterior WebGL. If you wish to continue to learn, I extremely advocate you transcend WebGL and take a look at a few of these hyperlinks: