18/3/2021 Наконец-то была закончена интеграция инструмента msdf-bmfont-xml для библиотеки openglobus. Текстовые метки стали выглядеть гораздо красивее! Мне помог инструмент msdf-bmfont-xml для создания атласов шрифтов и рендеринга текстур для (multichannel signed distance fields) MSDF.

msdf-bmfont-xml предлагает широкие возможности для формирования текстурных атласов из шрифтов в формате ttf. В нашем случае, текстура атласа представляет из себя многоканальную карту расстояний (multichannel signed distance fields), которая позволяет отображать острые углы букв, в отличии от оригинального signed distance field.
В этой статье я хочу рассказать, что из себя представляет атлас для хранения букв, и как отображать текст при помощи WebGL.
Текстурный атлас msdf-bmfont-xml
Текстурный атлас шрифта, это текстура с сохраненными на ней картинками символов. Каждая картинка имеет соответствующие текстурные координаты.

Для создания атласа я использую команду:
msdf-bmfont.cmd - reuse -i .\charset.txt -m 512,512 -f json -o %1.png -s 32 -r 8 -p 1 -t msdf %1
где %1 — имя файла шрифта в формате ttf, charset.txt — это файл с набором символов для которых строится атлас, про остальные параметры можно узнать на официальной страничке репозитория msdf-bmfont-xml.
При успешном выполнении команды создаются несколько файлов, нас будут интересовать текстура атласа в формате png и описание атласа в формате json.
В полученном файле описания json информация по символам хранится в разделе chars. Например, символ ‘q’ на картинке атласа расположен в координатах x = 131, y = 356, width = 22, height = 32. т.е. координаты левого верхнего угла [131, 356] и правого нижнего соответственно [131+22, 356+32]. Таким образом, если размер текстуры атласа равен 512 на 512 пикселей, значит текстурные координат символа ‘q’ соответственно будут равны [131/512, 356/512 ] и [153/512, 388/512]. Если передать эти текстурные координаты в шейдер, который рисует прямоугольник, то мы увидим в этом прямоугольнике наш символ. Кроме того, у нас имеется ширина и высота символа, согласно этим данным мы устанавливаем размер прямоугольника, чтобы символ выглядел пропорционально правильным.
{
id: 113,
char: "q",
width: 22,
height: 32,
xoffset: -3,
yoffset: 11,
xadvance: 18,
x: 131,
y: 356,
...
}
Другими важными параметрами для позиционирования символа являются:
xoffsеt — Смещение символа по горизонтали
yoffset — Смещение символа по вертикали
xadvance — Ширина символа; расстояние от левой границы символа до начала следующего символа в строке.
А также id символа по которому можно идентифицировать символ в таблице горизонтальных кернингов. Кернинг — это расстояние между двумя специфическими символами.
Пример: изображения строки “Wg!” шрифт Arial
Параметры символов:
W: width: 37, height: 31, xoffset: -4, yoffset: 4, xadvance: 30
g: width: 23, height: 32, xoffset: -3, yoffset: 10, xadvance: 18
! : width: 11, height: 31, xoffset: -1, yoffset: 4, xadvance: 9
Началом координат является левый верхний угол, изначально символы располагаются на одной прямой относительно верхней (горизонтальной) зеленой линии и относительно левой (вертикальной) зеленой линии, и имеют смещение относительно друг друга по горизонтали согласно параметрам width.

Следующая картинка показывает смещение символов относительно горизонтальной (центральной) оси по вертикали, параметр yoffsset.

Голубыми линиями обозначен параметр xadvance (расстояние до следующего символа), также каждый символ смещен по горизонтали согласно параметру xoffset.

Текущий вариант можно считать готовым, однако для лучшего качества следует применить параметры для кернинга, т.е. смещение относительно друг друга двух специфических символов. В этом примере я не показываю кернинги; кернинг учитывается в параметре xoffset во время отображения текста.
Рендеринг текста MSDF
Отрисовка массива вершин символа производится методом gl.drawArrays, где исходным буфером является буфер массива вершин для атрибута a_vertices:
vec2 a_vertices = [0, 0, 0, -1, 1, -1, 1, -1, 1, 0, 0, 0]
Основные параметры символов предварительно нормализуются при построении атласа шрифта:
imageSize = 512; //размер текстуры атласа
nWidth = width / imageSize; //нормализованная ширина символа
nHeight = height / imageSize; //нормализованная высота глифа
nAdvance = xadvance / imageSize; //нормализованный размер глифа до следующего символа в строке
nXOffset = xoffset / imageSize; //нормализованное смещение по горизонтали
nYOffset = 1.0 - yoffset / imageSize; //нормализованное смещение по вертикали
Шейдер GLSL
Исходник: https://github.com/openglobus/openglobus/blob/master/src/og/shaders/label.js
// Vertex shader:
// ...
vec2 v = screenPos + (a_vertices * a_gliphParam.xy + a_gliphParam.zw + vec2(advanceOffset, 0.0)) * a_size;
// Где:
// screenPos - экранные координаты строки
// a_vertices - Координаты вершин
// a_gliphParam - вектор с метриками символа, где:
// x - nWidth, y - nHeight, z - nXOffset, w - nYOffset
// advanceOffset - сумарное смещение по параметру nAdvance, каждого последующего символа в строке
// a_size - экранные размеры строки в пикселях
// Fragment shader:
// ...
const float imageSize = 512.0;
const float distanceRange = 8.0;
layout(location = 0) out vec4 outScreen;
float median(float r, float g, float b) {
return max(min(r, g), min(max(r, g), b));
}
float getDistance() {
vec3 msdf = texture(fontTexture, v_uv).rgb;
return median(msdf.r, msdf.g, msdf.b);
}
void main () {
vec2 dxdy = fwidth(v_uv) * vec2(imageSize);
float dist = getDistance() + min(v_weight, 0.5 – 1.0 / DIST_RANGE) - 0.5;
float opacity = clamp(dist * distanceRange / length(dxdy) + 0.5, 0.0, 1.0);
outScreen = vec4(v_rgba.rgb, opacity * v_rgba.a);
}
// Где:
// fontTexture - текстура атласа шрифтов
// v_weight - ширина символа от 0 до 1, используется для окантовки, по умолчанию равен 0.
// Окантовка рисуется ПЕРВЫМ проходом с заданным v_weight.
// v_uv - текстурные координаты символа в атласе шрифтов
// v_rgba - цвет символа, или окантовки
// ...
Надеюсь, что я достаточно понятно объяснил, как работать с атласами шрифтов и как я использую msdf-bmfont-xml для своего проекта. Этот подход существенно улучшил качество текстовых меток на карте.

Пишите в комментариях, чем вы пользуетесь, для рендеринга шрифтов, и как на ваш взгляд можно улучшить качество текстовых меток?
Если у вас возникнут вопросы по применению моей рекомендации можете задать их на openglobus форуме https://groups.google.com/forum/#!forum/openglobus, и я обязательно отвечу!
Желаю Вам хорошего настроения!