Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!
Пример: https://openglobus.org/examples/fonts/fonts.html
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, и я обязательно отвечу!
Желаю Вам хорошего настроения!
Полезные источники
https://github.com/Chlumsky/msdfgen - Описание основного метода рендеринга текста
https://github.com/soimy/msdf-bmfont-xml - Инструмент для создания атласа шрифтов
https://learnopengl.com/In-Practice/Text-Rendering - Основы рендеринга текста
https://github.com/openglobus/openglobus - Библиотека openglobus
https://github.com/openglobus/openglobus/blob/master/src/og/utils/FontAtlas.js - Загрузчик атласа шрифтов билиотеки openglobus