Multi-channel signed distance field generator
#gamedev #c++ #cpp #application #devtools #tooling #sdf
|
5 anni fa | |
---|---|---|
bin | 6 anni fa | |
core | 5 anni fa | |
ext | 5 anni fa | |
freetype | 6 anni fa | |
include | 6 anni fa | |
lib | 6 anni fa | |
.gitignore | 6 anni fa | |
CHANGELOG.md | 5 anni fa | |
CMakeLists.txt | 8 anni fa | |
LICENSE.txt | 9 anni fa | |
Msdfgen.rc | 9 anni fa | |
Msdfgen.sln | 6 anni fa | |
Msdfgen.vcxproj | 5 anni fa | |
Msdfgen.vcxproj.filters | 5 anni fa | |
README.md | 5 anni fa | |
icon.ico | 9 anni fa | |
main.cpp | 5 anni fa | |
msdfgen-ext.h | 5 anni fa | |
msdfgen.h | 5 anni fa | |
resource.h | 9 anni fa |
This is a utility for generating signed distance fields from vector shapes and font glyphs, which serve as a texture representation that can be used in real-time graphics to efficiently reproduce said shapes. Although it can also be used to generate conventional signed distance fields best known from this Valve paper and pseudo-distance fields, its primary purpose is to generate multi-channel distance fields, using a method I have developed. Unlike monochrome distance fields, they have the ability to reproduce sharp corners almost perfectly by utilizing all three color channels.
The following comparison demonstrates the improvement in image quality.
The project can be used either as a library or as a console program. It is divided into two parts, core and extensions. The core module has no dependencies and only uses bare C++. It contains all key data structures and algorithms, which can be accessed through the msdfgen.h header. Extensions contain utilities for loading fonts and SVG files, as well as saving PNG images. Those are exposed by the msdfgen-ext.h header. This module uses FreeType, TinyXML2, and LodePNG.
Additionally, there is the main.cpp, which wraps the functionality into a comprehensive standalone console program. To start using the program immediately, there is a Windows binary available for download in the "Releases" section. To build the project, you may use the included Visual Studio solution or CMake script.
The standalone program is executed as
msdfgen.exe <mode> <input> <options>
where only the input specification is required.
Mode can be one of:
The input can be specified as one of:
The complete list of available options can be printed with -help. Some of the important ones are:
For example,
msdfgen.exe msdf -font C:\Windows\Fonts\arialbd.ttf 'M' -o msdf.png -size 32 32 -pxrange 4 -autoframe -testrender render.png 1024 1024
will take the glyph capital M from the Arial Bold typeface, generate a 32×32 multi-channel distance field with a 4 pixels wide distance range, store it into msdf.png, and create a test render of the glyph as render.png.
Note: Do not use -autoframe
to generate character maps! It is intended as a quick preview only.
If you choose to use this utility inside your own program, there are a few simple steps you need to perform
in order to generate a distance field. Please note that all classes and functions are in the msdfgen
namespace.
Shape
object. You can either load it via loadGlyph
or loadSvgShape
, or construct it manually.
It consists of closed contours, which in turn consist of edges. An edge is represented by a LinearEdge
, QuadraticEdge
,
or CubicEdge
. You can construct them from two endpoints and 0 to 2 Bézier control points.normalize
method and assign colors to edges if you need a multi-channel SDF.
This can be performed automatically using the edgeColoringSimple
heuristic, or manually by setting each edge's
color
member. Keep in mind that at least two color channels must be turned on in each edge, and iff two edges meet
at a sharp corner, they must only have one channel in common.generateSDF
, generatePseudoSDF
, or generateMSDF
to generate a distance field into a floating point
Bitmap
object. This can then be worked with further or saved to a file using saveBmp
, savePng
, or saveTiff
.renderSDF
. Consider calling simulate8bit
on the distance field beforehand to simulate the standard 8 bits/channel image format.Example:
#include "msdfgen.h"
#include "msdfgen-ext.h"
using namespace msdfgen;
int main() {
FreetypeHandle *ft = initializeFreetype();
if (ft) {
FontHandle *font = loadFont(ft, "C:\\Windows\\Fonts\\arialbd.ttf");
if (font) {
Shape shape;
if (loadGlyph(shape, font, 'A')) {
shape.normalize();
// max. angle
edgeColoringSimple(shape, 3.0);
// image width, height
Bitmap<float, 3> msdf(32, 32);
// range, scale, translation
generateMSDF(msdf, shape, 4.0, 1.0, Vector2(4.0, 4.0));
savePng(msdf, "output.png");
}
destroyFont(font);
}
deinitializeFreetype(ft);
}
return 0;
}
Using a multi-channel distance field generated by this program is similarly simple to how a monochrome distance field is used. The only additional operation is computing the median of the three channels inside the fragment shader, right after sampling the distance field. This signed distance value can then be used the same way as usual.
The following is an example GLSL fragment shader including anti-aliasing:
in vec2 pos;
out vec4 color;
uniform sampler2D msdf;
uniform float pxRange;
uniform vec4 bgColor;
uniform vec4 fgColor;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
void main() {
vec2 msdfUnit = pxRange/vec2(textureSize(msdf, 0));
vec3 sample = texture(msdf, pos).rgb;
float sigDist = median(sample.r, sample.g, sample.b) - 0.5;
sigDist *= dot(msdfUnit, 0.5/fwidth(pos));
float opacity = clamp(sigDist + 0.5, 0.0, 1.0);
color = mix(bgColor, fgColor, opacity);
}
Note: This is an example shader only and probably is not optimal for your use case! Please do not blindly copy & paste.
The text shape description has the following syntax.
{ <contour 1> } { <contour 2> }
#
can be used, which represents the first point.c
, m
, y
or w
) and/or one or two Bézier curve control points inside parentheses.
For example,
{ -1, -1; m; -1, +1; y; +1, +1; m; +1, -1; y; # }
would represent a square with magenta and yellow edges,
{ 0, 1; (+1.6, -0.8; -1.6, -0.8); # }
is a teardrop shape formed by a single cubic Bézier curve.