Kirjautuminen

Haku

Tehtävät

Oppaat: Grafiikka: Tuliefekti

Kirjoittaja: peki

Tuli on pehmennysfiltterin sovellusta käyttävä palettiefekti. Ideana on piirtää ensin piirtää pohjakuvio kirkkaalla värillä, sitten kuviota pehmennetään ja sitä siirretään ylöspäin. Sitä kylmennetään kylmennyskartasta otettavilla väriarvoilla. Tästä efektistä on olemassa erittäin mielenkiintoisia versioita. Suosittelen esimerkiksi kokeilemaan erilaisia valumissuuntia oppaan luettuasi.

Taulukot ja pehmennys

Aivan ensimmäiseksi tarvitsemme kaksi ruudun kokoista taulukkoa, jotka sisältävät edellisen ja nykyisen ruudun dataa. Taulukot sisältävät jokaisen pikselin kuumuuden.

int buffer1[SCREENWIDTH][SCREENHEIGHT];
int buffer2[SCREENWIDTH][SCREENHEIGHT];

Jokaisen ruudun alussa on piirrettävä lieskojen aloitusdata. Piirtäkäämme nyt viiva ruudun alaosaan.

for (int i = 0; i < 20; i++)
{
    buffer1[SCREENHEIGHT / 2 - 10 + i][SCREENHEIGHT - 10] = 255;
    buffer1[SCREENHEIGHT / 2 - 10 + i][SCREENHEIGHT - 11] = 255;
}

Seuraavaksi puhutaan itse algoritmista. Tässä efektissä on käytävä jokainen pikseli yksitellen läpi. Sitä ei voi mitenkään kiertää.

// käydään koko ruutu läpi
for (int x = 1; x < SCREENWIDTH-1; x++)
{
    for (int y = 1; y < SCREENHEIGHT-1; y++)
    {
        ...
    }
}

Kyseisen silmukan sisällä meidän on laskettava jokaista pikseliä ympäröivien pikselien keskiarvo ja sijoitettava se yhtä pikseliä ylemmäs. Tämän takia silmukka alkaa ykkösestä eikä nollasta. On aivan itsesi päätettävissä, otatko keskiarvon kaikista ympäröivistä pikseleistä vaiko vain neljästä. Efektin ulkonäköön se vaikuttaa vain hieman. Pikseli, jota ympäröivien pikselien keskiarvoa mitataan, voidaan myös sisällyttää halutessa keskiarvoon.

Tämä kuva selventää asiaa:

A = Vuorossa oleva pikseli.
B = Pikseli, johon keskiarvo sijoitetaan.
1, 2, 3, 4 = Pikselit, joista keskiarvo otetaan.

A:n arvoa ei kuitenkaan vielä tässä vaiheessa muuteta!

Koodina siis näin:

// ympäröivien pikselien keskiarvo
int c1 = buffer1[x+1][y+1];
int c2 = buffer1[x-1][y-1];
int c3 = buffer1[x-1][y+1];
int c4 = buffer1[x+1][y-1];

uusivari = (c1 + c2 + c3 + c4) / 4;

buffer2[x][y-1] = uusivari;

Mielessäsi saattaa olla nyt eräs kysymys. Jos suoritamme tämän monta kertaa ja piirrämme pohjakuvan aina uudestaan, ruutuhan muuttuu kokonaan valkoiseksi. Tämän takia puhumme nyt kylmennyskartasta.

Kylmennyskartta

Tuli ei ole mitään ilman kylmennyskarttaa. Kylmennyskartalla saat tulen väreilemään ja lieskat hulmuamaan. Kylmennyskartta on erittäin tummasävyinen kuva, jossa on vain tietyn värin sävyjä, tässä tapauksessa punaisen.

Jokaisen pikselin kohdalla kylmennyskartasta otetaan pikseli vastaavasta kohtaa ja vähennetään sen arvo pehmennetyn värin arvosta. Kylmennyskarttaa pyöritetään ylöspäin joka kerta, kun yksi ruutu on piirretty.

Koodina:

// väri kylmennyskartasta
// kylmakohta on eräänlainen offsetti karttaan. se kertoo, mistä kohtaa otetaan mikäkin pikseli.
// Jos y - kylmakohdasta tulisikin pienempi kuin nolla, eli mentäisiin kartan yläpuolelle, siirrymmekin kartan alareunaan ja jatkamme sieltä. Lopussa kasvatamme kylmakohdan arvoa sopivalla luvulla.

Uint32 c; // Tämä on etumerkitön 32 bittinen kokonaisluku, jota käytämme bittishiftien ja andauksen helpottamiseksi.
//Tärkeintä on kuitenkin vain saada talteen värin punainen osa ja voit siihen käyttää vapaasti omia keinojasi.
if (y - kylmakohta >= 0)
    c = getpixel(kylma, x, y - kylmakohta);
else
    c = getpixel(kylma, x, SCREENHEIGHT + y - kylmakohta);

// otetaan talteen kylmennyskartan punaväri (8 ylintä bittiä; väri on 24 bittinen). Tämä väri voisi tietysti olla mikä tahansa.
// Johtuen 256 värisestä paletista haluamme kuitenkin vain 8 bittisen väriosan.
uusivari -= ((c >> 16) & 0xff);
if (uusivari < 0) uusivari = 0;

// piirretään pehmennetty ja kylmennetty pikseli ylöspäin, jotta saadaan liekit menemään ylöspäin
buffer2[x][y-1] = uusivari;

Jos et jaksa tai halua tehdä kylmennyskarttaa, voit tietenkin vähentää väristä myös jonkun arvotun arvon. Efekti on kyllä tällöin erittäin paljon heikomman näköinen.

Paletti

Paletin on syytä olla moniväriliuku. Esimerkiksi valkoisen, keltaisen ja oranssin kautta punaiseen, jonka kautta vielä mustaan. Palettien tekemiseen suosittelen T.M.:n tekemää generaattoria.
Paletit voi toki tehdä myös fysiikan lakien mukaan laskemalla heijastuvan valon aallonpituudet kuumuuden mukaan, mutta sitä en tässä oppaassa käsittele.

Yhdistetään oppimamme

On tullut aika yhdistää tämä kaikki yhdeksi koodiksi. Muista kopioida Puskuri1 puskuri2:een lopuksi, sekä piirtää alkuperäinen kuvio aina uutta ruutua aloitettaessa.

Tässä se nyt on.

int buffer1[SCREENWIDTH][SCREENHEIGHT];
int buffer2[SCREENWIDTH][SCREENHEIGHT];

void Piirratuli()
{
    for (int i = 0; i < 20; i++)
    {
        buffer1[SCREENHEIGHT / 2 - 10 + i][SCREENHEIGHT - 10] = 255;
        buffer1[SCREENHEIGHT / 2 - 10 + i][SCREENHEIGHT - 11] = 255;
    }

    // käydään koko ruutu läpi
    for (int x = 1; x < SCREENWIDTH-1; x++)
    {
        for (int y = 1; y < SCREENHEIGHT-1; y++)
        {
            // ympäröivien pikselien keskiarvo
            int c1 = buffer1[x+1][y+1];
            int c2 = buffer1[x-1][y-1];
            int c3 = buffer1[x-1][y+1];
            int c4 = buffer1[x+1][y-1];

            int uusivari = (c1 + c2 + c3 + c4) / 4;

            // väri kylmennyskartasta
            Uint32 c; // Tämä on etumerkitön 32 bittinen kokonaisluku, jota käytämme bittishiftien ja andauksen helpottamiseksi.
            //Tärkeintä on kuitenkin vain saada talteen värin punainen osa ja voit siihen käyttää vapaasti omia keinojasi.
            if (y - kylmakohta >= 0)
                c = getpixel(kylma, x, y - kylmakohta);
            else
                c = getpixel(kylma, x, SCREENHEIGHT + y - kylmakohta);

            // otetaan talteen kylmennyskartan punaväri (8 ylintä bittiä; väri on 24 bittinen). Tämä väri voisi tietysti olla mikä tahansa.
            // Johtuen 256 värisestä paletista haluamme kuitenkin vain 8 bittisen väriosan.
            uusivari -= ((c >> 16) & 0xff);
            if (uusivari < 0) uusivari = 0;

            // piirretään pehmennetty ja kylmennetty pikseli ylöspäin, jotta saadaan liekit menemään ylöspäin
            buffer2[x][y-1] = uusivari;
        }
    }
    // Piirretään kuva näytölle
    for (int x=1;x<SCREENWIDTH-1;x++)
        for (int y=1;y<SCREENHEIGHT-1;y++)
	  {
            putpixel(surface, x,y, paletti[buffer2[x][y]]);
            //vaihdetaan puskurit keskenään.
            buffer[x][y] = buffer2[x][y];
        }

    // kylmennyskarttaa nostetaan ylös
    kylmakohta += 3;
    if (kylmakohta >= SCREENHEIGHT) kylmakohta = 0;
}

Valmiin toteutuksen löydät sivustoni koodausosiolta.

Lopuksi

Kuten jo tuossa alussa ilmaisin, suosittelen kokeilemaan erilaisia variaatoita tästä efektistä. Tällä pehmennyksellä saa monessa paikassa ihmeitä - ja vähän enemmänkin - aikaan.

Pyytäisin taas linkittämään omia versioita tästä efektistä, jos sellaisen viitsit tehdä.

Hyvää koodailua!

Pekka Järvinen, 25.9.2004

Kommentit

tejeez [07.12.2004 21:43:26]

Lainaa #

joo ihan hassu

ZcMander [09.12.2004 20:58:53]

Lainaa #

peki [09.12.2004 23:03:08]

Lainaa #

Vaikuttaa aika hienolta. :)

Palantir [09.11.2005 06:51:05]

Lainaa #

nyt minäkin tajusin hieman "grafiikan" tekniikkaa.

moptim [28.07.2006 18:56:28]

Lainaa #

ois niin ihana jos ois binaari tai vb6... yritin tehdä vb6:lla oman mut iha kämäne oli

moptim [01.09.2006 18:47:30]

Lainaa #

eikä ees sisältäny tommosia...

pukki [08.10.2006 23:56:16]

Lainaa #

Olen itse joskus koodannut pienen (186 tavua :) tuliohjelman seuraavanlaisella algoritmilla:

Jokaisen pikselin arvoksi tulee sen alapuolella olevaa ympäröivien pikselien keskiarvo. Näin tuli liikkuu ylöspäin. Jos halutaan, että tuli feidaantuu vähän nopeammin, vähennetään tästä arvosta vielä yksi.

Kylmennyskarttaa ei tarvita, kun screenbufferiin varataan pari ylimääräistä scanlineä, joita ei tietenkään piirretä ruudulle. Näille kahdelle riville piirretään joka iteraatiolla satunnaisiin paikkoihin muutamia hotspotteja (paletin maksimiarvolla), ja taas muutamia 0-arvoja, jotta tuli ei pääse missään vaiheessa "valloilleen" :)

Mutta juu, näitähän voi tehdä monella tavalla.

TVdata [30.11.2011 18:35:05]

Lainaa #

Mukaan pitäisi laittaa MSVCR70.DLL.

Kirjoita kommentti

Huomio! Kommentoi tässä ainoastaan tämän oppaan hyviä ja huonoja puolia. Älä kirjoita muita kysymyksiä tähän. Jos koodisi ei toimi tai tarvitset muuten vain apua ohjelmoinnissa, lähetä viesti keskusteluun.

Muista lukea keskustelun ohjeet.
Tietoa sivustosta