- Шум #
- высота #
- частота
- октав
- передел
- Биомы #
- климат #
- острова #
- Хребет шум #
- Террасы #
- Размещение дерева #
- Свернуть карты #
- Бесконечность не предел #
- Реализация #
- Детская площадка #
- мысли #
- Больше #
Одна из самых популярных страниц на моем сайте о генерация полигональных карт [1]. Создание этих карт было большой работой. Я не начал там. Я начал с чего-то гораздо более простого, который я опишу здесь. Более простая техника может сделать такие карты менее чем за 50 строк кода:
Я не собираюсь объяснять, как рисовать эти карты; это будет зависеть от вашего языка, графической библиотеки, платформы и т. д. Я только объясню, как заполнить массив данными о высоте и карте биома.
Шум #
Распространенным способом создания 2D-карт является использование функции шума с ограниченной пропускной способностью, такой как шум Perlin или Simplex, в качестве строительного блока. Вот как выглядит функция шума:
Каждому местоположению на карте мы присваиваем число от 0,0 до 1,0. На этом изображении 0.0 - черный, а 1.0 - белый. Вот как установить цвет в каждом месте сетки в C-подобном синтаксисе:
for (int y = 0; y <высота; y ++) {for (int x = 0; x <ширина; x ++) {double nx = x / ширина - 0,5, ny = y / высота - 0,5; значение [y] [x] = шум (nx, ny); }}
Цикл будет работать одинаково в Javascript, Python, Haxe, C ++, C #, Java и большинстве других популярных языков, поэтому я покажу его в C-подобном синтаксисе, и вы сможете преобразовать его в язык, который вы используете. В оставшейся части урока я покажу, как изменяется тело цикла (значение [y] [x] =… line) по мере добавления новых функций. В конец Я покажу полный пример.
В зависимости от используемой вами библиотеки вам, возможно, придется сдвинуть или умножить полученные значения, чтобы они соответствовали диапазону от 0,0 до 1,0. Некоторые библиотеки возвращают от 0,0 до 1,0; некоторые возвращают -1,0 до +1,0; некоторые возвращают другие диапазоны, например, от -0,7 до +0,7. Некоторые не говорят, что они возвращают, поэтому вам, возможно, придется взглянуть на возвращаемые значения, чтобы выяснить диапазон.
высота #
Шум сам по себе - это просто набор цифр. Нам нужно придать этому значение . Первое, о чем мы могли бы подумать, это привести шум в соответствие с высотой (также называемой «картой высот»). Давайте возьмем шум от предыдущего и нарисуем его как возвышение:
Код почти такой же, за исключением того, что находится внутри внутреннего цикла; теперь это выглядит так:
высота
[y] [x] = шум (nx, ny);
Да это оно. Данные на карте те же, но теперь я называю это возвышением вместо значения.
Много холмов, но не намного больше. В чем дело?
частота
Шум может генерироваться на любой частоте . Пока я выбрал только одну частоту. Давайте посмотрим на эффект частоты. Попробуйте переместить ползунок, чтобы увидеть, что происходит на разных частотах:
частота =
Это просто увеличение и уменьшение. На первый взгляд это не очень полезно, но это так. я имею другой учебник это объясняет теорию : такие как частота, амплитуда, октавы, розовый и синий шум и т. д.
высота [y] [x] = шум ( freq * nx, freq * ny);
Иногда полезно думать о длине волны , которая является обратной к частоте. Удвоение частоты делает все в два раза меньше. Удвоение длины волны делает все в два раза больше. Длина волны - это расстояние, измеряемое в пикселях, мозаиках или метрах или как вы используете для своих карт. Это связано с частотой: длина волны = карта_размер / частота.
октав
Чтобы сделать карту высоты более интересной, мы добавим шум на разных частотах :
+ + знак равно высота [y] [x] = 1 * шум ( 1 * nx, 1 * ny); + 0,5 * шум ( 2 * nx, 2 * ny); + 0,25 * шум ( 4 * nx, 2 * ny);
Давайте смешаем большие низкочастотные холмы и маленькие высокочастотные холмы в одной карте. Переместите ползунок, чтобы добавить меньшие холмы к смеси:
Теперь это больше похоже на фрактальную местность, которую мы хотим! Теперь мы можем получить холмы и скалистые горы, но у нас все еще нет плоских долин. Нам нужно что-то еще для этого.
передел
Функция шума дает нам значения от 0 до 1 (или от -1 до +1 в зависимости от того, какую библиотеку вы используете). Чтобы сделать плоские долины, мы можем поднять высоту до уровня . Переместите ползунок, чтобы попробовать разные показатели.
exp = e = 1 * noise (1 * nx, 1 * ny); + 0,5 * шум (2 * nx, 2 * ny); + 0,25 * шум (4 * nx, 4 * ny); высота [у] [х] = Math.pow ( е , показатель степени) ;
Более высокие значения толкают средние возвышения в долины, а более низкие значения подтягивают средние возвышения к горным вершинам. Мы хотим оттолкнуть их. Я использую здесь степенные функции, потому что они простые, но вы можете использовать любую кривую, какую захотите; У меня есть более интересное демо Вот ,
Теперь, когда у нас есть разумная карта высот, давайте добавим несколько биомов!
Биомы #
Шум дает нам цифры, но нам нужна карта с лесами, пустынями и океанами. Первое, что нужно сделать, это сделать низкие возвышения в воде:
вода = функция биома (е) {если (е <уровень воды) вернуть ВОДУ; остальное вернуть ЗЕМЛЯ; }
Эй, это начинает выглядеть как процедурный мир! У нас есть вода, трава и снег. Что если мы хотим больше вещей? Составим последовательность воды, пляжа, луга, леса, саванны, пустыни, снега:
Рельеф, основанный только на высоте, функция биома (e) {если (e <0.1) вернуть ВОДУ; иначе, если (e <0,2) вернуть BEACH; иначе, если (e <0,3) вернуть FOREST; иначе, если (e <0,5) вернуть JUNGLE; иначе, если (e <0,7) вернуть SAVANNAH; иначе, если (e <0,9) вернуть DESERT; остальное вернуть СНЕГ; }
Эй, выглядит круто! Вы хотите изменить номера и биомы для вашей игры. У Crysis будет намного больше джунглей; У Скайрима будет намного больше льда и снега. Но независимо от того, на что вы меняете числа, этот подход немного ограничен. Типы местности совпадают с отметками, поэтому они образуют полосы. Чтобы сделать его более интересным, нам нужно выбрать биомы с чем-то другим, кроме высоты. Давайте создадим вторую карту шума для «влажности»:
Шум высоты слева; шум влаги справа
Теперь давайте использовать и высоту и влажность. На диаграмме слева внизу ось Y - это высота (первая диаграмма выше), а ось X - влажность (вторая диаграмма выше). Создает разумно выглядящую карту:
Ландшафт основан на двух значениях шума
Низкие возвышенности - это океаны и пляжи. Высокие возвышенности скалистые или снежные. Между ними мы получаем широкий спектр биомов. Код выглядит так:
функция биома (е, м) {если (е <0,1) вернуть ОКЕАН; if (e <0.12) возвращает BEACH; if (e> 0.8) {if (m <0.1) вернет SCORCHED; if (m <0,2) вернуть BARE; if (m <0.5) вернуть TUNDRA; вернуть СНЕГ; } if (e> 0.6) {if (m <0.33) return TEMPERATE_DESERT; if (m <0.66) вернуть SHRUBLAND; возврат ТАЙГА; } if (e> 0.3) {if (m <0.16) return TEMPERATE_DESERT; if (m <0.50) вернуть GRASSLAND; if (m <0,83) return TEMPERATE_DECIDUOUS_FOREST; return TEMPERATE_RAIN_FOREST; } if (m <0,16) return SUBTROPICAL_DESERT; if (m <0.33) вернуть GRASSLAND; if (m <0.66) return TROPICAL_SEASONAL_FOREST; return TROPICAL_RAIN_FOREST; }
Вы хотите изменить все эти числа в соответствии с потребностями вашей собственной игры.
В качестве альтернативы, если вам не нужны биомы, сгладьте градиенты (см. Эта статья [2]) может производить цвета:
С биомами или градиентами одно значение шума не дает достаточного разнообразия, но два довольно хороши.
климат #
В предыдущем разделе я использовал повышение в качестве показателя температуры . Более высокие возвышения имеют более низкие температуры. Однако широта также влияет на температуру. Давайте использовать и высоту и широту для контроля температуры:
экватор: горячий холод
столбы: горячий холод
Вблизи полюсов (высокие широты) климат холоднее, а на вершинах гор (высокие возвышения) климат также холоднее. Я еще ничего не сделал с этим; есть много настроек, необходимых для правильной настройки этих параметров.
Там также сезонные колебания климата. Летом и зимой северное и южное полушария становятся теплее и холоднее, но экватор не так сильно меняется. Здесь можно сделать гораздо больше, например, моделирование преобладающих ветровых и океанических течений, влияние биома на климат и сдерживающее влияние океана на температуру.
острова #
Для некоторых проектов я хочу, чтобы границы карты были водными. Один из способов сделать это - создать карту, как указано выше, а затем изменить ее форму .
До и после изменения формы
Как это работает? На виде сбоку исходная шумовая местность вписывается в содержащий прямоугольник. Чтобы сделать острова, мы превращаем контейнер в нечто подобное. Это толкает середину на землю, а края - в воду.
Простейшая форма - это e = (1 + e - d) / 2, где 0 ≤ d ≤ 1 - расстояние от центра (либо Манхэттен, либо Евклид, либо смесь). Эта форма гарантирует, что середина находится на суше, а края - в воде. Это слишком агрессивно, но это простое место для начала.
Если вы хотите создать свою собственную форму, которая соответствует тому, что вы хотите от островов, вы можете определить две функции формы. Выберите нижнюю фигуру, чтобы подтолкнуть карту вверх, и верхнюю, чтобы подтолкнуть карту вниз . Эти формы являются функциями от расстояния d до высоты 0-1. После того, как вы спроектировали эти формы, измените шум на остров, используя e = нижний (d) + e * (верхний (d) - нижний (d)).
В нижней части фигуры вообще не толкаются, поэтому середина карты не обязательно будет на земле. Это позволяет большему количеству лежащего в основе Perlin / Simplex шума просвечивать. Верхняя форма отталкивает что-нибудь от середины карты. Верхняя форма менее агрессивна, толкает вниз только около краев, а нижняя форма слегка подталкивает вверх около середины. Есть много других форм, чтобы попробовать!
Зачем вообще придерживаться стандартных математических функций? Как я исследовал в моем статья о повреждении РПГ [3], каждый (включая меня) использует математические функции, такие как полиномы, экспоненты и т. Д., Но на компьютере мы этим не ограничиваемся. Мы можем нарисовать любую форму и использовать ее здесь. Поместите нижнюю и верхнюю фигуры в справочные таблицы и используйте их в нижних (d), верхних (d) функциях.
Хребет шум #
Вместо того, чтобы повышать высоту до степени, мы можем использовать абсолютное значение для создания острых выступов:
функция ridgenoise (nx, ny) {return 2 * (0.5 - abs (0.5 - noise (nx, ny))) ; }
Чтобы добавить октавы, мы можем варьировать амплитуды более высоких частот так, чтобы только горы получали дополнительный шум:
e0 = 1 * ридгенуаз (1 * nx, 1 * ny); e1 = 0,5 * ридгенуаз (2 * nx, 2 * ny) * e0 ; е2 = 0,25 * ридгеноаз (4 * nx, 4 * ny) * (e0 + e1) ; е = е0 + е1 + е2; высота [у] [х] = Math.pow (е, показатель степени);
п =
У меня нет большого опыта в этой технике, и мне придется больше с ней играть, чтобы научиться правильно ее использовать. Также может быть интересно смешать низкочастотный высокочастотный шум с высокочастотным шумом.
Террасы #
Если мы округляем высоту до ближайшего уровня, мы получаем террасы:
п =
Это приложение функций перераспределения возвышения вида e = f (e). Ранее мы устанавливали e = Math.pow (e, показатель степени), чтобы сделать горные вершины круче; здесь мы используем e = Math.round (e *) / для создания террас. При использовании функции, отличной от ступенчатой, террасы могут быть более круглыми или встречаться только на некоторых возвышениях.
Размещение дерева #
Мы обычно используем фрактальный шум для высоты и влажности, но он также может быть использован для размещения неравномерно расположенных объектов, таких как деревья и камни. Для повышения у нас есть более высокие амплитуды с более низкими частотами («красный шум»). Для размещения объекта мы хотим использовать более высокие амплитуды с более высокими частотами («синий шум»). Слева синяя картина шума; справа находятся места, где шум больше, чем близлежащие значения:
R = для (int yc = 0; yc <высота; yc ++) {для (int xc = 0; xc <ширина; xc ++) {double max = 0; // есть более эффективные алгоритмы, чем это для (int yn = yc - R; yn <= yc + R; yn ++) {для (int xn = xc - R; xn <= xc + R; xn ++) {double e = значение [уп] [х]; if (e> max) {max = e; }}} if (value [yc] [xc] == max) {// поместить дерево в xc, yc}}}
Выбирая разные R для каждого биома, мы можем получить переменную плотность деревьев:
Круто, что шум Perlin / Simplex можно использовать для размещения деревьев, но другие алгоритмы, такие как сетки с дрожанием, диски Пуассона, тайлы Ванга или сглаживание графики, часто более эффективны и также обеспечивают более равномерное распределение синего шума.
Свернуть карты #
Иногда мы хотим, чтобы восточный край карты совпадал с западным краем. Это соответствует цилиндру в трехмерном пространстве. Мы можем реализовать это с небольшим изменением. Мы будем интерпретировать значение x на плоской карте как угол в мире цилиндров. Затем мы конвертируем угол в декартовы координаты. Чтобы также совместить север и юг, мы можем применить тот же шаблон снова, чтобы превратить значение y в угол, и посмотреть в 4d шумовом пространстве. Давайте посмотрим, как карты выглядят рядом с их копиями:
Только восток + запад и север + юг + восток + запад
Первый из них охватывает восток-запад, но не север-юг. Второй охватывает все четыре направления. Вот некоторый код:
const TAU = 2 * M_PI; функция cylindernoise (double nx, double ny) {double angle_x = TAU * nx; / * В «пространстве параметров шума» нам нужно nx и ny, чтобы пройти одно и то же расстояние. Окружность, созданная из nx, должна иметь окружность = 1, чтобы соответствовать длине = 1 линии, созданной из ny, что означает, что радиус окружности равен 1 / 2π, или 1 / tau * / обратный шум 3D ( cos (angle_x) / TAU, sin (angle_x) / TAU, ny); } функция torusnoise (double nx, double ny) {double angle_x = TAU * nx, angle_y = TAU * ny; возвратный шум 4D (cos (angle_x) / TAU, sin (angle_x) / TAU, cos (angle_y) / TAU, sin (angle_y) / TAU ); }
Я еще не экспериментировал с этим. Взгляни на Руководство Рона Вальстара [4] для более подробного объяснения.
Бесконечность не предел #
Расчет биома в положении (x, y) не зависит от расчетов в любой другой позиции. Этот локальный расчет приводит к двум хорошим свойствам: он может быть рассчитан параллельно, и его можно использовать для бесконечной местности. Наведите курсор на миникарту слева, чтобы создать карту справа. Мы можем сгенерировать любую часть карты, не создавая (или не сохраняя) все это.
Реализация #
Использование шума для создания ландшафта является популярной техникой, и вы можете найти учебные пособия для разных языков и платформ. Код генерации карты очень похож на разных языках. Вот самый простой цикл на трех разных языках:
- JavaScript: let gen = new SimplexNoise (); function noise (nx, ny) {// Изменение масштаба с -1,0: +1,0 до 0,0: 1,0 return gen.noise2D (nx, ny) / 2 + 0,5; } let value = []; for (пусть y = 0; y <высота; y ++) {value [y] = []; для (пусть x = 0; x <ширина; x ++) {пусть nx = x / ширина - 0,5, ny = y / высота - 0,5; значение [y] [x] = шум (nx, ny); }}
- C ++: module :: Perlin gen; double noise (double nx, double ny) {// Масштабирование с -1.0: +1.0 до 0.0: 1.0 return gen.GetValue (nx, ny, 0) / 2.0 + 0.5; } двойное значение [высота] [ширина]; for (int y = 0; y <высота; y ++) {for (int x = 0; x <ширина; x ++) {double nx = x / ширина - 0,5, ny = y / высота - 0,5; значение [y] [x] = шум (nx, ny); }}
- Python: из открытого открытия импорта в диапазоне (высота): значение.append ([0] * ширина) для x в диапазоне (ширина): nx = x / ширина - 0,5 ny = y / высота - 0,5 значение [y] [x] = шум (nx, Нью-Йорк)
Если у вас есть библиотека шума, все они очень похожи. Пытаться opensimplex для Python [5] или libnoise для C ++ [6] или симплекс-шум [7] для Javascript. Для большинства популярных языков существует множество библиотек шумов. В качестве альтернативы вы можете захотеть потратить время на изучение того, как работает шум Perlin, или на реализацию его самостоятельно. Я не
После того, как вы нашли библиотеку шума для вашего любимого языка, детали будут различаться (некоторые будут возвращать числа от 0,0 до 1,0, а другие от -1,0 до +1,0), но основная идея остается той же. Для реального проекта вы можете захотеть обернуть функцию шума и объект gen в класс, но эти детали здесь не имеют значения, поэтому я сделал их глобальными.
Для этого простого проекта не имеет большого значения, используете ли вы шум Перлина, симплексный шум, шум OpenSimplex, значение шума, смещение средней точки, алмазное смещение или обратное преобразование Фурье. У каждого из них есть свои плюсы и минусы, но все они дают достаточно похожие результаты для этого типа генератора карт.
Рисование карты будет зависеть от платформы и игры, поэтому я этого не предоставляю; Этот код предназначен только для создания возвышений и биомов, которые вы захотите нарисовать в любом стиле, который использует ваша игра. Не стесняйтесь копировать, переносить и использовать его для своих собственных проектов.
Детская площадка #
Я рассмотрел смешивание октав, поднятие высоты до мощности и объединение высоты и влажности, чтобы выбрать биом. Вот интерактивная диаграмма, которая позволяет вам играть со всеми этими параметрами, а затем показывает, как код составляется:
ехр =
Высота октав:
e1 =
e2 =
e3 =
e4 =
e5 =
e6 =
Влажность октав:
m1 =
м2 =
м3 =
m4 =
m5 =
m6 =
Вот код:
var rng1 = PM_PRNG.create (seed1); var rng2 = PM_PRNG.create (seed2); var gen1 = новый SimplexNoise (rng1.nextDouble.bind (rng1)); var gen2 = новый SimplexNoise (rng2.nextDouble.bind (rng2)); function noise1 (nx, ny) {return gen1.noise2D (nx, ny) / 2 + 0,5; } function noise2 (nx, ny) {return gen2.noise2D (nx, ny) / 2 + 0.5; } for (var y = 0; y <высота; y ++) {for (var x = 0; x <ширина; x ++) {var nx = x / ширина - 0,5, ny = y / высота - 0,5; var e = (* noise1 (1 * nx, 1 * ny) + * noise1 (2 * nx, 2 * ny) + * noise1 (4 * nx, 4 * ny) + * noise1 (8 * nx, 8 * ny ) + * noise1 (16 * nx, 16 * ny) + * noise1 (32 * nx, 32 * ny)); е / = (+++++); e = Math.pow (e,); var m = (* noise2 (1 * nx, 1 * ny) + * noise2 (2 * nx, 2 * ny) + * noise2 (4 * nx, 4 * ny) + * noise2 (8 * nx, 8 * ny ) + * noise2 (16 * nx, 16 * ny) + * noise2 (32 * nx, 32 * ny)); m / = (+++++); / * нарисовать биом (e, m) в точке x, y * /}}
Немного сложнее: вам нужно использовать разные семена для повышения уровня шума и влажности. В противном случае они будут одинаковыми, и ваши карты будут выглядеть не так интересно. В Javascript я использую библиотека prng-parkmiller [8]; в C ++ вы можете использовать два отдельных объекты linear_congruential_engine [9]; в Python вы можете создать два отдельных экземпляра Случайный. Случайный класс [10].
Еще один хитрый момент: после объединения нескольких октав шума диапазон выходного сигнала может оказаться не таким, как вы ожидали, и вам, возможно, придется добавить / умножить выходное значение в желаемый диапазон (например, от 0,0 до 1,0). Скотт Тернер имеет написано немного больше о распространенных проблемах с шумом [11].
мысли #
Что мне нравится в этом подходе к генерации карт, так это то, что он прост . Это быстро. Это очень мало кода для получения достойных результатов.
Что мне не нравится в этом подходе, так это то, что он ограничен. Локальный расчет означает, что каждое местоположение не зависит от любого другого местоположения. Различные области карты не связаны друг с другом . Каждое место на карте ощущается одинаково. Не существует глобальных ограничений, таких как «должно быть от 3 до 5 озер» или глобальных особенностей, таких как река, текущая от вершины самого высокого пика до океана. Еще одна вещь, которая мне не нравится, это то, что для того, чтобы получить то, что вам нравится, требуется много настроек параметров.
Почему я рекомендую это тогда? Я думаю, что это хорошая отправная точка, особенно для инди-игр или игровых джемов. Двое из моих друзей написали первоначальную версию Царство безумного Бога [12] в течение 30 дней, для игровой конкурс [13]. Они попросили меня помочь им сделать карты. Я использовал эту технику (плюс некоторые дополнительные функции, которые оказались не очень полезными), чтобы сделать карту для них. Месяцы спустя, после получения отзывов от игроков и более тщательного изучения игрового дизайна, мы разработали более продвинутый генератор карт с использованием полигонов Вороного, описанных Вот [14]. Этот генератор карт не использует методы, описанные на этой странице, но использует шум совсем по-другому для создания карт.
Повышение уровня шума - это весело и легко начать, но вы можете быстро выйти за пределы. Скотт Тернер имеет проницательное эссе с причинами использования альтернатив [15].
Больше #
Есть много интересных вещей, которые вы можете сделать с помощью шумовых функций. Если вы будете искать в Интернете, вы увидите такие варианты, как турбулентность, вал, мультифрактал с ребрами, демпфирование амплитуд, террасы, шум вороного, аналитические производные, деформация домена и другие. Взгляни на эта страница [16] для вдохновения. Я не прикрываю их здесь; вместо этого я сосредоточен на простоте для этой статьи.
Мои предыдущие проекты создания карт, которые повлияли на этот:
- Я использовал общий шум Перлина для мой первый генератор карт Царства Безумного Бога [17]. Мы использовали это в течение первых шести месяцев альфа-тестирования, а затем заменили его специально разработанным Генератор карт полигонов Вороного [18] на основе потребностей игрового процесса, которые мы определили во время альфа-тестирования. Биомы в этой статье и их цвета взяты из этих проектов.
- Во время изучения обработки сигналов для аудио я написал учебник по шуму это охватывает такие понятия, как частота, амплитуда, октавы и «цвета» шума. Те же самые понятия, которые работают для аудио, также применимы к генерации карт на основе шума. Я сделал некоторые неполированные демонстрации поколения местности в то время, но никогда не заканчивал их.
- Иногда я экспериментирую, чтобы найти пределы. Я хотел посмотреть, как мало кода мне не помешает, пока я создаю разумные карты. В этот мини-проект Я дошел до нуля строк кода - это все фильтры изображений (турбулентность, порог, цветовые градиенты). Я был доволен и обеспокоен этим. Сколько генерации карт можно сделать с помощью фильтров изображений? Достаточно много. Все в «гладкой градиентной цветовой схеме», которую я описал ранее, происходит из этого эксперимента. Слой шума представляет собой фильтр турбулентного изображения; октавы - это изображения, наложенные друг на друга; экспонента называется инструментом «корректировки кривых» в Photoshop.
Меня несколько беспокоит, что большая часть кода, который мы разработчики игр пишем для генерации ландшафта на основе шума (включая смещение средней точки), оказывается такой же, как фильтры звука и изображения. С другой стороны, он дает приличные результаты с очень небольшим количеством кода, поэтому я написал статью, которую вы читаете. Это простая и быстрая отправная точка . Я обычно не использую эти типы карт долго; Я заменю их генератором пользовательских карт, как только будет построена игра, и у меня будет лучшее представление о том, какие типы карт лучше всего соответствуют дизайну этой игры. Для меня это обычная модель: начните с чего-то чрезвычайно простого, затем заменяйте его только после того, как я лучше пойму систему, над которой работаю.
Есть много классных вещей, которые вы можете сделать с шумом, и я едва изучил их здесь. Попробуйте Студия Шума [19] для интерактивного изучения множества возможностей.
В чем дело?Что если мы хотим больше вещей?
Зачем вообще придерживаться стандартных математических функций?
Почему я рекомендую это тогда?
Сколько генерации карт можно сделать с помощью фильтров изображений?