Продолжаем серию статей о разработке мобильных игр.
В этой статье я расскажу вам, как визуализировать текст UTF-8 с помощью растровых шрифтов SDF, как создавать эти шрифты и как использовать эту технику для качественного рендеринга иконок.
Содержание
Часть 1. Мобильный кроссплатформенный движок Часть 2. Отрисовка текста UTF-8 с использованием шрифта SDF. Часть 3. Рендеринг капли с прозрачностью и отражениямиСДФ ( Поле расстояния со знаком ) — изображение в оттенках серого, созданное на основе высококонтрастного черно-белого изображения, в котором уровень серого указывает расстояние до ближайшей границы контрастности.
Звучит запутанно, но на самом деле все очень просто.
Сам шрифт SDF выглядит так:
Давайте возьмем это изображение и изменим его уровни в Photoshop или любом другом графическом редакторе.
Уже выглядит лучше! У нас четкий шрифт со сглаживанием по краям.
Мы также можем выбрать смелый или тонкий стиль.
Но, к сожалению, вы не сможете получить курсив.
.
Главное преимущество SDF — возможность увеличивать шрифт без заметных артефактов.
Рекомендую прочитать больше о технологии SDF. здесь .
Как создать шрифт SDF? Прежде всего, вам нужно создать самый обычный черно-белый растровый шрифт. Вы можете сделать это старым добрым способом БМФонт или в УБФГ .
Для получения хороших результатов создайте шрифт размером 400 пикселей без сглаживания, с отступами 45x45x45x45 и размером изображения 4096x4096. Для таких размеров советую отключить Merging, т.к.
скорее всего UBGF зависнет. ?Экспортируем изображение в PNG без прозрачности, а формат описания желательно выбрать BMFont (для большей совместимости).
Далее нам понадобится ИзображениеМагия и следующая команда:
конвертировать шрифт.png -filter Jinc ( +clone -negate -morphology Расстояние Евклидово -уровень 50%, -50% ) -морфология Расстояние Евклидово -compose Plus -composite -level 43%,57% -resize 12,5%font.pngНа выходе мы получим изображение размером 512х512, что в итоге даст нам очень хороший результат. Из файла описания нам нужно будет извлечь символы в Юникоде и их положение/размер (не забудьте разделить координаты на 8, так как мы уменьшили изображение).
Какие именно символы нужно экспортировать я расскажу ниже в разделе про UTF-8. Подождите, у UBFG есть встроенное поле расстояний! Да, у меня есть.
Но результат заметно хуже.
Возможно, авторы UBFG исправят это в обновлениях.
Шейдеры для рендеринга текста Вершинный шейдер чтобы напечатать каждую букву, символ за символом:
ДЕФПРЕЦИЗИЯ необходим для OpenGL ES. В шнуры[1] И шнуры[0] передаем положение и масштаб символа на экране.#ifdef DEFPRECISION precision mediump float; #endif attribute mediump vec2 Vertex; uniform highp mat4 MVP; uniform mediump vec2 cords[4]; varying mediump vec2 outTexCord; void main(){ outTexCord=Vertex*cords[3]+cords[2]; gl_Position = MVP * vec4(Vertex*cords[1]+cords[0], 0.0, 1.0); }
И в шнуры[2] И шнуры[3] — координаты символа на текстуре шрифта.
Фрагментный шейдер #ifdef DEFPRECISION
precision mediump float;
#endif
varying mediump vec2 outTexCord;
uniform lowp sampler2D tex0;
uniform mediump vec4 color;
uniform mediump vec2 params;
void main(void){
float tx=texture2D(tex0, outTexCord).
r;
float a=min((tx-params.x)*params.y, 1.0);
gl_FragColor=vec4(color.rgb,a*color.a);
}
В цвет передать цвет и прозрачность буквы.
И через параметры настроить толщину и сглаживание краев шрифта.
Если вы можете настроить толщину шрифта, то вы также сможете отобразить рамку! Шейдер фрагмента текста с рамкой : #ifdef DEFPRECISION
precision mediump float;
#endif
varying mediump vec2 outTexCord;
uniform lowp sampler2D tex0;
uniform mediump vec4 color;
uniform mediump vec4 params;
uniform mediump vec3 borderColor;
void main(void){
float tx=texture2D(tex0, outTexCord).
r;
float b=min((tx-params.z)*params.w, 1.0);
float a=clamp((tx-params.x)*params.y, 0.0, 1.0);
gl_FragColor=vec4(borderColor+(color.rgb-borderColor)*a, b*color.a);
}
Дополнительно передаем толщину, сглаживая до params.zw и цвет рамки в цвет границы .
Результат должен выглядеть так:
Чтобы получить красивые края как для маленького, так и для большого размера текста, вам необходимо выбрать разные параметры контраста/сглаживания ( параметры ) для мелкого шрифта и для крупного шрифта.
Затем интерполируйте их до текущего размера.
По моему мнению, для небольшие размеры подходит хорошо:
- более смелый стиль
- более гладкие края
- граница минимальна и размыта, чтобы не рябило
- более тонкий стиль шрифта
- края очень острые
- бордюр больше и острее
Плоские иконки стали довольно популярными в современном дизайне.
Бесплатные векторные иконки полный полный .
Все, что нам нужно сделать, это собрать черно-белый текстурный атлас из необходимых иконок и таким же образом запустить его через ImageMagick! В результате мы можем хранить значки с довольно низким разрешением, но получать хорошие результаты при масштабировании и повороте значков! В качестве бонуса вы можете легко добавить к значкам градиент. Для этого вам просто нужно повесить цвета на вершины, а градиент мы получим интерполируя между точками.
Радиальный градиент придется делать попиксельно во фрагментном шейдере.
UTF-8 В современных проектах уже никто не использует однобайтовые кодировки.
Все перешли на UTF-8, wchar, unicode. Например, мне удобно работать со строками в кодировке UTF-8 char*.
UTF-8 легко декодируется в Юникод и идеально сочетается с Java/String и NSString. Функция преобразования UTF-8 в Unicode: static inline unsigned int UTF2Unicode(const unsigned char *txt, unsigned int &i){
unsigned int a=txt[i++];
if((a&0x80)==0)return a;
if((a&0xE0)==0xC0){
a=(a&0x1F)<<6;
a|=txt[i++]&0x3F;
}else if((a&0xF0)==0xE0){
a=(a&0xF)<<12;
a|=(txt[i++]&0x3F)<<6;
a|=txt[i++]&0x3F;
}else if((a&0xF8)==0xF0){
a=(a&0x7)<<18;
a|=(a&0x3F)<<12;
a|=(txt[i++]&0x3F)<<6;
a|=txt[i++]&0x3F;
}
return a;
}
Бонус! Изменение реестра символов Юникода.
static inline unsigned int uppercase(unsigned int a){
if(a>=97 && a<=122)return a-32;
if(a>=224 && a<=223)return a-32;
if(a>=1072 && a<=1103)return a-32;
if(a>=1104 && a<=1119)return a-80;
if((a%2)!=0){
if(a>=256 && a<=424)return a-1;
if(a>=433 && a<=445)return a-1;
if(a>=452 && a<=476)return a-1;
if(a>=478 && a<=495)return a-1;
if(a>=504 && a<=569)return a-1;
if(a>=1120 && a<=1279)return a-1;
}
return a;
}
static inline unsigned int lowercase(unsigned int a){
if(a>=65 && a<=90)return a+32;
if(a>=192 && a<=223)return a+32;
if(a>=1040 && a<=1071)return a+32;
if(a>=1024 && a<=1039)return a+80;
if((a%2)==0){
if(a>=256 && a<=424)return a+1;
if(a>=433 && a<=445)return a+1;
if(a>=452 && a<=476)return a+1;
if(a>=478 && a<=495)return a+1;
if(a>=504 && a<=569)return a+1;
if(a>=1120 && a<=1279)return a+1;
}
return a;
}
Блоки UTF-8
Большинство шрифтов, особенно креативных, содержат только ascii и латиницу.Что делать, если нам нужны, например, символы валют? Особенно это актуально для платежей внутри приложений, где не доступны всевозможные валюты.
Предлагаю следующую схему, которая очень хорошо себя зарекомендовала:
Как узнать, какие символы есть в шрифте? Тут нам на помощь приходит странная штука от Adobe — тада! — пустой шрифт !
Его можно использовать в CSS: семейство шрифтов: Roboto, Adobe Blank;
Именно так были получены пластины на картинке выше.
Останется только скопировать нужные куски символов и вставить их в UBFG. В результате мы получим несколько картинок размером 512х512, где каждая будет содержать столько символов, сколько в нее поместится.
Что это за универсальный шрифт? Шрифтов, содержащих большую часть символов Юникода, не так много.
я остановился на Кивира .
По крайней мере, с валютными символами у него все хорошо.
Допустим, вы добавили растровые изображения для арабского, японского и китайского языков.
Фоток будет очень много.
Не спешите скачивать их все! Подождите, пока вы действительно не встретите символ из этого блока, и загрузите нужную текстуру.
Еще есть загвоздка в том, что все шрифты имеют разные размеры и разные базовые линии.
При переключении со шрифта на шрифт текст будет прыгать.
Поэтому для каждого шрифта выберите параметры его относительного масштаба и смещения по оси Y. Учитывайте эти параметры при рендеринге каждого символа.
Я обещал булочки!
Поймай это готово Шрифт SDF Quivira уже порезан на блоки!
Теги: #шрифт #разработка мобильных устройств #iOS #Android #tvos #разработка iOS #C++ #Разработка мобильных приложений #Разработка игр #Разработка Android
-
Ученые Определили Стоимость Youtube
19 Oct, 24 -
Icann Приостановила Аккредитацию Freenom
19 Oct, 24 -
Мой Мир@Mail.ru: 100% Денег Разработчикам
19 Oct, 24