Каков современный уровень визуализации текста в OpenGL начиная с версии 4.1? [закрыто]

199

В OpenGL уже есть ряд вопросов о рендеринге текста, таких как:

Но в основном обсуждается рендеринг текстурированных квадов с использованием конвейера с фиксированной функцией. Конечно, шейдеры должны сделать лучше.

Я не очень обеспокоен интернационализацией, большинство моих строк будут помечены галочками (дата и время или чисто числовые). Но графики будут перерисованы с частотой обновления экрана, и там может быть довольно много текста (не более нескольких тысяч глифов на экране, но достаточно, чтобы компоновка с аппаратным ускорением была бы хорошей).

Каков рекомендуемый подход для рендеринга текста с использованием современного OpenGL? (Цитирование существующего программного обеспечения с использованием этого подхода является хорошим доказательством того, что оно работает хорошо)

  • Геометрические шейдеры, которые принимают, например, положение и ориентацию и последовательность символов и испускают текстурированные квадраты
  • Геометрические шейдеры, которые отображают векторные шрифты
  • Как и выше, но вместо этого используются тесселяционные шейдеры
  • Вычислительный шейдер для растеризации шрифтов
Бен Фойгт
источник
10
Я не могу ответить на вопрос о состоянии дел, будучи в основном ориентированным на OpenGL ES в настоящее время, но тестирование TTF с использованием тесселятора GLU и представление его в виде геометрии через старый конвейер с фиксированной функциональностью с кернингом, рассчитанным на CPU, дало хорошие визуальные результаты по антивирусам. -Лизинг оборудования и хорошая производительность по всем направлениям даже почти десять лет назад. Так что не только с шейдерами вы можете найти «лучший» способ (конечно, в зависимости от ваших критериев). FreeType может выплевывать границы глифов Безье и информацию о кернинге, поэтому вы можете работать в реальном времени из TTF во время выполнения.
Томми
QML2 (из Qt5) делает несколько интересных трюков с полями OpenGL и расстояния при рендеринге текста: blog.qt.digia.com/blog/2012/08/08/native-looking-text-in-qml-2
mlvljr
Так что я не теряю это снова, вот библиотека, которая реализует метод поля расстояния Valve. code.google.com/p/glyphy Я не пробовал. Также, возможно, стоит посмотреть: code.google.com/p/signed-distance-field-font-generator
Timmmm
7
это «не по теме» проклятие переполнения стека. шутки в сторону?
Николаос Джотис
1
более наивная версия "как это сделать": stackoverflow.com/questions/8847899/…
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

202

Рендеринг контуров, если только вы не отрисовываете всего дюжину символов, остается «не ходовым» из-за количества вершин, необходимых на символ для приблизительной кривизны. Хотя существуют подходы для оценки кривых Безье в пиксельном шейдере, они страдают от того, что их нелегко сглаживать, что тривиально при использовании квадрата с текстурированной картой расстояний, а вычисление кривых в шейдере все еще в вычислительном отношении намного дороже, чем необходимо.

Наилучшим компромиссом между «быстрым» и «качественным» по-прежнему являются текстурированные квадраты со текстурой поля со знаком. Это немного медленнее, чем при использовании обычного обычного текстурированного квадрата, но не так сильно. Качество, с другой стороны, совершенно другое. Результаты действительно потрясающие, они настолько быстрые, насколько вы можете получить, и такие эффекты, как свечение, тоже легко добавлять. Кроме того, методика может быть приятно понижена до более старого оборудования, если это необходимо.

Смотрите знаменитую бумагу Valve для техники.

Техника концептуально похожа на то, как работают неявные поверхности (метаболлы и т. Д.), Хотя она не генерирует многоугольники. Он полностью выполняется в пиксельном шейдере и принимает расстояние, выбранное из текстуры, как функцию расстояния. Все, что выше выбранного порога (обычно 0,5), находится «внутри», все остальное «вне». В простейшем случае на 10-летнем оборудовании, не поддерживающем шейдеры, установка порога альфа-теста на 0,5 будет делать именно это (но без специальных эффектов и сглаживания).
Если кто-то хочет добавить немного больше веса к шрифту (искусственно выделенный жирным шрифтом), немного меньший порог сделает свое дело без изменения одной строки кода (просто измените форму "font_weight"). Для эффекта свечения просто рассматривается все, что выше одного порога, как «внутри», а все, что выше другого (меньшего) порога, как «снаружи, но в сиянии», и LERP между ними. Сглаживание работает аналогично.

Используя 8-битное значение расстояния со знаком, а не один бит, этот метод увеличивает эффективное разрешение вашей карты текстуры в 16 раз в каждом измерении (вместо черного и белого используются все возможные оттенки, таким образом, мы имеем 256-кратное увеличение информация, использующая то же хранилище). Но даже если вы увеличите намного больше 16x, результат все равно выглядит вполне приемлемым. Длинные прямые линии со временем станут немного волнистыми, но типичных «блочных» артефактов сэмплирования не будет.

Вы можете использовать геометрический шейдер для генерации квадраторов из точек (уменьшить пропускную способность шины), но, честно говоря, выигрыши довольно незначительны. То же самое верно для рендеринга инстансированных символов, как описано в GPG8. Затраты на инстансинг амортизируются, только если у вас много текста для рисования. Преимущества, на мой взгляд, никак не связаны с дополнительной сложностью и возможностью снижения рейтинга. Кроме того, вы либо ограничены количеством константных регистров, либо должны считывать данные из объекта буфера текстур, что является неоптимальным для согласованности кэша (и целью было оптимизировать с самого начала!).
Простой, простой старый буфер вершин будет таким же быстрым (возможно, быстрее), если вы запланируете загрузку немного раньше времени, и будет работать на каждом оборудовании, созданном за последние 15 лет. И это не ограничивается каким-либо конкретным количеством символов в вашем шрифте, ни определенным количеством символов для визуализации.

Если вы уверены, что в вашем шрифте не более 256 символов, массивы текстур, возможно, стоит рассмотреть, чтобы обрезать полосу пропускания шины аналогично генерации четырехугольников из точек геометрического шейдера. При использовании текстуры массива координаты текстуры всех четырехугольников имеют одинаковую, постоянную sи tкоординаты и отличаются только rкоординатой, которая равна индексу символа для визуализации.
Но, как и в случае с другими методами, ожидаемый выигрыш незначителен за счет несовместимости с оборудованием предыдущего поколения.

Есть удобный инструмент от Jonathan Dummer для генерации текстур на расстоянии: страница описания

Обновление:
как было недавно отмечено в Программируемом извлечении вершин (D. Rákos, «OpenGL Insights», стр. 239), нет никаких существенных дополнительных задержек или накладных расходов, связанных с программным извлечением данных вершин из шейдера на новейших поколениях графических процессоров, по сравнению с тем же, используя стандартную фиксированную функцию.
Кроме того, последние поколения графических процессоров имеют все больше и больше разумных размеров кэш-памяти L2 общего назначения (например, 1536 кБ на nvidia Kepler), поэтому можно ожидать, что проблема некогерентного доступа при вытягивании случайных смещений для четырехугольных углов из текстуры буфера будет меньше проблема.

Это делает идею извлечения постоянных данных (таких как размеры квадратов) из буферной текстуры более привлекательной. Таким образом, гипотетическая реализация может снизить до минимума передачи PCIe и памяти, а также памяти графического процессора при таком подходе:

  • Загружайте только символьный индекс (по одному для каждого отображаемого символа) в качестве единственного ввода в вершинный шейдер, который передает этот индекс и gl_VertexID, и усиливайте его до 4 точек в геометрическом шейдере, все еще имея индекс символа и идентификатор вершины (это будет "gl_primitiveID, сделанный доступным в вершинном шейдере") как единственные атрибуты, и получит это через обратную связь преобразования.
  • Это будет быстро, потому что есть только два выходных атрибута (основное узкое место в GS), и он близок к «no-op» в противном случае на обоих этапах.
  • Привязать текстуру буфера, которая содержит для каждого символа в шрифте позиции вершин текстурированного четырехугольника относительно базовой точки (это в основном «метрики шрифта»). Эти данные могут быть сжаты до 4-х чисел на квад, сохраняя только смещение нижней левой вершины и кодируя ширину и высоту выровненного по оси блока (если допустить, что половина поплавка, это будет 8 байтов постоянного буфера на символ - типичный 256-символьный шрифт может полностью поместиться в 2 кбайт кеша L1).
  • Установите форму для базовой линии
  • Привязать текстуру буфера с горизонтальными смещениями. Вероятно, их можно даже рассчитать на графическом процессоре, но это гораздо проще и эффективнее для подобных вещей на процессоре, поскольку это строго последовательная операция, а вовсе не тривиальная (вспомним кернинг). Кроме того, потребуется еще один проход обратной связи, который станет еще одной точкой синхронизации.
  • Визуализируйте ранее сгенерированные данные из буфера обратной связи, вершинный шейдер извлекает горизонтальное смещение базовой точки и смещения угловых вершин из объектов буфера (используя идентификатор примитива и индекс символа). Исходный идентификатор вершины представленных вершин теперь является нашим «примитивным идентификатором» (помните, что GS превратил вершины в квадраты).

Таким образом, в идеале можно уменьшить требуемую ширину полосы вершины на 75% (амортизироваться), хотя она сможет отображать только одну строку. Если бы кто-то хотел иметь возможность визуализировать несколько линий за один вызов отрисовки, ему нужно было бы добавить базовую линию к текстуре буфера, а не использовать униформу (уменьшая прирост пропускной способности).

Тем не менее, даже при условии 75% сокращения - поскольку данные вершин отображают «разумные» объемы текста, составляет всего лишь около 50-100 кБ (что практически равно нулю)к графическому процессору или шине PCIe) - я все еще сомневаюсь, что дополнительная сложность и потеря обратной совместимости действительно стоят того. Уменьшение нуля на 75% все еще только ноль. По общему признанию, я не пробовал вышеупомянутый подход, и необходимы дополнительные исследования, чтобы сделать действительно квалифицированное заявление. Но тем не менее, если кто-то не сможет продемонстрировать действительно ошеломляющую разницу в производительности (используя «нормальные» объемы текста, а не миллиарды символов!), Я считаю, что для данных вершин простой, простой старый буфер вершин вполне оправданно оправдан быть частью «современного решения». Это просто и понятно, это работает, и это работает хорошо.

Уже ссылаясь на « OpenGL Insights » выше, стоит также отметить главу «2D-рендеринг фигур по полям расстояний» Стефана Густавсона, в которой подробно объясняется рендеринг поля расстояний.

Обновление 2016:

Между тем, существует несколько дополнительных методов, которые направлены на удаление артефактов скругления углов, которые становятся мешающими при экстремальных увеличениях.

Один подход просто использует поля псевдодальности вместо полей расстояния (разница в том, что расстояние - это кратчайшее расстояние не до фактического контура, а до контура или воображаемой линии, выступающей над краем). Это несколько лучше и работает с той же скоростью (идентичный шейдер), используя тот же объем памяти текстур.

Другой подход использует медиану-три в трехканальных деталях текстуры и реализации, доступных на github . Это должно быть улучшение по сравнению с хакерами, которые использовались ранее для решения проблемы. Хорошее качество, немного, почти не заметно, медленнее, но использует в три раза больше памяти текстур. Кроме того, дополнительные эффекты (например, свечение) труднее получить право.

И, наконец, хранение фактических кривых Безье, составляющих символов, а также оценки их в пиксельный шейдер стал практичным , с немного уступает производительности (но не так много , что это проблема) и потрясающие результаты даже при высоких увеличениях.
Демо-версия WebGL для рендеринга большого PDF с этой техникой в ​​режиме реального времени доступна здесь .

Damon
источник
1
Они выглядят довольно хорошо (даже при наивной фильтрации и в отсутствие mipmapping, так как у вас очень маленькие текстуры и данные прекрасно интерполируются). Лично я думаю, что они даже выглядят лучше, чем «настоящие» вещи во многих случаях, потому что нет намеков на странности, которые часто приводят к вещам, которые я воспринимаю как «странные». Например, текст меньшего размера внезапно не выделяется жирным шрифтом без видимой причины и не попадает в границы пикселей - эффекты, которые вы часто видите с «настоящими» шрифтами. Для этого могут быть исторические причины (показы 1985 года), но сегодня я не понимаю, почему так должно быть.
Деймон
2
Работает и выглядит отлично, спасибо, что поделились! Для тех, кто хочет использовать HLSL для фрагмента шейдера, смотрите здесь . Вы можете адаптировать это для GLSL, заменив clip(...)строку на if (text.a < 0.5) {discard;}(или text.a < threshold). НТН.
Инженер
1
Спасибо за обновление. Я хотел бы снова поднять голос.
Бен Фойгт
2
@NicolBolas: Вы, кажется, не читали очень внимательно. Оба вопроса объясняются в ответе. Кеплер приводится в качестве примера «последнего поколения», второго прохода нет (и это объясняется почему), и я заявляю, что не верю, что гипотетическая техника экономии пропускной способности заметно быстрее или вообще не стоит проблем. Тем не менее, вера ничего не значит - нужно попытаться узнать (я не знаю, так как не считаю рисование «нормальных» объемов текста узким местом в любом случае). Тем не менее, это может быть полезно, когда кто-то отчаянно нуждается в пропускной способности и имеет «ненормальное» количество текста.
Деймон
3
@NicolBolas: Вы правы насчет этого предложения, извините. Это действительно немного вводит в заблуждение. В предыдущем абзаце я написал: «Возможно, это можно было бы даже сгенерировать на GPU, но для этого потребовалась бы обратная связь и ... неопытность». - но затем по ошибке продолжил с "сгенерированными данными из буфера обратной связи" . Я исправлю это. На самом деле, я перепишу все это на выходных, так что это будет менее двусмысленным.
Деймон
15

http://code.google.com/p/glyphy/

Основное различие между GLyphy и другими средствами визуализации OpenGL на основе SDF состоит в том, что большинство других проектов сэмплируют SDF в текстуру. Это имеет все обычные проблемы, которые есть у выборки. То есть. он искажает контур и имеет низкое качество. Вместо этого GLyphy представляет SDF с использованием фактических векторов, переданных в графический процессор. Это приводит к очень высокому качеству рендеринга.

Недостатком является то, что код для iOS с OpenGL ES. Я, вероятно, собираюсь сделать порт для Windows / Linux OpenGL 4.x (надеюсь, автор добавит немного реальной документации).

Отображаемое имя
источник
3
Всем, кто интересуется GLyphy, вероятно, следует посмотреть выступление автора на Linux.conf.au 2014: youtube.com/watch?v=KdNxR5V7prk
Fizz
14

Самым распространенным методом по-прежнему остаются текстурированные четырехугольники. Однако в 2005 году LORIA разработала так называемые векторные текстуры, то есть рендеринг векторной графики в качестве текстур на примитивах. Если использовать это для преобразования шрифтов TrueType или OpenType в векторную текстуру, вы получите следующее:

http://alice.loria.fr/index.php/publications.html?Paper=VTM@2005

datenwolf
источник
2
Знаете ли вы какие-либо реализации, использующие эту технику?
Люк
2
Нет (как в серийном выпуске), но в статье Килгарда (см. Мой ответ ниже для ссылки) есть краткая критика, которую я резюмирую как: пока не практичная. Там было больше исследований в этой области; более поздние работы, на которые ссылается Килгард, включают research.microsoft.com/en-us/um/people/hoppe/ravg.pdf и uwspace.uwaterloo.ca/handle/10012/4262
Fizz,
9

Я удивлен, что ребенок Марка Килгарда , NV_path_rendering (NVpr), не был упомянут ни одним из вышеперечисленных. Хотя его цели являются более общими, чем рендеринг шрифтов, он также может отображать текст из шрифтов и с помощью кернинга. Он даже не требует OpenGL 4.1, но на данный момент это расширение только для поставщиков / Nvidia. Он в основном превращает шрифты в пути, используя glPathGlyphsNVкоторые зависит от библиотеки freetype2 для получения метрик и т. Д. Затем вы также можете получить доступ к информации о glGetPathSpacingNVкернинге и использовать общий механизм рендеринга пути NVpr для отображения текста с использованием «преобразованных» шрифтов пути. (Я поместил это в кавычки, потому что нет реального преобразования, кривые используются как есть.)

Записали демо для шрифта возможностей NVpr в это , к сожалению , не особенно впечатляет. (Может быть, кто-то должен сделать его в духе гораздо более захватывающей демонстрации SDF, которую можно найти в промежутках ...)

Доклад о презентации NVpr API 2011 для части шрифтов начинается здесь и продолжается в следующей части ; немного жаль, как эта презентация расколота.

Более общие материалы по NVpr:

  • Nvidia NVpr , но некоторые материалы на целевой странице не самые свежие
  • Статья Siggraph 2012 для мозгов метода рендеринга пути, называемого «трафарет, затем обложка» (StC); В статье также кратко объясняется, как работает конкурирующая технология, такая как Direct2D. Связанные со шрифтом биты были перенесены в приложение к документу . Есть также некоторые дополнения, такие как видео / демо .
  • Презентация GTC 2014 для обновления статуса; в двух словах: теперь он поддерживается Google Skia (Nvidia предоставила код в конце 2013 и 2014 годов), который, в свою очередь, используется в Google Chrome и [независимо от Skia, я думаю] в бета-версии Adobe Illustrator CC 2014
  • официальная документация в реестре расширений OpenGL
  • USPTO выдало по крайней мере четыре патента Kilgard / Nvidia в связи с NVpr, о которых вам, вероятно, следует знать, если вы хотите реализовать StC самостоятельно: US8698837 , US8698808 , US8704830 и US8730253 . Обратите внимание, что есть еще около 17 документов USPTO, связанных с этим как «также опубликованных как», большинство из которых являются патентными заявками, поэтому вполне возможно, что больше патентов может быть выдано из них.

И так как слово «трафарет» не произвело ни одного хита на этой странице до моего ответа, кажется, что подмножество SO-сообщества, которое участвовало на этой странице, поскольку, несмотря на то, что было довольно многочисленным, не знали о тесселяции, буфере буфера основанные методы для рендеринга пути / шрифта в целом. У Kilgard есть пост, похожий на FAQ, на форуме opengl, который может пролить свет на то, как методы рендеринга путей без тесселяции отличаются от стандартной трехмерной графики, даже если они все еще используют графический процессор [GP]. (NVpr нужен чип с поддержкой CUDA.)

С исторической точки зрения Килгард также является автором классического «Простого API на основе OpenGL для текстурного картографического текста», SGI, 1997 , который не следует путать с NVpr на основе трафаретов, который дебютировал в 2011 году.


Большинство, если не все недавние методы, обсуждаемые на этой странице, в том числе основанные на трафарете методы, такие как NVpr, или методы на основе SDF, такие как GLyphy (которые я не буду здесь обсуждать, поскольку другие ответы уже охватывают это), имеют, однако, одно ограничение: они подходит для отображения большого текста на обычных (~ 100 DPI) мониторах без зазубрин при любом уровне масштабирования, а также хорошо смотрится даже при небольшом размере на сетчатых дисплеях с высоким DPI. Однако они не полностью предоставляют то, что дает Microsoft Direct2D + DirectWrite, а именно намеки на маленькие глифы на основных дисплеях. (Визуальный обзор подсказок в целом см., Например, на этой странице типографии . Более подробный ресурс на сайте antigrain.com .)

Я не знаю ни одного открытого и производимого материала на основе OpenGL, который может делать то, что Microsoft может, намекая на данный момент. (Я признаю, что не знаю, как работают Apple OS X GL / Quartz, потому что, насколько мне известно, Apple не публиковала информацию о том, как они выполняют рендеринг шрифтов и путей на основе GL. Кажется, что OS X, в отличие от MacOS 9, этого не делает делайте хинтинг вообще, что раздражает некоторых людей .) Во всяком случае, есть одна исследовательская работа 2013 года, которая касается хинтинга через шейдеры OpenGL, написанной Николасом П. Ружером из INRIA; Вероятно, стоит прочитать, если вам нужна подсказка от OpenGL. Хотя может показаться, что библиотека типа freetype уже выполняет всю работу, когда дело доходит до подсказок, на самом деле это не так по следующей причине, которую я цитирую из статьи:

Библиотека FreeType может растеризовать глиф, используя субпиксельное сглаживание в режиме RGB. Однако это только половина проблемы, поскольку мы также хотим добиться субпиксельного позиционирования для точного размещения глифов. Отображение текстурированного четырехугольника с дробными пиксельными координатами не решает проблему, поскольку приводит только к интерполяции текстуры на уровне целого пикселя. Вместо этого мы хотим добиться точного сдвига (между 0 и 1) в субпиксельной области. Это можно сделать в фрагментном шейдере [...].

Решение не совсем тривиально, поэтому я не буду пытаться объяснить это здесь. (Бумага открытого доступа.)


Еще одна вещь, которую я узнал из статьи Ружье (и которую Килгард, похоже, не учел), заключается в том, что возможности шрифтов (Microsoft + Adobe) создали не один, а два метода спецификации кернинга. Старый основан на так называемой таблице kern и поддерживается freetype. Новый называется GPOS и поддерживается только новыми библиотеками шрифтов, такими как HarfBuzz или pango в мире свободного программного обеспечения. Поскольку кажется, что NVpr не поддерживает ни одну из этих библиотек, кернинг может не работать из коробки с NVpr для некоторых новых шрифтов; Есть некоторые из тех, кто, по-видимому, в дикой природе, согласно этому обсуждению на форуме .

Наконец, если вам нужно сделать сложную разметку текста (CTL), вам кажется, что в настоящее время вам не повезло с OpenGL, так как для этого не существует библиотеки на основе OpenGL. (DirectWrite, с другой стороны, может обрабатывать CTL.) Существуют библиотеки с открытым исходным кодом, такие как HarfBuzz, которые могут визуализировать CTL, но я не знаю, как заставить их работать хорошо (как при использовании методов на основе трафарета) через OpenGL. Возможно, вам придется написать связующий код для извлечения измененных контуров и передачи их в решения на основе NVpr или SDF в качестве путей.

шипение
источник
4
Я не упомянул NV_path_rendering, потому что это расширение, принадлежащее вендору, чтобы сделать ситуацию еще хуже. Обычно я стараюсь давать ответы только для тех методов, которые универсально применимы.
datenwolf
1
Ну, я могу согласиться с этим в некоторой степени. Сам метод («трафарет, затем покрытие») на самом деле не сложно реализовать непосредственно в OpenGL, но он будет иметь большие командные издержки, если его делать наивно таким образом, поскольку предыдущие попытки на основе трафарета заканчивались. Ския [через Ганеша] попробовал решение на основе трафарета, но отказался от него, по словам Килграда. Способ, которым он реализован Nvidia, уровень ниже, используя возможности CUDA, заставляет его работать. Вы можете попробовать "Mantle" StC самостоятельно, используя целую кучу расширений EXT / ARB. Но учтите, что у Kilgard / Nvidia есть две патентные заявки на NVpr.
Физз
3

Я думаю, что вам лучше всего рассмотреть каирскую графику с бэкэндом OpenGL.

Единственной проблемой, с которой я столкнулся при разработке прототипа с ядром 3.3, было устаревшее использование функций в бэкэнде OpenGL. Это было 1-2 года назад, так что ситуация могла бы улучшиться ...

В любом случае, я надеюсь, что в будущем настольные графические драйверы OpenGL будут реализовывать OpenVG.

Orhun
источник