Применение класса hq3x или вращаем pixel art

Давным-давно, я поднимал тему про вращение растрового изображения без порождения эффекта лестницы.
Наконец-то я достиг максимальных результатов в этом деле, т.к. постоянно работаю с pixel art'ом и мне хотелось избавиться от этого ужасного проклятия — «лесенки».

Объяснить мне это тяжело, поэтому буду все показывать в картинках:


Эффект лесенки

Некоторые скажут: "Включи сглаживание и не парься!!1". Но увы, сглаживание портит картинку не хуже, чем лесенка. А выбирать из двух зол меньшее — не хочу. Я выбираю третий, тот самый вариант, который я сейчас покажу.


Нет лесенки, но ужасное сглаживание. Четкость пропала :(

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


Pixel art изображение в x1 и x3 масштабах

Почему x3? Потому что весь секрет кроется в этом замечательном масштабе, если мы целенаправленно увеличим изображение в три раза, а потом уменьшим до начального состояния, то «флешевское сглаживание» не будет распространяться на мелкие детали (т.к. 1 пиксель уже равен 9-и пикселям). Но на этом останавливаться еще рано:


Четкая картинка, но с лесенкой :(

Тогда, я подумал о том, чтобы сглаживать х3 картинку вручную, вот таким образом:


И попробовать снова старый способ:


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

Вот тут уже понятней, но внизу есть лишний пиксель. Связано этой с тем, что изображение обрабатывал вручную

Теперь вы скажете: «И что вот так каждую картинку перерисовывать?». Нет. Спасибо Claymore, что натолкнул на мысль реализовывать сию вещь программно. Решил попробывать, да оказывается я изобретал колесо. В старых эмуляторах используется похожий алгоритм hq2x:


Ностальгия :'( (Streets of Rage)

И я пошел серфить в «западных интернетах» с целью найти сорц алгоритма. К счастью, я нашел готовую реализацию алгоритма на AS3:

package {

        /**
         * @author nicoptere
         */
        import flash.display.BitmapData;
         
        public class ScaleX {
        
                static public function scale2x( bd:BitmapData ):BitmapData{
                        var dest:BitmapData = new BitmapData( bd.width*2, bd.height*2, true );
                        
                        var E:uint;
                        var E0:uint;
                        var E1:uint;
                        var E2:uint;
                        var E3:uint;
                        
                        var B:uint;
                        var D:uint;
                        var F:uint;
                        var H:uint;
                        
                        var i:int;
                        var j:int;
                        var i2:int;
                        var j2:int;
                        
                        var w:int = bd.width;
                        var h:int = bd.height;
                        bd.lock();
                        dest.lock();
                        for ( i = 0; i < w; i+=1 )
                        {
                                for ( j = 0; j < h; j+=1 )
                                {
                                        
                                        E = bd.getPixel32( i, j );
                                        
                                        //on est sur un bord
                                        if( i==0 || j==0 || i == w-1 || j == h-1 )
                                        {
                                                
                                                E0 = E;
                                                E1 = E;
                                                E2 = E;
                                                E3 = E;
                                                
                                        }else{
                                        
                                                B = bd.getPixel32( i, j-1 );
                                                D = bd.getPixel32( i-1, j );
                                                F = bd.getPixel32( i+1, j );
                                                H = bd.getPixel32( i, j+1 );
                                                
                                                if (B != H && D != F ) 
                                                {
                                                        
                                                        E0 = ( D == B ) ? D : E;
                                                        E1 = ( B == F ) ? F : E;
                                                        E2 = ( D == H ) ? D : E;
                                                        E3 = ( H == F ) ? F : E;
                                                        
                                                }else{
                                                        
                                                        E0 = E;
                                                        E1 = E;
                                                        E2 = E;
                                                        E3 = E;
                                                        
                                                }
                                        }
                                        
                                        i2 = i*2;
                                        j2 = j*2;
                                        dest.setPixel32(   i2,   j2, E0 );
                                        dest.setPixel32( i2+1,   j2, E1 );
                                        dest.setPixel32(   i2, j2+1, E2 );
                                        dest.setPixel32( i2+1, j2+1, E3 );
                                        
                                }
                        }
                        bd.lock();
                        dest.unlock();
                        return dest;
                }
                
                static public function scale3x( bd:BitmapData ):BitmapData {
                        var dest:BitmapData = new BitmapData( bd.width*3, bd.height*3, true );
                        
                        var E:uint;
                        
                        var E0:uint;
                        var E1:uint;
                        var E2:uint;
                        var E3:uint;
                        var E4:uint;
                        var E5:uint;
                        var E6:uint;
                        var E7:uint;
                        var E8:uint;
                        
                        var A:uint;
                        var B:uint;
                        var C:uint;
                        var D:uint;
                        var F:uint;
                        var G:uint;
                        var H:uint;
                        var I:uint;
                        
                        var i:int;
                        var j:int;
                        
                        var i3:int;
                        var j3:int;
                        
                        var w:int = bd.width;
                        var h:int = bd.height;
                        
                        bd.lock();
                        dest.lock();
                        for ( i = 0; i < w; i+=1 )
                        {
                                for ( j = 0; j < h; j+=1 )
                                {
                
                                        E = bd.getPixel32( i, j );
                                        
                                        //on est sur un bord
                                        if( i==0 || j==0 || i == w-1 || j == h-1 )
                                        {
                                                
                                                E0 = E;
                                                E1 = E;
                                                E2 = E;
                                                E3 = E;
                                                E4 = E;
                                                E5 = E;
                                                E6 = E;
                                                E7 = E;
                                                E8 = E;
                                                
                                        }else{

                                                A = bd.getPixel32( i-1, j-1 );
                                                B = bd.getPixel32(   i, j-1 );
                                                C = bd.getPixel32(   i, j+1 );
                                                D = bd.getPixel32( i-1, j );
                                                F = bd.getPixel32( i+1, j );
                                                G = bd.getPixel32( i-1, j+1 );
                                                H = bd.getPixel32(   i, j+1 );
                                                I = bd.getPixel32( i+1, j+1 );
                                                
                                                if ( B != H && D != F ) 
                                                {
                                                        
                                                        E0 = ( D == B ) ? D : E;
                                                        E1 = ( ( D == B && E != C ) || ( B == F && E != A ) ) ? B : E;
                                                        E2 = ( B == F )? F : E;
                                                        E3 = ( ( D == B && E != G ) || ( D == H && E != A ) ) ? D : E;
                                                        E4 = E;
                                                        E5 = ( ( B == F && E != I ) || ( H == F && E != C ) ) ? F : E;
                                                        E6 = ( D == H ) ? D : E;
                                                        E7 = ( ( D == H && E != I ) || ( H == F && E != G ) ) ? H : E;
                                                        E8 = ( H == F ) ? F : E;
                                                        
                                                } else {
                                                        
                                                        E0 = E;
                                                        E1 = E;
                                                        E2 = E;
                                                        E3 = E;
                                                        E4 = E;
                                                        E5 = E;
                                                        E6 = E;
                                                        E7 = E;
                                                        E8 = E;
                                                
                                                }
                                        }
                                        i3 = i*3;
                                        j3 = j*3;
                                        
                                        dest.setPixel32(   i3, j3, E0 );
                                        dest.setPixel32( i3+1, j3, E1 );
                                        dest.setPixel32( i3+2, j3, E2 );
                                        
                                        dest.setPixel32(   i3, j3+1, E3 );
                                        dest.setPixel32( i3+1, j3+1, E4 );
                                        dest.setPixel32( i3+2, j3+1, E5 );
                                        
                                        dest.setPixel32(   i3, j3+2, E6 );
                                        dest.setPixel32( i3+1, j3+2, E7 );
                                        dest.setPixel32( i3+2, j3+2, E8 );
                                        
                                }
                        }
                        bd.unlock();
                        dest.unlock();
                        return dest;
                }
        }
}


Теперь пробуем:

Изображение после обработки


Вуаля! Минимум стараний и максимум результат

Результат:


link: www.swfcabin.com/open/1346195013
as3 hq2x: www.nicoptere.net/AS3/scale/ScaleX.as
  • +24

Комментарии (70)

0
Полезно! Спасибо
Заюзаю в следующей своей пиксельной игрушке
0
не особо же изменилось?
+11
На последней флешке с вращающимися зайцами (это же зайцы, да?) больше всего понравился первый вариант.
+2
Аналогично. После всех изысканий и стараний, проделанных в статье, простой smoothing смотрится совсем не плохо на фоне остальных.
+1
+1, smoothing-вариант, имхо, смотрится выигрышнее.
+5
Мне кажется, что «от smoothing картинка замыливается, использую только хитрый алгоритм» — это уже примерно как «в цифровой технике нет души, слушаю только тёплый ламповый звук».
  • iLORd
  • iLORd
+2
Я думаю дело в «подражании» ретро технологиям. Замыленная картинка на 8-16ти битных приставках это нонсенс. Поэтому и игры которые стилистически стремятся быть похожими, с замыливанием не дадут проникнутся ретро атмосферой.

Я думаю тут важны таки мелочи. Иначе какой смысл от пикселарта:)
+1
мне кажется на фоне не замыленных картинок где виден каждый пиксел замыленный герой будет выделяться, тогда уж все надо мылить
0
Эм? Ну автор статьи какраз таки и победил замыливание персонажа.
0
Если «только пиксельарт, только хардкор», то и крутить спрайты неправильно, надо руками рисовать анимацию поворота)
0
Насколкьо я помню, сега не позволяла крутить спрайты, а на супер нинтдендо это используется вовсю. и никакого замыливания. Обрати внимание.
+5
Да я, если честно, вообще не понимаю увлечения пиксель-артом. Для некоторых проектов в ретро-стиле это ок, но сейчас же его лепят в каждой третьей игре…
0
не могу не согласится)
+1
+1
Ащще не люблю пиксельарт.
+3
Людям нравится пиксельарт, они делают то, что им нравится.
Я люблю пиксельарт.
0
Людям нравится Дом 2, они смотрят Дом 2:D

Вырвалось)
0
К чему бы это?
0
Ну просто ещё есть понятие безвкусица) Это я так:)
0
По твоему пиксельарт — это безвкусица?
0
Ты невнимателен. Пиксел-арт должен быть в тему. А лепить его куда попало (что сейчас и происходит в большинстве случаев) — это безвкусица.
0
Чушь какая-то.
Если все красиво делать, то все будет в тему. Хотя если тебе не нравится пиксельарт, то тут сложно угодить.
0
Не вижу логики в точке зрения «если всё красиво сделать». Но у всех свои вкусы. Я уважаю пиксел арт. Но в больнстве случаев какой бы он красивый нибыл — он реально не в тему.
+2
«Реально не в тему» — это очень расплывчатое объяснение.
Я наоборот, уважаю пиксельарт. И в большинстве случаев, он реально не в тему.
0
*реально в тему
fix
0
Холивар?) Начнём с истории — пиксел арт в играх появился не потому что «красиво» или он «подходит» а из за аппаратных ограничений. Небыло бы ограничений — разве были бы пиксельные игры? Да нет конечно! Были бы единицы, типа АРТХАУС. А что происходит сейчас — это подражание ретро играм и всего лишь.

Нет причин использовать пиксел арт в физз пазлах — смысл? Небыло ТАКИХ (построенных на реальной физике) физ паззлов раньше (да и вообще были ли в полном смысле слова физ паззл?).

Ещё такой ньанс — в подавляющем большинстве случаев пиксельные игры — удел инди. Почему? В некоторых случаях это реально упрощает разработку, стилизуя игру под ущербную кастрированную графику, объясняя закосом под ретро (сейчас 2012, зачем мне разглядывать мелкие пиксельные объекты, или малодетализированное окружение????). Крупные разработчики врядле возьмутся сейчас делать такую графику. Это извращение над игроком.

Можешь объяснить почему «он реально в тему» и чем это обусловлено кроме фапа на мастерство художника? Ну серьёзно. Какие плюсы для игрока? Возможность играть на мониторах которые отображаю 256 цветов?
+5
стилизуя игру под ущербную кастрированную графику
Ну вот все и выяснилось. Тебе просто не нравится пиксельарт, что я тебе могу объяснить?
Попробуй расскажи мне, чем тебе нравится дом 2, и у тебя ничего не получится.

Зачем делать кастрированные и ущербные 2D игры, когда давно изобрели 3D?
Не нравится пиксельарт, не играй и не смотри игры с пиксельартом. Случайно увидел — закрой, пойди поиграй в круизис с калл оф дюти.
0
Опа, ну да, легче обвинить чем объяснить плюсы пиксел арта :D Так держать фапер
0
Неадекват какойто. мы же не игры обсуждаем а технологию графического оформления? Зачем к кучу всё мешаешь? Есть и 3д игры с закосом под пикслеьную графику (привет майнкрафту и кстате по сравнению с ним, нитромы реально не популярны). Из 10 моих знакомых — обычных геймеров: 9 знаю майнкрафт и ниодин — нитромов.

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

А то выходит что реально тёплый ламповый звук лучше?
0
«ущербная и кастрированная» может быть любая графика. Я имел ввиду ущербный пиксел арт. а такого треша навалом. Ещё раз — я люблю пискельные игры. И всегда хотел попробовать себя в пиксел-арте. Но всё хорошо в меру и ко всему нужно прикладывать голову а не попу.
0
«фапер», «неадеват»
Что «аргументы» вроде «реально не в тему» кончились, остались только оскорбления?
У оппонента ожидаемо проявился баттхерт. Продолжай.
+2
Я подожду ответа. У меня тысяча аргументов против пиксел арта. У тебя за — ниодного.
+2
Хоть миллион, людям как нравился пиксельарт, так и нравится.
P.S. Плюс от меня, случайный.
0
Ну да. Другого я не ожидал. Кроме субьектива. Типа почему так? Ну просто потому что ТАК.
0
неадекват я в плане того что ты написал. это не переход на личность. ну а фапер — так ведь оно и есть. чего уж там: В ну или фанбой ок
+1
С тобой все в порядке?
Какое графическое оформление тебе нравится?
Хотя мне безразлично, все равно ты фапер, фанбой и неадекват, потому что мне такое оформление не нравится.
0
При чём тут нравится. Речь изначально шла о том, что пиксел арт далеко не ко всем играм подходит. И это уже не касается только субьективного «нравится или нет».

Могу с совсем другой стороны подойти — НЕ пиксельные игры НАМНОГО популярней пиксельных, и пользуются НАМНОГО большим спросом. И тогда уж, если ты инди ориентированный на заработок, должен это учесть И НЕ СТАВИТЬ ПИКСЕЛ АРТ ХОТЯБЫ В ФИЗ ПАЗЗЛЫ!!!:DDD
0
Ты много видишь физпаззлов с пиксельартом? Я нет.
Часто вижу платформеры, автораннеры, рпг, стратегии, с приятной пиксельной графикой. Можно в этих жанрах использовать писельарт? Или так делают только фаперы и неадекваты?

А то, что тебе не нравится пиксельарт, это очевидно, что бы ты не говорил.
0
Физзпаззлов мало но есть и они воообще не в тему ведь. а про нелюбовь к пикселарту поставил дигноз неглядя:) отлично) Ладно, давай заканчивать. Реально невижу объектива, какой тогда смысл.

Будующее за вектором как инструментом:) Пикселарт даже с иконографии в будующем уйдёт. Но это личное мнение.
+6
Крупные разработчики врядле возьмутся сейчас делать такую графику. Это извращение над игроком.

Нитром сделал имя на пиксельарте.

ЗЫ Сам я не любитель пиксельарта )
+1
Нитром это инди. И сравнительно не популярные кстате.
0
И сравнительно не популярные
0
тут должна была быть смешная гифка, которой я хотел парировать твое дерзкое высказывание, но не сложилось.
0
окау( Пиксельная? может у меня просто рарешение слишком высокое и она не отображается?
0
:D не люблю его, но всёже минусы пикселизации раскрыты. Плюсов я пока так и не узнал. Кроме удовлетворения «ценителей»
0
http://www.artlebedev.ru/kovodstvo/sections/71/
+1
Как насчет такого пиксельарта?
dl.dropbox.com/u/4952054/ludumdare20/postRelease/index.html
0
Вау! Прикольно…
Но думаю, если вдруг все начнут делать игры в этом стиле — станет не так прикольно…
0
Так уже. И довольно давно.
0
Хм, как-то наверное не все, потому что я не так часто вижу что-то подобное…
0
А я довольно часто. Сейчас этот стиль модный. На Ludum Dare много такого встречаю, как и сейчас, так и в прошлый раз. Даже посмотри Lone Survivor, в стиме. Тот же стиль.
0
Так на людуме это от одного автора наверное? )
blog.deepnight.net/p/games.html
0
В конце концов решает геймплей)
+4
Внесу свое слово по поводу дискуссии выше.

Пиксельарт это стиль, а не технология. Это тоже самое, что сказать модерн отстой, барокко крутизна.

У лебедева написано именно про технологию, а не про стиль. Пиксельарт может рисовать самыми разными способами, в том числе и вектором ( flash-animated.com/piksel-art-vo-fleshe ). Очевидно же, что последние 15 лет никакого практического смысла в использовании пиксельарта уже нет. О чем спорить?
А уж нравится вам или нет — это дело сугубо вашего вкуса (также как стили в живописи, архитектуре и т.п.).

А на жизнь и смерть того или иного стиля влияет мода и популярность. Не так давно делать игру в 2д было смертным грехом и все очень ругали графику арканума, фаллаута тактикс и подобных игр.
0
Всё верно. Пикселарт это стиль, но он имеет свою технологию построения изображения (читать — правила построения). О стилях спорить глупо. Но если мы построим огромный, сверхтехнологичный космический городок и оформим его в стиле борроко — это ведь будет безвкусно как минимум. И речь тут уже не о вкусах даже. Это ограничивает нас. Если будем делать всё по канонам, то нельзя будет использовать мониторы, компьютерную технику в помещениях, телефоны и т.п. Понятно что я утрирую, но всёже.

В таком случае, скоро нельзя будет упрекнуть игру в плохой графике, т.к. «у всех свои вкусы». И опять прошу не понимать это как то, что я считаю пикселарт плохой графикой.
+1
кончай холиварить в моей теме, Рус D:
0
:D
0
Кстате спасибо за ссылку! Теперь попробую себя в пикселарте:)
+2
В таком случае, скоро нельзя будет упрекнуть игру в плохой графике, т.к. «у всех свои вкусы». И опять прошу не понимать это как то, что я считаю пикселарт плохой графикой.
Ну это разные вещи. Плохая графика — результат работы плохого художника, от стиля это никак не зависит. И наоборот, бывает такой пиксельарт, что даже не сразу поймешь, что это пиксельарт ( www.pixeljoint.com/pixelart/66189.htm ).

Одно дело вы говорите, что в этой игре плохо нарисованная графика, и совсем другое, что пиксельарт плохой стиль.

Но если мы построим огромный, сверхтехнологичный космический городок и оформим его в стиле борроко — это ведь будет безвкусно как минимум.
Не совсем уместное сравнение. Во-первых, тут каждый строит свой маленький домик и очевидно что большинство людей построят: 1-в популярном и модном на данный момент стиле 2-по своим возможностям и бюджету.
Поэтому пиксельарт тут вполне уместное решение, пускай даже его и много.
Во-вторых, я бы не назвал вектор супер технологичным. С такой позиции надо защищать, как минимум 3д графику и т.п.
0
1) Опять же люди, вы меня не слышите. Я не оценивал пикселарт как стиль — плохой или хороший. Я какраз писал что плохой арт да ещё и пиксел арт (плохой пикселарт — это ооооочень страшно!!! Лучше плохой вектор или растр на мой взгляд:D) это ужасно.

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

Ну и по технологии — я лишь сказал что вектор применять практичней чем любой растр:) Но это спорно конечно.

Технологии идут вперёд и 3д — это то, к чему, по большому счёту мы все должны идти. Как разработчики. Покрайней мере для своего развития. что круче 2д или 3д — вот этот холивар был бы глупым)
0
PS — я не считаю что измерения имеют значение ^^

ладно, всё, я сдаюсь :D
0
и хорошо если это не дизеринг кстате
Нет, там все вручную. В пиксельарте он очень редко используется.

Лучше плохой вектор или растр на мой взгляд
А вот это уже дело вкуса :D Если уровень графики одинаковый.

пиксель арт в векторе это еще более извращение, чем пиксель арт в растре)
Да это, конечно, очень на любителя. Я больше как пример ссылку привел, чем из каки-то практических соображений
+1
пиксель арт в векторе это еще более извращение, чем пиксель арт в растре)
0
Тёплый ламовый звук Forever \m/
0
Жаль, что нельзя выбрать алгоритм сглаживания для изображений при использовании метода draw(). Проделал приём с тройным масштабом в Photoshop и получил лучший результат.
0
Если делать 4:1 пиксельарт, то и смотрится хорошо, и проблем меньше.
0
А теперь, представьте что пиксель-арт размером 16x16. Вы в этом мыле ничего не увидите.
  • z3lf
  • z3lf
0
а часто ли такой пиксель-арт используется в играх? иконографика — да, какие-то элементы интерфейса — возможно, но делать игровые объекты такого размера — издевательство над игроками.
0
Ну и насколько тормозит такой алгоритм? Вероятно предполагается кешировать повернутые спрайтики и рисовать из кеша? Если нет то интересно было бы глянуть на 100, 1000 таких зайцев
0
Естественно из кеша. Реалтайм даже предположить глупо.
+2
Пиксели рулят
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.