Troika: Структура текста

Созданный на 20 авг. 2020  ·  39Комментарии  ·  Источник: protectwise/troika

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

Спасибо!

Самый полезный комментарий

Спасибо за информацию о Mapbox, @anzorb. Я определенно проверю их реализацию более внимательно, но из этого сообщения в блоге (они называют схему «ореолом») звучит так, как будто они используют подход альфа-порога SDF, как я упоминал ранее. Но их стратегия генерации SDF и упаковки атласа отличается от моей и не имеет тех же проблем с неравномерными размерами полей расстояний для каждого глифа. Однако, вероятно, у него будет такое же ограничение максимальной ширины контура.

На самом деле я добился некоторого прогресса в создании контуров _экранного пространства_, используя стандартные производные. Экранное пространство означает, что контур будет иметь ширину, например, 1 экранный пиксель, независимо от размера/масштаба или наклона текста. Если цель состоит в том, чтобы просто добавить контраста фону, кажется, что это может быть так же хорошо (или даже предпочтительнее) как контуры, которые масштабируются вместе с размером текста. У вас есть мнение по этому поводу?

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

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

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

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

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

Я начал играть с textOutline как с функцией некоторое время назад, но так и не закончил ее.

Я был бы ОЧЕНЬ рад, если бы он изначально поддерживался :)
Тем не менее, я очень новичок в шейдерах, поэтому я не уверен, насколько могу помочь. Я буду внимательно следить за этим вопросом.

Я вернулся к этому немного и быстро понял, почему я не закончил это изначально. Хотя SDF _может_ использоваться для создания контуров, есть некоторые предостережения:

  • Максимальная потенциальная ширина контура ограничена размером самого поля расстояний (~8 текселей в текстуре SDF 64x64).
  • Это не соответствует единому визуальному размеру всех глифов, поскольку текстура SDF масштабируется в соответствии с границами каждого глифа.

Таким образом, большие глифы, такие как «W», могут иметь достаточно толстый контур, а маленькие, такие как точки, могут иметь только очень тонкий контур. И каждый глиф должен использовать другую точку отсечки SDF, чтобы их контуры были визуально согласованными.

Я не думаю, что это непреодолимо, но не так просто, как я думал изначально.

@lojjic , ​​люблю эту библиотеку 🥇, но для нашего проекта требуется набросок текста...

Я вернулся к этому немного и быстро понял, почему я не закончил это изначально. Хотя SDF _может_ использоваться для создания контуров, есть некоторые предостережения:

  • Максимальная потенциальная ширина контура ограничена размером самого поля расстояний (~8 текселей в текстуре SDF 64x64).
  • Это не соответствует единому визуальному размеру всех глифов, поскольку текстура SDF масштабируется в соответствии с границами каждого глифа.

Таким образом, большие глифы, такие как «W», могут иметь достаточно толстый контур, а маленькие, такие как точки, могут иметь только очень тонкий контур. И каждый глиф должен использовать другую точку отсечки SDF, чтобы их контуры были визуально согласованными.

Я не думаю, что это непреодолимо, но не так просто, как я думал изначально.

Мне любопытно, можем ли мы сделать какую-то постобработку с использованием шейдеров для достижения эффекта контура и/или тени? Заранее спасибо!
Вот эффект, которого я пытаюсь достичь:
Screen Shot 2020-08-31 at 4 32 03 PM

Редактировать: это из mapbox gl, я думаю, что они также используют SDF https://blog.mapbox.com/drawing-text-with-signed-distance-fields-in-mapbox-gl-b0933af6f817 , может быть, мы можем анализировать их код, чтобы увидеть, что они сделали. https://github.com/mapbox/mapbox-gl-js

Спасибо за информацию о Mapbox, @anzorb. Я определенно проверю их реализацию более внимательно, но из этого сообщения в блоге (они называют схему «ореолом») звучит так, как будто они используют подход альфа-порога SDF, как я упоминал ранее. Но их стратегия генерации SDF и упаковки атласа отличается от моей и не имеет тех же проблем с неравномерными размерами полей расстояний для каждого глифа. Однако, вероятно, у него будет такое же ограничение максимальной ширины контура.

На самом деле я добился некоторого прогресса в создании контуров _экранного пространства_, используя стандартные производные. Экранное пространство означает, что контур будет иметь ширину, например, 1 экранный пиксель, независимо от размера/масштаба или наклона текста. Если цель состоит в том, чтобы просто добавить контраста фону, кажется, что это может быть так же хорошо (или даже предпочтительнее) как контуры, которые масштабируются вместе с размером текста. У вас есть мнение по этому поводу?

Стандартизированная ширина гораздо более предпочтительна, чем масштабируемая ширина. Таким образом, он будет больше похож на css.
Любой text-shadow , применяемый через css, настраивается независимо от font-size .
Если предлагаемое вами решение @lojjic работает так же, то это будет 🎉 🎉 🎉

Спасибо за оперативный ответ @lojjic!

Цель состоит в том, чтобы добавить контраста, и ваше предложение выглядит великолепно. Вы имеете в виду, что контур будет статичным? т.е. нет способа контролировать это (как описал @stephencorwin , используя подход text-shadow-ish).

Честно говоря, я не вижу в этом проблемы, пока 1 пиксель виден на дисплеях со сверхвысокой плотностью (потому что в настоящее время мы умножаем размер шрифта на соотношение пикселей устройства для достижения «одинаковых» пропорций, независимо от плотности) , я думаю, ваше предложение означает, что контур будет заметно тоньше на устройствах с более высоким DPI, не так ли?

Заранее спасибо!

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

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

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

Мне тоже нужна эта функция. Я реализовал такую ​​функцию в приложении для Android, которое разработал много лет назад и использовал только для себя и друзей. Я начал повторно реализовывать это приложение с помощью React и пришел, чтобы найти ваш проект.
Как и другие, я хочу поблагодарить вас за эту библиотеку. Я поражен этим.

Что касается вашего комментария о большем количестве чтений текстур, я не думаю, что вам следует об этом сильно беспокоиться. Дополнительные тексели, которые вам нужны, будут текселями, которые нужны соседним фрагментам, и поэтому я думаю, что они в любом случае будут в кеше графического процессора, и они будут стоить почти ничего.

Спасибо за вклад @FunMiles. (Колорадо представляет! 😄)

Я думаю, что вы, вероятно, правы для контуров толщиной в 1, может быть, 2 фрагмента. Меня беспокоит то, что по мере увеличения толщины увеличивается как количество чтений текстур (толщина 4 = 56 чтений?), так и вероятность того, что эти чтения будут более дорогими. Но я просто размышляю, основываясь на некоторых вещах, я ни в коем случае не эксперт по графическим процессорам. Я, вероятно, просто должен попробовать это.

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

Прямо сейчас вы читаете скалярное расстояние для пикселя с помощью texture2D(uTroikaSDFTexture, vTroikaSDFTextureUV).r; . Если я правильно понимаю, что вы написали ранее, одна проблема связана с пространством вокруг глифов. Можете ли вы объяснить более подробно?
Возвращаясь к вашему примеру с «W» и «,» является ли диапазон vTroikaSDFTextureUV в текстуре плотно прилегающим точно вокруг глифа или есть дополнительное пространство? Знает ли фрагментный шейдер границы UV для текущего глифа? Если да, то для фрагмента, который выпал бы наружу, можно спроецировать текущую UV на границы и идти читать данные прямо внутри.

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

Возвращаясь к вашему примеру с «W» и «,» является ли диапазон vTroikaSDFTextureUV в текстуре плотно прилегающим точно вокруг глифа или есть дополнительное пространство?

Вокруг границ истинного пути каждого глифа есть небольшое дополнительное пространство, достаточное для размещения внешних частей поля расстояния. Это 8 текселей в SDF, но поскольку каждый SDF представляет собой однородный 64x64, масштабированный на квадрат глифов разного размера, это видимое поле варьирует глиф за глифом.

Знает ли фрагментный шейдер границы UV для текущего глифа? Если да, то для фрагмента, который выпал бы наружу, можно спроецировать текущую UV на границы и идти читать данные прямо внутри.

Эта информация должна быть доступна. Я не следую за вами в «проецировании текущего UV на границы и иду читать данные прямо внутри».

Я был бы признателен, если бы вы знали способ получить более толстые контуры с меньшим количеством прочтений текселей! Я концептуализировал это как: для фрагмента, не находящегося внутри пути глифа, выполните радиальный поиск, чтобы увидеть, есть ли какие-либо фрагменты в пределах r=попадет в путь глифа. Это может быть просто неправильный способ думать об этом.

Одна из проблем заключается в том, что происходит, когда глифы располагаются рядом друг с другом. Отложив это пока в сторону, давайте возьмем случай, когда один символ рисуется с довольно толстым контуром t . Персонаж рисуется с прямоугольником вокруг него, достаточно большим, чтобы вместить контур.
Фрагментный шейдер имеет две основные информации: vTroikaSDFTextureUV и vTroikaGlyphUV .

Насколько я понимаю, vTroikaGlyphUV имеет значения, зажатые между 0 и 1 внутри глифа. Когда я говорил о проекции, я имел в виду, что если vTroikaGlyphUV находится за пределами этих границ, вы можете получить расстояние точки, «проецируемой» на границу глифа. То есть, если uv = (1,05, 0,7), то спроецированная точка равна (1,0, 0,7). Используя значения в (1,0, 0,7 + эпсилон) и (1,0, 0,7-эпсилон), вы можете получить градиент расстояния и, используя его, вычислить приблизительное расстояние в (1,05, 0,7).
Когда я делал свою собственную работу для этого, я помню, как читал статью о том, как кто-то делал SDF с градиентом в текстуре. Этот подход позволяет избежать чтения трех текселей, но делает текстуру трехкомпонентной и усложняет построение текстуры.

Отвечает ли это на ваш вопрос?

Теперь, когда у вас есть несколько персонажей рядом, каждый из них должен знать о соседях, что немного усложняет задачу. Вам нужно нести GlyphUV и TextureUV не только для текущего персонажа, но и для соседа. Для этого может потребоваться разрезать каждый символ на два прямоугольника, чтобы соседним был один перед первым прямоугольником и один после второго прямоугольника. Я не уверен, как поступить с многострочным... Мне не приходилось иметь дело с этим случаем в моем приложении. Мое приложение было картой, и каждая метка была одной строкой.

PS: возможно, я неправильно понял ваше «дополнительное пространство вокруг истинных границ пути каждого глифа». Вы говорите, что для X, например, он заключен в прямоугольник и что есть четыре треугольника, которые не имеют допустимых значений?

@FunMiles Я _думаю_, может быть, я вижу, куда ты идешь. Мне нужно время, чтобы все обдумать, но в данный момент мне нужно сосредоточиться на другой работе в срок.

PS: Немного забавной математической заметки:
На всякий случай, если кому-то интересно, как можно получить градиент из двух или трех совмещенных точек, нужно помнить, что градиент расстояния имеет фиксированную норму (в зависимости от выбранного масштаба). И градиент определенно указывает на внешнюю часть поля глифа. Так, например, если все 3 точки примера (1,0, 0,7), (1,0, 0,7+эпсилон) и (1,0, 0,7-эпсилон) находятся на одном и том же расстоянии, то градиент равен (масштаб, 0).

Хотя это определенно неэффективно, можно использовать смещения. Я использую react-three-fiber , который использует troika-text с пакетом drei, но решил поделиться на случай, если другие будут искать решение в качестве временной меры, пока библиотека не поддерживает инсульт изначально:

import React from 'react';
import {Text} from 'drei';

const StrokedText: React.FC<
  {
    strokeWidth?: number;
    strokeColor?: string;
    strokeResolution?: number;
    bold?: boolean;
  } & any
> = ({
  strokeWidth = 1,
  strokeColor: color = '#000000',
  strokeResolution = 100,
  position,
  bold,
  ...props
}) => {
  const font = bold ? FONTS.BOLD : undefined;
  const sharedProps = {
    ...props,
    font,
    color,
    sdfGlyphSize: 12,
    debugSDF: true,
  };

  let zOffset = 0;
  const offset = () => (zOffset += 0.001);

  return (
    <group name="Stroked Text" position={position}>
      {Array(strokeWidth)
        .fill({})
        .map((_, i) => {
          const s= i / strokeResolution;
          return (
            <React.Fragment key={i}>
              {/* <Text {...sharedProps} position={[-s, 0, offset()]} />*/}
              {/* <Text {...sharedProps} position={[s, 0, offset()]} />*/}
              <Text {...sharedProps} position={[0, -s, offset()]} />
              <Text {...sharedProps} position={[0, s, offset()]} />
              <Text {...sharedProps} position={[-s, -s, offset()]} />
              <Text {...sharedProps} position={[s, s, offset()]} />
              {/* <Text {...sharedProps} position={[-s, s, offset()]} /> */}
              {/* <Text {...sharedProps} position={[s, -s, offset()]} /> */}
            </React.Fragment>
          );
        })}
      <Text name="Text" {...props} position={[0, 0, strokeWidth ? offset() : 0]} />
    </group>
  );
};

export default StrokedText;

Затем в другом месте я могу вызвать его с <StrokedText /> вместо <Text /> обычно:

<StrokedText
  color="#ffffff"
  fontSize={fontSize}
  clipRect={[-0.5, -0.15, 0.5, 0.15]}
  textAlign="center"
  position={[0, 0, 0.01]}
>
  {text}
</StrokedText>

image

@FunMiles У меня наконец-то появилось время, чтобы проанализировать ваши предложения. Я думаю, что это имело бы смысл, предполагая, что весь прямоугольник SDF глифа должен содержать полезные (> 0,0) значения расстояния. В настоящее время это не так; Я думаю, вы поняли это в своем продолжении о «x», где SDF падает до нуля в пределах границ четырехугольника, поэтому есть значительные области, где нет полезного градиента расстояния для экстраполяции:

image

Возможно, я могу изменить генератор SDF, чтобы обеспечить ненулевой градиент по всему квадрату...? Мысли вслух, это может иметь коннотации для точности расстояния и может привести к артефактам между персонажами в текстуре атласа... 🤔

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

Я пробовал это, и да, это позволяет экстраполировать поле расстояний за пределы четырехугольника, но, как я и опасался, это значительно снижает качество самого глифа. Этого следовало ожидать только с 8-битным градиентом; распространение его на большее расстояние приводит к снижению точности в каждом текселе. :(

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

кодируется во второй канал текстуры.

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

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

Вернемся к технической стороне. Давайте возьмем изображение X в вашем ответе. Я правильно понимаю, что для всех глифов это 64x64 пикселя? С 8 битами, если вам нужно кодировать все пространство расстояний, это означает, что у вас есть точность 2 _fractional_ бита. Я думаю, вы утверждаете, что этой точности недостаточно.

Возможно, есть трюк, который можно использовать для повышения точности при очень небольших затратах. Вместо того, чтобы делать сетку 64x64 из 8 бит, сжимайте данные блоков 2x2 в 32-битные данные. Одна очевидная особенность знакового расстояния между двумя пикселями заключается в том, что оно всегда находится в пределах [-1,1] справа и слева, вверх и вниз и [-sqrt(2), sqrt(2)] по диагонали. Итак, представьте, что вы используете 12 бит для SD центра блока 2x2, у вас будет 5 бит для каждого центра пикселей, чтобы закодировать разницу между их центром и центром 2x2. Эти 5 бит представляют расстояние не более sqrt(2)/2. Фактически у вас есть точность 5,5 бит.
12 битов центральной точки кодируют как минимум 6 бит точности на расстоянии максимум 64. Технически, если бы коробка имела только одну точку в углу, 12 бит должны были бы иметь возможность кодировать до sqrt (2) * 64, но Я не думаю, что это на самом деле возможно, так как поле предположительно разумно сосредоточено вокруг каждого глифа. На самом деле, чем дальше от любого края глифа и чем меньше информации должны кодировать дельты (когда вы находитесь далеко от края глифа, поле градиента становится почти однородным в окрестности), поэтому с точки зрения теории информации это даже можно улучшить кодировку
чтобы иметь больше фактической информации.... Но я бы не стал начинать с такой добавленной оптимизации.

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

Если вам нужна помощь, я мог бы реализовать кодирование/декодирование всего этого.

PS: я помню, как видел обсуждение в одном из README.md о более сложном SDF. Мне это приснилось? 😛 Может ли кто-нибудь указать мне на нее, чтобы посмотреть, может ли эта другая система помочь здесь?

@FunMiles Очень умная идея сжатия. Я буду иметь это в виду, если другие варианты не сработают. Потеря линейной интерполяции аппаратным обеспечением - это компромисс, на который я бы не хотел идти. 😉

Мне пришло в голову, что моя проблема с недостаточным количеством бит усугубляется использованием 0,5 в качестве «нулевого» расстояния, поэтому для кодирования расстояния _вне_ глифа доступна только половина альфа-значений. Я потенциально мог бы изменить это, чтобы использовать больше значений для внешних расстояний и меньше для внутренних расстояний, получая некоторую точность на периферии.

Ре. «более сложный SDF», вы можете иметь в виду «MSDF», где используются несколько цветовых каналов?

@FunMiles Очень умная идея сжатия. Я буду иметь это в виду, если другие варианты не сработают. Потеря линейной интерполяции аппаратным обеспечением - это компромисс, на который я бы не хотел идти. 😉

Я не думаю, что вам следует беспокоиться о потере этой интерполяции. Стоимость очень низкая, но преимущества постоянного наличия градиента (и даже небольшой кривизны), когда нет аппаратной поддержки, имеют большее значение, на мой взгляд. По сути, вы округляете точку выборки и получаете новое значение смещения для округленной точки выборки. Вычисление частичного покрытия фрагмента для сглаживания происходит так же, как обычно при наличии градиента.
Мне пришло в голову, что моя проблема с недостаточным количеством бит усугубляется использованием 0,5 в качестве «нулевого» расстояния, поэтому для кодирования расстояния _вне_ глифа доступна только половина альфа-значений. Я потенциально мог бы изменить это, чтобы использовать больше значений для внешних расстояний и меньше для внутренних расстояний, получая некоторую точность на периферии.

Это даст вам не более одного бита точности. Не чихать, но все же не так важно.

Ре. «более сложный SDF», вы можете иметь в виду «MSDF», где используются несколько цветовых каналов?
Это то, что я имел в виду. Я нашел проект GitHub об этом на C++. У вас было что-то, чтобы использовать это, или я просто запутался, смешав разные вещи, которые я искал две недели назад?

Кстати, не думаю, что MSDF поможет.
Могу ли я спросить, не могли бы вы получить небольшой файл .md, объясняющий, как вы создаете SDF в JavaScript? С этим, я думаю, я мог бы создать код для реализации моей идеи сжатия.

SDF построен здесь: https://github.com/protectwise/troika/blob/master/packages/troika-three-text/src/worker/SDFGenerator.js#L134 — особо объяснять нечего, в основном сопоставление текселей с единицами шрифта и запись измеренных расстояний в значения текселей. Мы должны были бы добавить некоторые дополнительные измерения расстояния для центральной точки каждого блока 2x2.

Пытаюсь полностью обдумать это... Воспроизведение билинейной интерполяции в GLSL выглядит просто/достаточно дешево, когда у вас есть 4 ближайших значения. Чтобы получить эти значения, когда они закодированы с помощью вашей схемы сжатия, я думаю, что это потребует:

  • Если точно по центру текселя: либо 2, либо 3 образца текстуры
  • Если между 4 текселями блока 2x2: 4 образца текстуры
  • Если между двумя соседними блоками: ~9 образцов текстуры~ либо 7, либо 8 образцов текстуры
  • Если между четырьмя соседними блоками: ~11 образцов текстуры~ 13 образцов текстуры

Я правильно понимаю?

О, подождите, я все еще думал об одноканальной текстуре. Используя четыре канала rgba, каждый блок данных считывается только один раз. Таким образом, максимум 4 образца текстуры.

Я начал читать SDFGenerator.js. Я посмотрю на это больше.

Я не думаю, что вам когда-либо потребуется больше, чем чтение одного текселя на фрагмент, если только вы не хотите специально улучшить альфа-смешивание для угловых случаев, когда вы можете оказаться в точке, окруженной краями глифов со всех сторон. Вам просто нужен ближайший центр блока 2x2, где находится центр фрагмента.
Тем не менее, я понимаю, что может возникнуть трудность, которую я не предвидел... WebGL 1.0 очень ограничен в том, что он предоставляет, что могло бы быть использовано здесь: Нет целочисленного типа без знака uint , даже 32-битных целых чисел! 🤯 и никаких побитовых операций... Таким образом, сжатие, которое я предлагаю, немного сложнее написать, используя 16-битные целые числа.

Все это доступно в WebGL 2.0, но я предполагаю, что здесь мы хотим ориентироваться на WebGL 1.0?

Все это доступно в WebGL 2.0, но я предполагаю, что здесь мы хотим ориентироваться на WebGL 1.0?

Я думаю, что было бы разумно ограничить структуру текста до webgl 2 и просто определить, совместим ли пользователь с этим браузером. Хотя я могу понять, что возможно сделать как оптимальную версию webgl 2, так и резервную версию webgl 1. Имо, мы могли бы начать с webgl 2 и позволить сообществу впитать его, прежде чем немедленно пытаться поддерживать оба.

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

эй @lojjic , ​​просто проверяю это. Есть ли прогресс или вещи, с которыми мы можем помочь?

Дополнительное примечание: Safari, наконец, начинает поддерживать WebGL2, поэтому можно не поддерживать WebGL1 для этой функции.

Наша реализация WebGL2 находится в достаточно хорошем состоянии, поэтому мы должны включить ее по умолчанию для более широкого тестирования.
https://trac.webkit.org/changeset/267027/вебкит

У меня есть iPhone 6, на котором никогда не будет WebGL 2.0 😒

Интересно, что в WebGL 1.0 требования даже хуже, чем я думал. Целых чисел на самом деле не существует:

4.1.3 Целые числа
Целые числа в основном поддерживаются как вспомогательное средство программирования. На аппаратном уровне реальные целые числа помогли бы
эффективная реализация циклов и индексов массива, а также ссылки на текстурные блоки. Однако нет
требование, чтобы целые числа в языке отображались на целочисленный тип в оборудовании. Не ожидается, что
базовое оборудование полностью поддерживает широкий спектр операций с целыми числами. Затенение OpenGL ES
Реализация языка может преобразовывать целые числа в числа с плавающей точкой для работы с ними. Следовательно, портативных нет.
поведение упаковки.

Однако я не терял надежды. Мне просто нужно немного переосмыслить...
Между тем, @lojjic , ​​не могли бы вы рассказать нам, какой альтернативный подход вы придумали?

@FunMiles Конечно. Я могу расширить поле расстояния до краев текстуры, сохраняя при этом достаточную точность формы глифа, кодируя значения расстояния с использованием нелинейной шкалы. Таким образом, расстояния очень близко к пути глифа (в пределах 1-2 текселей) имеют много значений для работы, в то время как дальше их меньше. Шейдер просто должен знать, как преобразовать обратно в исходное линейное расстояние. Правильная форма глифа выглядит великолепно, а более низкая точность экструдированных контуров едва заметна, так как они и так закруглены.

Я доказал это, используя как двухуровневую линейную шкалу, так и экспоненциальную шкалу. У обоих есть плюсы и минусы.

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

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

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

PS: Идея понизить точность вдали пришла мне в голову ранее сегодня после прочтения целочисленной чепухи WebGL... 😛 У меня все еще есть надежда, что технику сжатия можно будет реализовать в WebGL 1.0, чтобы дешево получить 11 бит точности и несколько больше битов с большим количеством тестов. т.е. используя rgba , a может содержать 8 бит среднего значения, тогда для каждого из rgb они будут подписаны, и знак будет представлять 1 бит точности для каждого среднее значение, затем, наконец, остаток, который будет находиться в диапазоне 0-127, будет иметь значение 64, вычтенное для представления 7 битов для 3 из 4 необходимых значений. Фактическим значением будет центр (от 0 до 2^11-1) + dist_i. Четвертое значение будет восстановлено путем вычитания их суммы, поскольку сумма должна быть средней. Можно было бы получить только 11 бит, но этого, вероятно, достаточно. Чтобы получить больше битов, потребуется проверить, меньше ли значение или r , g и b -64, положительное или отрицательное, больше 64. Это по существу дайте 14 бит для центра и только 6 бит для разностей. Наверное хороший компромисс? Мне нужно проверить, что очень слабые гарантии, которые WebGL 1.0 ставит на точность, не будут мешать этому мышлению....

Да, это то, что я имел в виду, области, где и U, и V находятся за пределами 0-1. Спасибо, попробую.

@lojjic В качестве дополнительного вопроса в предыдущем посте вы, похоже, были обеспокоены наличием двух значений текстуры на тексель SDF из-за памяти. Хотя я сам предпочитаю уменьшать память, 256 глифов в сетке 64x64 потребляют всего 1/4 МБ памяти текстур. Я знаю, что для некоторых языков может потребоваться гораздо больше глифов, но, тем не менее, BBC утверждает, что для чтения газеты необходимо всего 2000/3000 символов. Таким образом, 4000 символов составляют 4 МБ с одним байтом на тексель SDF. Стоит беспокоиться?

@FunMiles Справедливое замечание, и на самом деле меня это не очень беспокоило.

Осталось выполнить кое-какую выдающуюся работу, но b19cd3aff4b0876253d76b3fc66b7e2a1f16a7e5 решает эту проблему. Для тех, кто использует react-three-fiber, это было выпущено в drei v2.2.0.
https://github.com/pmndrs/drei/pull/156

Я знаю, что это закрыто, но я хочу сказать спасибо за работу. Я заставил его работать в моем приложении. Потребовалось немного выяснить, что свойства не будут принимать просто число для размера контура. Однако результат именно то, что мне было нужно.
Screen Shot 2020-11-09 at 7 44 52 PM

@FunMiles Красиво выглядит! Вы _должны_ иметь возможность использовать числовое значение для outlineWidth , поэтому, если это по какой-то причине не работает для вас, дайте мне знать.

И спасибо за ваш вклад на этом пути. Мне все еще не удавалось получить плавную экстраполяцию расстояния за пределы четырехугольника, используя технику, которую вы упомянули, поэтому толстый контур в настоящее время имеет шероховатые кусочки, в частности, в углах, но этого достаточно для большинства случаев (до ~ 10% от размера шрифта). Я определенно открыт для того, чтобы попытаться усовершенствовать это до сих пор.

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