Текстурирование спрайтов с помощью (dis)placement map


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

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

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

    Но посмотрев ресурсы, я увидел очень интересную штуку. Я увидел странного вида отрендеренные картинки:



    А также, различные текстуры такого вида:



    С текстурой все понятно. Но что за ерунда на первой картинке? Логика подсказывала, что это — какого-то рода карта искажений текстуры. Сначала я думал про какие-то искривления и карту нормалей… Но потом в голову пришла простая идея — displacement map. Это когда в картинке зашифрованы координаты сдвигов. Например, в красном канале — сдвиг по «x», а в синем — по «y».

    Раскидав ее по каналам, я получил это:



    И практически сразу все понял. Сразу видно, что синий канал — это явно альфа-канал.

    Посмотрев на зеленый канал и увидев на нем явный вертикальный градиент, стало очевидно, что в нем зашифрована y-координата. Ну, а красному не оставалось быть ничем иным, как иксом.

    Поползав «пипеткой» по картинке, подозрения подтвердились еще и тем, что каждая и R и G компонент менялась в диапазоне 0..127, в то время как картинка текстуры как раз и была размером 128х128 пикселей.

    Довольно быстро я набросал код, который делал следующее для каждого пикселя:
    color = map.getPixel(x, y);
    tX = color.R;
    tY = color.G;
    alpha = color.B;
    textureColor = texture.getPixel(tX, tY);
    dest.setPixel(x, y, textureColor, alpha);

    В результате получил почти то, что вижу в исходном проекте. Текстурированные фазы анимации человечка:



    Правда, механизм не совсем displacement (в случает дисплейсмента речь идет об относительных изменениях наложения текстуры). Здесь скорее нужно назвать просто «placement map». :)

    Можно еще накидать алгоритм сглаживания и использования соседних точек на текстуре — и все будет вообще шоколадно.

    Стоит отметить, что во флэше есть встроенный механизм DisplacementMapFilter, но он не подошел, т.к. он работает с относительными координатами (как, в прочем, и положено дисплейсменту). Т.е. в нашем случае он бы делал
    textureColor = texture.getPixel(x+tX, y+tY);

    Итого.


    Мне очень понравилась технология. Она позволяет получить кучу разноскиновых объектов высокого качества не применяя технологий 3D рендеринга и при этом иметь простую смену этих самых скинов.

    Уже планирую использовать это в своем проекте.

    Вопрос.


    Остается только вопрос — с помощью какого софта можно получить вот такие текстуры, и что самое главное — рендер подобных карт искажений???

    Был бы очень признателен хабрасообществу, если бы кто-нибудь поделился Знанием.

    P.S.


    Уже после публикации, понял, как можно получить подобный рендер даже без каких-то специальных плагинов и т.п.:
    1. Берем нашу модельку, делаем UV-развертку.
    2. Заливаем текстуру UV-развертки специальным градиентом, где R-компонента будет соответствовать координате «x», а G — координате «y».
    3. Ставим B-компоненту по всей текстуре в максимум.
    4. Рендерим получившуюся вещь без учета света, отражений и т.п., на черном фоне.
    В результате получим искомый результат. X/y будут в R/G, а альфа автоматом попадет в «B», т.к. где наш объект будет прозрачным — там будет просвечивать цвет фона (черный), в остальных же местах — «B» будет браться из текстуры (255).

    PS: Про экспорт альфа-канала в синий цвет помог додумать хабраюзер xanep.
    Share post

    Comments 63

      +2
      Мне статья напомнила об этих методах: «Perfect spatial hashing» (2D/3D) и «Geometry images» (3D)
        +3
        Думаю, что специального софта нет. Но и сделать подобные карты не сложно, если у вас есть 3D моделька человечка в максе/майе. Сначала текстурируете человечка нормальной текстурой, делаете анимацию, заменяете текстуру на специальную с координатами вместо цвета, ставите человечка в нужную позу и рендерите раскадровку.
          0
          Да, я уже сам догадался и дописал «P.S.» :)
            +4
            Альфа канал в B-компоненте вы автоматом получите, если текстура развертки в синем канале будет залита 255, а рендерить вы будете на черном фоне.
              0
              Точно! Спасибо :)
          +2
          неожиданно! интересно это кто-то сам родил или этим уже давно пользуются?
            –1
            В 3D-играх такое на каждом шагу, во флэше вижу впервые
              –4
              в 3D то я в курсе.
                +2
                В 3D играх такое не используется.
                  –2
                  ну как не используется? Обычные текстурные координаты (UV). Только на 3д-моделях они обычно повертексно распределены, а здесь закодированы пиксельно.
                  Но особой разницы же нет, обычный float2 вектор, просто отображенный значениями RG в битмапе.
                    +1
                    ОМФГ, да любые бинарные данные это флоат2 вектор, и что с того?
                    Этак технология не имеет смысла в контексте трехмерной графики.
                      0
                      не нервничайте.

                      еще как имеет, если использовать per pixel mapping (в каких либо специфичных ситуациях)
                      в анриловском движке, например, дисторшен текстурных координат именно так и делается — подаются uv-анимированные битмапы (RG) как координаты смещения пикселов текстуры.
                      еще в matte-paint'овых задниках можно использовать, которые с хайреза в лоурез пекутся.

                      Вообще можно везде использовать, где нужна низкая детальность геометрии и высокая точность позиционирования текселя на поверхности (чтобы диагонали не ломали равномерное распределение текстуры)
                        –1
                        Говорить как вы — это все равно, что говорить, что перемножение матриц и поиск подстроки — одно и то-же: берем бинарные данные, производим над ними операцию, кладем результат.
                        На каком-то уровне абстракции, конечно все так, но в реальности мы имеем очевидную ситуацию: есть разные технологии, которые достигают разных целей.
                          0
                          я вам про Фому вы мне про Ерёму…
                          Вы вообще в разработке графики для игр участвовали когда-нибудь?
              0
              Спасибо, интересный метод.
              Возможно, пригодится) Я задумывался о реализации похожего эффекта.
                0
                А в чем выигрыш, в занимаемом месте? Можно в цифрах, сколько весили исходные картинки и сколько весит результат?
                  +1
                  Я так понимаю, что выигрыш в кастомизации — можно наплодить много разных человечков :)
                    +1
                    Не столько в месте, сколько в удобстве скиннинга. Для добавления нового вида человечков достаточно просто нарисовать скин.

                    В противном случае пришлось бы все перерендеривать.
                      0
                      А например то, что нет антиалиасинга — это недостаток технологии, или конкретной реализации? Мне почему-то кажется, что сделать сглаживание при таком подходе сложно. Не даром у человечка во время движения появляется до пяти глаз.
                        0
                        Сделать сглаживание — вполне реально. Правда, возможно придется в скин паддинги вставить. Если заранее нагенерить — то и тормозить не будет.

                        Но у подхода есть минус — геометрия статична, если захочется добавить, скажем, шляпу — фигач новую анимацию. Но можно попробовать пофиксить это через микс нескольких анимаций для разных геометрий и наборов скинов для каждой. При таких размерах изображения должно хватить штук эдак 5 для практически любого логичного внешнего вида персонажа.
                        0
                        Даже если разобрать логически сам способ получения карты анимации:
                        Мы рендирим объект с антиалиазингом. Получаем пиксель, который на 50% составляет ногу, на 50% ботинок. Т.к. цвет — это координаты смещения в текстуре, то при смешивании двух таких цветов мы получаем не координату на текстуре, где лежит средний между этими объектами цвет, а просто координаты пикселя, который лежит где-то по середине.
                          0
                          Видимо, нужно как-то рендерить без антиальясинга.

                          Сам я в 3Д не знаток. Пусть кто-нить подскажет. Думаю, это возможно.
                            0
                            Рендирить то без антиалиасинга возможно, но это и результат будет без антиалиасинга, вот в чем проблема.
                              0
                              Так в примере и есть — без антиальясинга.

                              Антиальясинг можно сделать уже потом. Допустим, когда берем точку из текстуры с координатами (100,100) — учитывать еще соседние точки в текстуре.
                            0
                            Рендерить просто нужно в большем разрешении (тут правда есть ограничение в 256 точек). А выводить на экран уменьшенное изображение — вот и будет обычный антиалиасинг.
                              0
                              Да нет тут ограничения в 256 точек ;)

                              Ограничение в 256 точек — оно для текстуры, а не для рендеримого изображения!
                                0
                                Спрайтмэп надо сделать во вдвое большем разрешении, а после ее рендеринга вдвое уменьшить вместе с альфа-каналом.
                          0
                          Выигрыш в том, что если у вас 100 текстур человечков, то вам не нужно делать 100 анимаций-раскадровок, а только одну.
                          0
                          А я всегда думал, как такие текстуры натягиваются на анимацию. Все оказалось достаточно просто :)
                            0
                            Спасибо — очень полезная технология — особенно для онлайн-проектов. Хотелось бы посмотреть на качество маппинга на краях в более масштабном случае.
                              +1
                              А разве наличие альфа-канала как раз не то что нужно?
                                0
                                Да вроде то, но пример больно мелкий.
                              +1
                              Отличная инфа, спасибо )
                                0
                                Классное решение. Правда, для спрайтов больше 255 пикселей в любом направлении не подойдет, но такие пока, к счастью, встречаются редко :)
                                  +2
                                  Use png with 48 bit, Luke :)
                                    0
                                    Зависит от конкретной задачи. Зачастую можно выкрутиться и со спрайтами больше 255 пикселей.
                                    — Можно использовать текстуру RGBA, где будет по 2 канала на каждую координату х/у. Ну а если под альфу используется 1 бит, то её можно кодировать в любом канале. К примеру, 255 в красном канале будет обозначать полную прозрачность, любые другие значения — полную непрозрачность.
                                    — Можно использовать текстуру 16 бит/канал. Правда такую не просмотрите в графических редакторах, что усложняет генерацию и отладку. Я в свое время намучился ))
                                      +1
                                      Не спрайтов, больше 255 пикселей, а текстур с размерами более 256x256.
                                        0
                                        Точно! Спасибо за фикс.
                                        0
                                        Можно понихить градации альфы до 64, получим возможность использовать текстуры до 512*512. Но вообще, с учетом общих проблем метода, такие большие текстуры ему все равно не нужны, 256*256 за глаза хватит.
                                        0
                                        Хех, знакомая картинка ) Ситивиль?
                                        Спасибо за анализ, когда сам копался в исходниках обратил внимание на необычный способ, но копать не стал.
                                          0
                                          во флэше есть встроенный механизм DisplacementMapFilter, но он не подошел
                                          Но AS реализация наверняка будет напрягать процессор, там должен использоваться встроенный механизм с оптимизированным кодом. Если вы еще и это раскопаете, полезность топика удвоится.
                                            +2
                                            А зачем сильно быстро? Все текстурки можно наложить один раз при старте, и получить привычные спрайты.
                                              +1
                                              Например для быстрого старта :) Для игр это важно. Но я не в теме флеш-игр, возможно вы правы.

                                              Кстати, по пути нагуглил пример использования DisplacementMapFilter по прямому назначению. Выглядит неплохо, но и процессор напрягает сильно (может потому, что на Убунте).
                                                0
                                                На то он и фильтр.
                                                Для быстрого старта можно делать render-on-demand.
                                                Да, такие штуки всегда требуют ухищрений и от архитекторы.
                                                Зато экономия и в объёме передачи данных и в занимаемой памяти.
                                            0
                                            Я такое делал еще в 2000-м году на C++. «Времена меняются — Хёршис остается».
                                              0
                                              Не могли бы сделать картинки в чуть побольше варианте? Спасибо
                                                0
                                                Не заработают – сам принцип по-другому строен.
                                                  0
                                                  Не могу. Это ж не мои картинки и отрендерены они были именно в таком размере :)
                                                  0
                                                  Спасибо, идея интересная, хотя и применима только в очень узком кругу задач. Да, кстати, называть это displacement map-ом не совсем верно, т.к. этот термин применяется для совсем другой технологии. Ну и на данной текстуре, собственно, нет никаких смещений — только абсолютные координаты.
                                                    0
                                                    Да, согласен. Подкорректировал статью. Это, скорее, — некий «placement map» :)
                                                    –3
                                                    Я себе так скин в Quake 2 рисовал. Именно такую развертку и приходилось делать.
                                                    В флеше вижу впервые.
                                                      +4
                                                      Это здесь вообще не при чем. В Quake 2 — там чистое 3D и на 3д модельку натягивается текстура.

                                                      Тут же речь идет вообще о другой технологии, без какого-либо 3д.
                                                        –3
                                                        И здесь на модельку натягивается текстура =)
                                                        Только моделька не 3D, а 2D.
                                                        Хотя с таким же успехом можно назвать это палитрой… но выглядит оно действительно похоже на развёртку текстуры Q1/Q2.
                                                          0
                                                          врядли «без какого либо 3Д», ибо рендеринг спрайтов с анимацией делается именно в 3д с последующей упаковкой UV+Alpha в RGB атласа спрайта
                                                            –1
                                                            С момента появления кинематографа фильмы были трехмерными.
                                                              0
                                                              это вы к чему?
                                                                0
                                                                Ну ведь оригинал фильма — поезда всякие — трехмерные, значит и сам фильм трехмерный, так?

                                                                (Хинт, если конечные данные+алгоритм не содержат прямых данных о трехмерных свойствах, и не используют трехмерных преобразований над этими данными — такая технология будет «без какого-либо 3д», а то, как мы генерируем исходные данные — вопрос третий.)
                                                                  0
                                                                  с вами крайне сложно вести беседу, простите.
                                                      • UFO just landed and posted this here
                                                          0
                                                          Есть подозрение, что потом сверху лепится пререндеренный слой с освещением.
                                                          • UFO just landed and posted this here
                                                              0
                                                              Единственное, что могу предположить:
                                                              При правильном методе текстурирования расстояние между точками, на которые сссылаются соседние точки анимации, связано с нормалью(правда, криво-косо).
                                                                0
                                                                Ан нет, все гораздо проще, похоже.
                                                                На текстуре есть градиент. На анимации в плоскости G — тоже очевидный градиент по ссылкам. Штука в том, что текстура — не классическая текстура, она так-же содержит версии для разной освещенности. А анимация напрямую ссылается на них. Но, учитывая то, что например лицо на текстуре явно только в одной версии, освещение таким методом сделано не для всего конечного спрайта, а только для некоторых областей.

                                                          Only users with full accounts can post comments. Log in, please.