2011-10-31:

32 kolory

perihelion:gfx:code for fun
Przez przypadek wpadła mi w ręce ostatnio recenzja gry Perihelion - jest to wydana w roku 1993 gra RPG na Amigę. Z uwagi na ówczesne ograniczenia, autorzy gry dysponowali jedynie paletą 32 kolorów i muszę przyznać, że sposób w jakie wykorzystali te 32 kolory mi zaimponował. Konkretniej: złożyli paletę z dwóch "gradientów": szarego i pomarańczowego, co dało kapitalny efekt (screeny poniżej). Anyway, efekt spodobał mi się na tyle, że stworzyłem krótki programik, który konwertuje dany obrazek własnie do takiej palety 32 kolorów.

Perihelion


Link do postu autora gry o grze + sama gra do ściągnięcia: click

A teraz trzy wybrane screeny (źrodło: link powyżej):

perihelion inventory

perihelion world map

perihelion battle

32colors.cpp


Jak pisałem we wstępie do postu, tak bardzo spodobał mi się osiągnięty przez autorów efekt, że postanowiłem "naklepać" konwerter, który miał konwertować dowolny obrazek właśnie do dwu-gradientowej palety łącznie 32 kolorów. Konwerter działa następująco:

- dla każdego pixela
-- wylicz odległość koloru od pomarańczowego
-- jeśli odległość jest mniejsza lub równa A, to użyj palety pomarańczowej
-- w przeciwnym wypadku użyj palety szarej
-- wylicz jasność pixela
-- dodaj do niej wartość wynikającą z ditheringu
-- zmniejsz "głębie" jasności do 4 bitów (16 odcieni)
-- pomnóż przez wybraną paletę kolorów
-- i wstaw jako nowy pixel

Jeśli chodzi o dithering, to użyłem ditheringu Bayera (sentyment do Windowsa z czasów sprzed >=24bpp) z macierzą 4x4. Unavowed zasugerował użycie ditheringu Stuckie (Stuckiego?), natomiast z powodów różnych zostałem przy Bayerze.

Co do odległości, to to jest ciekawy problem - jak wyliczyć jakąś wartość, która powie coś o tym jak bardzo dany kolor jest podobny do innego.
Moja wstępna koncepcja zakładała znormalizowanie obu kolorów (gdzie przez "znormalizowanie" mam na myśli potraktowanie RGB jako wektora 3D i podzielenie x,y,z przez długość wektora), a następnie znowu potraktowanie obu kolorów jako punkty 3D i policzenie odległości między nimi.
Natomiast Arashi podsunęła mi pomysł, żeby z RGB wyciągnąć barwę (hue), która btw jest wyrażana w stopniach, i policzyć odległość kątową kolorów (liczenie odległości kątowej na kole jest ciekawe ;>).
Ostatecznie zaimplementowałem oba rozwiązania.

Jeśli chodzi o A, to jest to parametr wyrażony w jednostkach odległości między RGB (0.0 do 1.0) dla pierwszego przypadku, oraz w stopniach dla drugiego (0 do 359).

Anyway, kod + binarka windowsowa jest do ściągnięcia tutaj: 32colors.zip (392kb) (src+win32 bin).
Oczywiście należy pamiętać, że to kod robiony for fun, więc ma trochę błędów (np. zakładam, że szerokość obrazka jest podzielna przez 4, oraz że input będzie 24 bpp), i zresztą całość jest robiona "na oko" ;>

OK, a teraz kilka screenów (skonwertowane można "zoomować"):







I dwa bonusy (skonwertowana fotka by Arashi i .S.K.Y. z inną paletą kolorów):





I tyle...

Comments:

2011-11-01 09:04:32 = Ange4771
{
Interesting !

nice graphics indeed ( more http://hol.abime.net/1029/screenshot ), very "bitmap brothers"-like ;)
the dual palette trick is nice and the automatic conversion proves it's very useful.
}
2011-11-01 13:42:22 = Dab
{
Works great for Killzone! :D
http://www.gamesector.net/wp-content/uploads/2011/02/killzone-2-02.jpg
http://dabroz.scythe.pl/upload/2011/11/killzone-2-02.png_040.png
}
2011-11-02 10:18:06 = D
{
Ja zaś czekam zniecierpliwieniem na post: jak czas w pracy, po pracy, jak efektownie pracować (uczyć się). :)
}
2011-11-02 14:34:54 = Xevaquor
{
Świetne to wygląda chyba sam coś takiego pokleję :)
}
2011-11-02 17:48:24 = Albi
{
O ile grafiki z samej gry wyglądają średnio, o tyle efekt na normalnych obrazach powala.
}
2011-11-02 18:04:15 = Drraven
{
Ciekawe jak wydajność jeśli zostało by to użyte na jakieś dość dynamicznej grze np. 2D.
Pixel po pixelu co jedną klatkę?
}
2011-11-02 18:13:49 = Gynvael Coldwind
{
@Ange4771
Agreed! "Gods" was one of the first games that came into my mind after seeing the screenshots :)

@Dab
Hmm, there is no difference between the original an converted image. You sure they are using more than 32 colors? </joke>

@D
Hehe szczerze, to sam chetnie bym sie dowiedzial jak efektywnie pracowac/uczyc sie/etc :)

@Xevaquer
Plz share ur results :)

@Albi
;>

@Drraven
Jesli by uzyc strikte software'owego approach, to jest to kwestia przejscia po kazdym pixelu i przeliczenia koloru (szybkie).
Na shaderach bylo by jeszcze szybsze :)
}
2011-11-02 18:57:49 = gemGreg
{
Jak by to powiedzieć. Obrazy przechodzące przez twój filtr są lepsze od oryginałów :)
}
2011-11-02 22:22:26 = Blastboy
{
Z tego co się orientuję, to Amiga 1000 wyświetlała jednocześnie 32 kolory, ale do dyspozycji miała paletę 4096 kolorów. To tak jak teraz jest z GIFami - wyświetla na raz 256 kolorów, ale przestrzeń barw jest 24 bitowa. Ciekawa inspiracja do kodowania.
}
2011-11-02 22:56:23 = Gynvael Coldwind
{
@gemGreg
Uh, twój komentarz brzmi trochę jak ten kawał:

Mąż do żony:
- Kochanie powiedz mi coś takiego co mnie jednocześnie ucieszy i zasmuci.
Żona:
- masz dłuższego niż twój brat.

;D

@Blastboy
Ba! I rzucę Ci jeszcze dwie ciekawostki, jedna ad GIF, a druga ad Atari :)

1. Z uwagi na możliwość definiowania lokalnej palety w GIFach per logical image (obrazek GIFa składa się z jednego lub więcej logical image'ów; w animowanych GIF'ach są to frame'y, ale nie każdy GIF musi być animowany), można zrobić GIF który po złożeniu będzie miał więcej niż 256 kolorów (wystarczy złożyć finalny obrazek np. z "logicznych" kwadratów 16x16, gdzie każdy taki kwadrat ma własną paletę).
(W sumie chyba wspominałem o tym w jakimś arcie który kiedyś publikowałem (Hakin9 5/2k8: Format GIF okiem hakera) i jest też o tym na wiki http://en.wikipedia.org/wiki/Graphics_Interchange_Format#True_color)

2. Atari 800 XL umie na raz wyświetlać tylko 16 kolorów z palety 256 kolorów. Ale, jeśli zsynchronizować zmianę palety kolorów ze scan line'ami, (czyli zmieniać paletę kolorów po narysowaniu danej linii na monitorze przez kartę graf), to można uzyskać 256 kolorów na ekranie (ale tylko 16 kolorów na linię).
(O tym dowiedziałem się od Tomka Cieślewicza na CONFidence 2010 - http://gynvael.coldwind.pl/?id=314).

http://gynvael.vexillium.org/dump/atari_256_colors.png - screen z emulatora (programik nie był mojego autorstwa, skądś go przepisałem, ale niestety nie pamiętam skąd ;<)




}
2011-11-03 13:04:16 = zak
{
@Gyn
O metodzie na wyświetlanie 256 kolorów na Atari dowiedziałem się na zajęciach ze sprzętu peryferyjnego pctów. W użyciu była także myszka optyczna jako skaner (prawie 10 lat temu). To tak odnośnie artykułu o studiowaniu (wszystko zależy od tego na jakich ludzi się trafi w życiu :)
}
2011-11-08 19:10:23 = VGT
{
W "kolorowej" tematyce miałem kiedyś okazję przezczytać ten artykuł na temat silnika Quake2: http://fabiensanglard.net/quake2/quake2_software_renderer.php

Wrażenie na mnie zrobiło, jakie triki są tam zastosowane, aby za pomocą 8 bit'owej palety mieszać tekstury wraz z radiosity. Zacytuje artykuł: "it is awesome to manage to fake 64 gradients of 256 colors....will only 256 colors". Pewnie źródła kolejnych wersji ID Tech, ktore można sobie poprzeglądać, to kopalnia wiedzy i tego typu trików.
}
2011-11-18 11:49:51 = QrA
{
Amiga to również miłość moich młodych lat. Apropo palet i wyświetlanych kolorów przypomniałem sobie tryb wyświetlania na amidze z ukladami ECS o nazwie HAM (HAM6 bodajże). Nie wiem jak to dawało radę ale można było wyświetlić 4096 kolorów na raz (ale wymagany był tryb interlaced - stąd przypuszczam że zasada działania była zbliżona do tego opisanego przez Ciebie na Atari 800 XL). Jednak prędkość tego trybu na 68000 pozostawiała wiele do życzenia. Bardzo ciekawy artykuł!

PS. Dzięki za Syndicate Wars Port :D
PS2. Można liczyć na port Moonstone? :>
}
2011-11-18 22:58:14 = Gynvael Coldwind
{
@zak
:)

@VGT
Thx za linka, poczytam :)

@QrA
HAM6 to jest jeden z moich ulubionych tricków :)
Działało to następująco:
Miałeś 6 bitów per pixel, z czego:
* 2 to był bit trybu
* pozostałe 4 (a więc wartości 0-15) to była wartość
Do tego miałeś do dyspozycji 16 kolorową paletę kolorów.
I do tego był hmm, niejawny rejestr obecnego R,G,B.

I teraz tak, bity trybu mogły ustawić jeden z czterech trybów dla danego pixela:
- Wybierz kolor z palety: wtedy bity Wartości były traktowane jako numer koloru w palecie. Niejawny rejestr RGB był ustawiany na RGB pobrane z palety.
- Zmień Red: bity Wartości były traktowane jako kanał R, a więc R z niejawnego rejestru RGB było zmieniane na Wartość.
- Zmień Green bity Wartości były traktowane jako kanał G, a więc G z niejawnego rejestru RGB było zmieniane na Wartość.
- Zmień Blue: bity Wartości były traktowane jako kanał B, a więc B z niejawnego rejestru RGB było zmieniane na Wartość.
Dany pixel miał kolor który był w niejawnym rejestrze RGB po wykonaniu jednej z powyższych operacji.

Czyli de facto miałeś 4 bity na R, 4 na G i 4 na B, a więc 12 bitów per kolor (czyli 4096 kolorów). Problem jest tylko taki, że nie możesz zmieniać więcej niż jednej barwy per pixel (chyba, że chcesz pobrać kolor z palety) :)
Więc... owszem, można było wyświetlić obraz który miał 4096 kolory, ale to nie znaczy, że dowolny pixel mógł mieć dowolny kolor. Kolory kolejnych pixeli zależały od kolorów poprzednich pixeli (tj kolor danego pixel zależał od dwóch z trzech kanałów poprzedniego pixela), albo były kolorami wybranymi z palety kolorów.
To niestety bardzo ogranicza zastosowania - głównie do jakiś splash screenów, etc. (co nie zmienia faktu, że jest świetne ;>)

Bodajże chipset AGA miał tryb HAM8 w którym było 6 bitów na wartość (0-63), co dawało 2**18 kolorów.

Ad PS2. Niestety nie hehe, chwilowo mam portowania dość ;)
Cheers!
}

Add a comment:

Nick:
URL (optional):
Math captcha: 10 ∗ 9 + 8 =