Рендеринг контуров, если только вы не отрисовываете всего дюжину символов, остается «не ходовым» из-за количества вершин, необходимых на символ для приблизительной кривизны. Хотя существуют подходы для оценки кривых Безье в пиксельном шейдере, они страдают от того, что их нелегко сглаживать, что тривиально при использовании квадрата с текстурированной картой расстояний, а вычисление кривых в шейдере все еще в вычислительном отношении намного дороже, чем необходимо.
Наилучшим компромиссом между «быстрым» и «качественным» по-прежнему являются текстурированные квадраты со текстурой поля со знаком. Это немного медленнее, чем при использовании обычного обычного текстурированного квадрата, но не так сильно. Качество, с другой стороны, совершенно другое. Результаты действительно потрясающие, они настолько быстрые, насколько вы можете получить, и такие эффекты, как свечение, тоже легко добавлять. Кроме того, методика может быть приятно понижена до более старого оборудования, если это необходимо.
Смотрите знаменитую бумагу 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 с этой техникой в режиме реального времени доступна здесь .