Troika: Быстрая идея по улучшению сглаживания текста

Созданный на 17 июн. 2021  ·  7Комментарии  ·  Источник: protectwise/troika

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

В моей текущей версии Тройки вы делаете return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.5; для расстояния aa. У этого есть артефакт, заключающийся в том, что при просмотре под углом крайний левый и крайний правый края получают несколько видимое размытие:
image

Я изменил его на скаляр; расстояние, как в моем коде круга: return fwidth(distance) * 0.5; С расстоянием, переданным из troikaGetFragDistValue следующим образом: float distance = troikaGetFragDistValue(); float aaDist = troikaGetAADist(distance);
Делая это таким образом, я не получаю артефакт размытия:
image

Коэффициент 0.5 можно увеличить для более плавного сглаживания. Например, 0.8 :
image

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

image

Я нашел этот метод в сообщении bgolus здесь: https://forum.unity.com/threads/antialiasing-circle-shader.432119/ , где есть дополнительная информация. Он также говорит, что вы можете сделать, например, length(vec2(dFdx(distance), dFdy(distance))) вместо fwidth(distance) для еще большей точности, хотя я не уверен, что это незначительно или нет.

Все 7 Комментарий

Спасибо за предложение! Я обязательно посмотрю на это, так как я заметил эти размытые края и под наклонными углами и хотел бы их устранить.

Теоретически я думаю, что и изменение в distance , и изменение в vTroikaGlyphUV * vTroikaGlyphDimensions должны быть эквивалентны, поскольку они оба представляют расстояние в единицах пространства шрифта, но должна быть какая-то тонкая разница, которую я упущен из виду. Если я смогу избежать артефактов, которые вы показали, я буду рад интегрировать ваше изменение. :)

Здорово! Изменение так же просто, как эта одна строка (и передача этого аргумента расстояния для его размещения). Если вам все еще нужен необработанный код, я могу отправить его завтра, когда буду работать.

Теоретически я думаю, что и изменение расстояния, и изменение vTroikaGlyphUV * vTroikaGlyphDimensions должны быть эквивалентны, поскольку они оба представляют расстояние в единицах пространства шрифта.

Я не совсем понимаю, как производные будут одинаковыми. distance — это расстояние SDF, которое я предполагаю, но другое — масштабированное положение uv. Я не смотрел код и то, как они сравниваются, но мне кажется, что их производные должны отличаться. Насколько я знаю, fwidth просто принимает разницу определенного входного значения, переданного в качестве аргумента, с теми же конкретными входными значениями в соседних фрагментах, что возможно, потому что соседние фрагменты выполняются одновременно и параллельно. Ранее сегодня я провел тест с gl_FragColor.rgb = vec3(fwidth(gl_FragColor.x + gl_FragColor.g + gl_FragColor.b)); , чтобы увидеть, отображаются ли края, как при обнаружении краев/фильтре Собеля, чтобы попытаться немного подтвердить этот факт, и это произошло.

Спасибо за дискуссию, иногда эта штука ломает мне мозг. ;)

Когда я сказал, что они «эквивалентны», я имел в виду, что они оба представляют расстояние в одних и тех же единицах, но вы правы, они не совсем одинаковы.

Поддерживая, здесь нам нужна _потенциальная_ скорость изменения между фрагментами. Использование изменения в переменной distance , которое вы предлагаете, часто будет точным для этого, но в некоторых случаях оно будет очень неточным. Это связано с тем, что SDF, из которого выбирается это значение, является _неоднородным_; он изгибается и меняет направление изменения глифа. Поэтому производная distance между соседними фрагментами может оказаться слишком маленькой, даже нулевой, например, когда поле расстояния переключается с увеличения на уменьшение на полпути между путями глифов, или слишком большой, например, при отображении текста. при малых размерах, а сетка фрагментов перекрывает SDF в непредсказуемых местах. Я подозреваю, что это объясняет, по крайней мере, некоторые артефакты, которые вы видите.

Итак, как мы можем определить _потенциальную_ скорость изменения, не используя distance ? Что ж, эта «масштабированная ультрафиолетовая позиция», как вы ее называете, по сути является координатой x/y внутри прямоугольника глифа с использованием тех же единиц измерения, что и distance . Производная от этого x/y дает нам скорость потенциального перемещения между фрагментами. В целом это дает лучший результат, чем использование distance , потому что он основан на _равномерной_ скорости изменения по всему глифу, и поэтому мы не получаем артефактов, и он более точен при небольших размерах.

Но, как вы видели, она ломается под косыми углами; в этих случаях скорость потенциального изменения x/y по вертикали сильно отличается от скорости по горизонтали. И поэтому мы в конечном итоге используем одно значение для потенциальной скорости изменения, которое слишком велико в одном направлении и слишком мало в другом, что дает размытые и/или прерывистые линии в зависимости от фактического направления изменения SDF в этом фрагменте.

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

Да, об этом сложно думать, ха-ха. Я вижу, что поле расстояний менее гладкое, да.

Эти глючные пиксели могут быть просто потому, что у фрагментов есть какие-то странные значения расстояния.

Издалека это не невероятно изменчиво, но вы определенно можете видеть, что ультрафиолетовые лучи стали лучше. Возможно, будет хорошо работать интерполяция между переменной расстояния и UV, зависящими от расстояния до камеры; например, выбрать больше fwidth (расстояние), когда близко, и больше fwidth (uv), когда далеко.

Для сравнения (не снимал оба с точно такой же позиции и ориентации камеры, но я думаю, что разница хорошо видна):
Расстояние (коэффициент 0,8):
image
УФ:
image

Я заметил, что эта модификация значительно улучшает результат для пользовательских преобразований текста. Вот некоторый текст, измененный, чтобы следовать кривой, используя createDerivedMaterial ( см. эту технику ). Здесь используются текущие шейдеры troika-three-text:

image

Вот тот же снимок экрана, но с модификацией @asbjornlystrup на troikaGetAADist :

image

На втором кадре видно чрезвычайно растянутое «ВЕЛИКОЛЕПНО!» немного четче.

Как ни странно: я также обнаружил, что настройка константы до 0,25 (с 0,5) также дает хороший четкий край:

return length(fwidth(vTroikaGlyphUV * vTroikaGlyphDimensions)) * 0.25;

Спасибо @canadaduane , я определенно вижу улучшение растянутых частей. Я также вижу уродливые артефакты, появляющиеся между буквами L в HELLO, которые нам обязательно нужно выяснить, прежде чем принимать это изменение.

Была ли эта страница полезной?
0 / 5 - 0 рейтинги