Рендеринг шрифтов для WebGL при помощи инстумента msdf-bmfont-xml и технологии MSDF

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Пример: https://openglobus.org/examples/fonts/fonts.html

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

Пример по ссылке: https://openglobus.org/examples/fonts/fonts.html
Пример по ссылке: https://openglobus.org/examples/fonts/fonts.html

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

В этой статье я хочу расскать, что из себя представляет атлас для хранения букв, и как отображать текст при помощи WebGL. 

Текстурный атлас msdf-bmfont-xml

Текстурный атлас шрифта, это текстура с сохраненными на ней картинками символов. Каждая картинка имеет соответствующие текстурные координаты. 

Текстура 512x512 для атласа шрифта Roboto-Regular
Текстура 512x512 для атласа шрифта Roboto-Regular

Для создания атласа я использую комманду:

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 для своего проекта. Этот подход существенно улучшил качество текстовых меток на карте.

Пример редактора планировщика маршрута БПЛА компании Microavia c использованием библиотеки Openglobus
Пример редактора планировщика маршрута БПЛА компании Microavia c использованием библиотеки Openglobus

Пишите в комментариях, чем вы пользуетесь, для рендеринга шрифтов, и как на ваш взгляд можно улучшить качество текстовых меток?

Если у вас возникнут вопросы по применению моей рекомендации можете задать их на 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

Источник: https://habr.com/ru/post/548088/


Интересные статьи

Интересные статьи

Мультимедиа — ужасный термин, особенно с этим проваливающимся в преисподнюю ааааа на конце. А придумать адекватную замену не получается, разве что какое-нибудь «аудиовизуальное многоборье», еще х...
Постановка задачи Для отображения иконок в приложении Xamarin.Forms можно использовать изображения в различных форматах, например png, svg или шрифты ttf. Чаще всего для добавления стандартных...
Возможное фото 10 нм IceLake. Источник Странные вещи творятся на процессорном рынке. Мировой лидер в лице фирмы Intel пятый год бьется в попытках перейти на 10 нм техпроцесс. Изначально заяв...
Предисловие После выхода последней игры из серии «Метро» я потратил несколько часов на изучение её внутренней работы и решил поделиться тем, что может показаться интересным с технологической т...
Если Вы используете в своих проектах инфоблоки 2.0 и таблицы InnoDB, то есть шанс в один прекрасный момент столкнуться с ошибкой MySQL «SQL Error (1118): Row size too large. The maximum row si...