Эффективный способ рисования контуров вокруг спрайтов

21

Я использую XNA для программирования игры и экспериментирую с различными способами достижения «выбранного» эффекта на моих спрайтах. Проблема, с которой я сталкиваюсь, заключается в том, что каждый кликабельный, который рисуется в спрайте, рисуется с использованием более чем одного спрайта (каждый объект может состоять из до 6 спрайтов).

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

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

  • Грег.
Greg
источник

Ответы:

20

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

  • Обычная версия
  • «Толстая», неокрашенная версия - в основном белая версия вашего спрайта X-много пикселей «толще», чем оригинал.

Нарисуйте весь объект, используя «толстую» версию, затем нарисуйте обычную версию поверх.

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

Чтобы сгенерировать "жирную" версию, я рекомендую написать расширение Content Pipeline. которое может автоматически принимать ваши исходные спрайты, читать их альфа-канал, создавать новый альфа-канал путем выборки максимального альфа-канала в исходном изображении X-много пикселей вокруг каждого пикселя, и установка RGB = (1,1,1).

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

Если у вас есть только несколько спрайтов, то вы можете просто использовать хороший редактор изображений (GIMP, Photoshop) и делать это вручную: альфа-канал для выделения, расширение выделения, выделение до альфы, заливка цветовых каналов белым.

Эндрю Рассел
источник
4

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

Iain
источник
11
Я слышал, что этот вид шейдеров на удивление раздражает писать (мы использовали его в одной из наших 3D-игр и продолжали сталкиваться с неприглядными крайними случаями). Одной вещью, которую вы можете рассмотреть, является кодирование контура непосредственно в текстуру спрайта с определенным цветом, например (0, 255, 255, 0), и просто сделайте так, чтобы шейдер преобразовал этот цвет, когда выбран спрайт. Затем шейдер выполняет тривиальное изменение цвета, и вы имеете высокий уровень контроля художника над контуром и любыми деталями, которые вы хотите.
4

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

when selected:
   outline = new sprite canvas of appropriate size
   for sprite in object:
      # use larger numbers for thicker outlines
      for x in (-1, 0, 1) and y in (-1, 0, 1):
         render sprite mask into canvas at x,y with desired color

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

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

Самый простой подход грубой силы состоит в создании двух копий каждого спрайта, обычного и выделенного. Затем просто поменяйте их местами, когда они выделены.

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

wkerslake
источник
Я думаю, что часть проблемы в том, что ОП говорит, что каждый объект может быть сделан из нескольких спрайтов. Поэтому я предполагаю, что контур должен быть контуром объединенного объекта, а не иметь отдельный контур вокруг каждого спрайта компонента.
Крис Хоу
1
Крис, это не обязательно должен быть контур объединенного объекта, и он будет отлично работать для объекта, созданного из нескольких спрайтов. Просто сделайте в точности то, что сказал wkerslake, но убедитесь, что вы рисуете выделенные спрайты за всеми нормальными спрайтами для этого объекта. Таким образом, независимо от того, сколько спрайтов сделано из объекта, выделение будет использовать только чуть более чем удвоенное время рисования для этого объекта, что должно быть намного меньше, чем генерация контуров во время рендеринга с объединенными спрайтами.
AttackingHobo
1

Как насчет каждого спрайта, также есть другой спрайт, который является контуром базового спрайта. При рисовании контура объекта нарисуйте базовые спрайты, затем создайте маску комбинированного рендеринга, затем нарисуйте спрайты контура, исключая маску.

серый
источник
2
Почему бы сначала не нарисовать контурные спрайты и нарисовать над ними обычные спрайты?
Крис Хоу
Одна из причин не делать этого (или предложение Криса) состоит в том, что он использует в два раза больше памяти текстур; Другая причина в том, что рабочий процесс Artist - это дерьмо, потому что вам нужно обновлять два файла каждый раз, когда вы меняете спрайт.
2
Ну да, в идеале у вас не будет двух реальных спрайтов. Если вы используете альфа-тестирование для своих спрайтов, вы можете поместить контур в спрайт и дать ему альфа-значение немного выше, чем ваши прозрачные области. Затем, управляя эталонным значением альфа-теста, вы можете контролировать, является ли контур видимым или нет. Все, что я говорил, это то, что если у вас есть 2 версии спрайтов (будь то 2 ресурса или 2 состояния одного и того же ресурса), вы должны сначала нарисовать схему, а затем обычную версию. Таким образом, вам не нужны какие-либо модные шейдеры, маски или что-то еще.
Крис Хоу
1
Идея Криса имеет свои достоинства; кроме того, можно избежать обновления художником 2 файлов (или даже одного и того же ресурса), если вы создадите инструмент для генерации альфа-канала для спрайтов контура, который выполняется как часть вашего актива / конвейера контента. Память текстур может или не может быть проблемой, но отдельные спрайты контура должны быть сжимаемыми DXT; возможно 1/6 размера оригинальной текстуры в видеопамяти и без потери точности.
jpaver
1

Несколько разных решений с разными компромиссами.

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

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

Лучшее: мое мнение, но создание представления вашего объекта в поле со знаком расстояния (SDF), вероятно, будет лучшим решением. Эти текстуры могут быть намного меньше исходной текстуры и при этом сохранять полезные данные. По сути, каждый пиксель кодирует, как далеко он находится от объекта, используемого для его генерации. Имея эти данные в руках, вы можете написать все виды эффектов от свечения до контуров. Граница может измениться по размеру, цвету и т. Д., И все еще является относительно дешевым шейдером и только одним дополнительным рисунком. Недостатком является инструментальная обработка и предварительная обработка.

Aku
источник
0

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

РЕДАКТИРОВАТЬ: Однако, как вы можете видеть из комментариев, это не очень хорошая идея.

Коммунистическая утка
источник
1
Простое увеличение спрайта не дает истинного плана
Iain
2
Я не думаю, что это будет, но это будет легко.
Коммунистическая утка
2
Многое легко, но не решает проблему. Для спрайтов с любым интересным силуэтом масштабирование приведет к абсолютному мусору.
Масштабирование будет хорошо смотреться только для сплошных квадратных или круглых объектов. Если форма действительно широкая, высокая или имеет зазоры, она будет выглядеть очень плохо.
AttackingHobo
3
Увеличение масштаба даст лучшие результаты, чем бездействие, а также будет полезным шагом на пути к «совершенству». Хотя графические результаты не будут идеальными, если бы это было для внутреннего инструмента, это может быть достаточно хорошо.
дэш-том-бэнг
0

Я согласен с расширением спрайта. Безусловно, самый простой маршрут, и вы можете применить его к выбору ЛЮБОГО спрайта без необходимости создавать дополнительные спрайты специально для этой цели.

  • т.е. spriteOutline.Scale = spriteOriginal.Scale * 0.1f; spriteOutline.Color = новый цвет (255, 0, 0, 255);

источник
0

Замените цвет исходного спрайта на цвет контура (или даже подкрасьте его, если хотите). Визуализируйте этот плоский или окрашенный спрайт четыре раза со смещением в 1 пиксель: при x, y = (- 1, -1), затем (+ 1, -1), затем (-1, + 1), затем (+1 +1). Повторите для всех спрайтов, которые составляют объект.

После этого верните исходные спрайты в правильном порядке сверху в (0,0).

Илья Боссов
источник