Plasma - kámoška pro všechno... (ENIF)

Pokud se zabýváte věcmi, jako je například voxel, nebo jste chroničtí tuneláři (nebo masochističtí klaustrofobici se zálibou v létání v těsných a tmavých tunelech), tak jste už někdy potřebovali texturu, která na sebe navazuje ve všech směrech. Není problém strávit nějakou tu (delší) chvilku u vašeho oblíbeného grafického programu a nakreslit si něco dle vašeho gusta, ale co jemné přechody, co kopečky a údolíčka a vůbec - co ten strávený čas při malování?
Jedním ze způsobů, jak si ušetřit čas (a nervy), je použití plazmy. Je to algoritmus, který vytvoří z několika málo vstupních parametrů prakticky nekonečné množství textur. Jen prakticky nekonečné? To proto, že některé textury budou prostě vypadat hnusně, ale na to přijdete jistě sami :-).
Nebudu se příliž podrobně (vlastně skoro vůbec :-|) rozepisovat, jak vlastně taková plazma funguje, prostě si projděte vlastní generátor a zkuste měnit konstanty v něm obsažené... Princip je totiž úplně prostý, nový bod se vytváří iteračně podle svých sousedů a upravije se o náhodně vygenerovanou konstantu.
Generátor, který tady popisuji vypočítává texturu 256x256 bodů velkou. Upravit ho na jiný rozměr není problém. A když ano, tak ne moc velký, alespoň doufám :-). Pokud si budete chtít prohlédnout výslednou texturu, použijte paletu v odstínech šedi.

1. potřebujeme buffer, do kterého se bude počítat:
unsigned char Bit[256*256];                  // Texture


Než se začne generovat textura, je samozřejme nutné tento buffer nejprve vynulovat:
for (i = 0; i < 256*256; i++) Bit[i] = 0;


a nastavit výchozí hodnotu pro generátor:
Bit[0] = 128;  // lze samozOejmA mAnit


2. při generování plasmy je důležité správně spočítat novou barvu
bodu, když je to potřeba. To zajistí následující funkce. Zkuste si
pohrát s konstantou 2...
int ncol(int mc, int n, int dvd)
{
  int loc;

  loc = (mc + n - (rand() % (2 * n)) ) / dvd;
  if (loc > 250) return 250;                   // pozor NE 255!
  if (loc < 5) return 5;                       // a tady NE 0!
  return loc;
}


3. a vlastní generující funkce... Upozorňuji, že přetypování na
unsigned short int (čili UWORD) má své opodstatnění - 256*256 dává
přesně maximální hodnotu, kterou lze v UWORDu zobrazit, a zajišťuje
se tak automatické "wrapnutí" (přechod) z konce textury na její
začátek. Při menším/větším rozměru je třeba dělat test! (A nebo po
spočítání "přezoomovat" do vámi požadovaného rozlišení.)
Jako vstup chce tato procedura souřadnice horního levého a pravého
dolního rohu v bufferu, které určí rozměry textury. Pokud chcete
získat na sebe vzájemně navazující texturu, musíte zadat x1 i y1
rovné nule a x2 i y2 jako velikost_textury, čili:
plasma(0,0, 256,256);


Samozřejmě, že i tato procedura obsahuje konstanty, které se dají
(v rozumných mezích :-)) měnit.
void plasma(int x1, int y1, int x2, int y2)
{
 int xn, yn, dxy, p1, p2, p3, p4;

 if ((x2 - x1 < 2) && (y2 - y1 < 2)) return;

 p1 = Bit[(unsigned short int) (256 * y1 + x1)]; 
 p2 = Bit[(unsigned short int) (256 * y2 + x1)]; 
 p3 = Bit[(unsigned short int) (256 * y1 + x2)];
 p4 = Bit[(unsigned short int) (256 * y2 + x2)]; 
 xn = (x2 + x1) >> 1; 
 yn = (y2 + y1) >> 1;
 dxy = 5 * (x2 - x1 + y2 - y1) / 3;
 
 if (Bit[(unsigned short int) (256 * y1 + xn)] == 0)
 Bit[(unsigned short int) (256 * y1 + xn)] = ncol(p1 + p3, dxy, 2);
 if (Bit[(unsigned short int) (256 * yn + x1)] == 0)
 Bit[(unsigned short int) (256 * yn + x1)] = ncol(p1 + p2, dxy, 2);
 if (Bit[(unsigned short int) (256 * yn + x2)] == 0)
 Bit[(unsigned short int) (256 * yn + x2)] = ncol(p3 + p4, dxy, 2);
 if (Bit[(unsigned short int) (256 * y2 + xn)] == 0)
 Bit[(unsigned short int) (256 * y2 + xn)] = ncol(p2 + p4, dxy, 2);

 Bit[(unsigned short int) (256 * yn + xn)] =
 ncol(p1 + p2 + p3 + p4, dxy, 4);

 plasma(x1, y1, xn, yn); 
 plasma(xn, y1, x2, yn);
 plasma(x1, yn, xn, y2); 
 plasma(xn, yn, x2, y2);
}


Ještě poznámka na závěr. Pokud chcete, aby vygenerovaná textura byla pokaždé jiná, tak nezapomeňte měnit randseed - například takto:
 srand((unsigned int) time(NULL));