OpenGL:n perusteet Osa 3: Teksturointi



Samankaltaiset tiedostot
OpenGL:n perusteet - Osa 2: 3D grafiikka

OpenGL:n perusteet - Osa 1: Ikkunan luominen

OpenGL:n perusteet Osa 4: Valot ja varjot

Windowsin sanomanvälitys. Juha Järvensivu 2007

Sisällys. OpenGL 2. Display-listat. Display-Listat. Display-listat. Display-listat. Tietokonegrafiikka / perusteet Ako/T /301 4 ov / 2 ov

OpenGL 2. Sisältö. Tietokonegrafiikka / perusteet T /301 4 ov / 2 ov

Windowsin sanomanvälitys. Juha Järvensivu 2008

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Harjoitustyö: virtuaalikone

Sisällys. T Tietokonegrafiikan perusteet. OpenGL-ohjelmointi 11/2007. Mikä on OpenGL?

ASCII-taidetta. Intro: Python

Mainoksen taittaminen Wordilla

Android. Sähköpostin määritys. Tässä oppaassa kuvataan uuden sähköpostitilin käyttöönotto Android Ice Cream Sandwichissä.

Loppukurssin järjestelyt

Loppukurssin järjestelyt C:n edistyneet piirteet

T Tietokonegrafiikan perusteet. OpenGL-ohjelmointi

Kerta 2. Kerta 2 Kerta 3 Kerta 4 Kerta Toteuta Pythonilla seuraava ohjelma:

3.3 Paraabeli toisen asteen polynomifunktion kuvaajana. Toisen asteen epäyhtälö

Taulukot. Jukka Harju, Jukka Juslin

1 of

Merkkijono määritellään kuten muutkin taulukot, mutta tilaa on varattava yksi ylimääräinen paikka lopetusmerkille:

Matikkaa KA1-kurssilaisille, osa 3: suoran piirtäminen koordinaatistoon

Osoitin ja viittaus C++:ssa

KUVANKÄSITTELY THE GIMP FOR WINDOWS OHJELMASSA

Ohjeita. Datan lukeminen

Alkuarvot ja tyyppimuunnokset (1/5) Alkuarvot ja tyyppimuunnokset (2/5) Alkuarvot ja tyyppimuunnokset (3/5)

Toinen harjoitustyö. ASCII-grafiikkaa

Ohjelmointiharjoituksia Arduino-ympäristössä

Jypelin käyttöohjeet» Ruutukentän luominen

Hämeenlinnan Offset-Kolmio Paino Oy:n aineisto-ohjeet

Tasogeometriaa GeoGebran piirtoalue ja työvälineet

5.3 Ensimmäisen asteen polynomifunktio

IDL - proseduurit. ATK tähtitieteessä. IDL - proseduurit

Tampereen yliopisto Tietokonegrafiikka 2013 Tietojenkäsittelytiede Harjoitus

Tampereen yliopisto Tietokonegrafiikka 2013 Tietojenkäsittelytiede Harjoitus

Tietorakenteet ja algoritmit

Demokoodaus Linuxilla, tapaus Eternity

ATK tähtitieteessä. Osa 3 - IDL proseduurit ja rakenteet. 18. syyskuuta 2014

Pong-peli, vaihe Koordinaatistosta. Muilla kielillä: English Suomi. Tämä on Pong-pelin tutoriaalin osa 2/7. Tämän vaiheen aikana

Datatähti 2019 loppu

Joomla pikaopas. Yksinkertainen opas, jossa neuvotaan esimerkkisivuston teko Joomla julkaisujärjestelmällä vaihe vaiheelta.

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

Tapahtumapohjainen ohjelmointi. Juha Järvensivu 2007

Tietueet. Tietueiden määrittely

Peilaus pisteen ja suoran suhteen Pythonin Turtle moduulilla

Digikuvan peruskäsittelyn. sittelyn työnkulku. Soukan Kamerat Soukan Kamerat/SV

TALLENNETAAN MUISTITIKULLE JA MUISTIKORTILLE

Tehtävä 3 ja aikakausilehden kansi pastissi 4. runokirjan kansi

Perusteet. Pasi Sarolahti Aalto University School of Electrical Engineering. C-ohjelmointi Kevät Pasi Sarolahti

Kun olet valmis tekemään tilauksen, rekisteröidy sovellukseen seuraavasti:

FOTONETTI BOOK CREATOR

Zeon PDF Driver Trial

Muita kuvankäsittelyohjelmia on mm. Paint Shop Pro, Photoshop Elements, Microsoft Office Picture Manager

Collector for ArcGIS. Ohje /

Tuotetietojen täydentäminen sähköpisteelle

Tietorakenteet ja algoritmit

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot. Tiedostot

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Uuden Peda.netin käyttöönotto

TAITAJA 2007 ELEKTRONIIKKAFINAALI KILPAILIJAN TEHTÄVÄT. Kilpailijan nimi / Nro:

Entiteetit erotetaan muusta tekstistä & ja puolipiste. esim. copyright-merkki näkyy sivulla

Ohjelmoinnin perusteet Y Python

Google-dokumentit. Opetusteknologiakeskus Mediamylly

Kuvat. 1. Selaimien tunnistamat kuvatyypit

811312A Tietorakenteet ja algoritmit , Harjoitus 2 ratkaisu

KESKUSTANUORTEN NETTISIVUT- OHJEITA PIIRIYLLÄPITÄJÄLLE 1. KIRJAUTUMINEN

Luku 6. Dynaaminen ohjelmointi. 6.1 Funktion muisti

Mikäli olet saanut e-kirjan latauslinkin sähköpostilla, seuraa näitä ohjeita e-kirjan lataamisessa.

Ohjelmassa muuttujalla on nimi ja arvo. Kääntäjä ja linkkeri varaavat muistilohkon, jonne muuttujan arvo talletetaan.

KUVAN TUOMINEN, MUOKKAAMINEN, KOON MUUTTAMINEN JA TALLENTAMINEN PAINTISSA

Asiointipalvelun ohje

Ohjeet e kirjan ostajalle

Kotisivuohjeet. Eteläpohjalaiset Kylät ry. Sivupohjien rakenne

Kuukauden kuvat kerhon galleriaan lähtien kuukaudenkuvaajan kuvagalleria on siirretty uudelle palvelimelle osoitteeseen:

Tietotekniikan valintakoe

Useimmin kysytyt kysymykset

Kuvan pienentäminen Paint.NET-kuvankäsittelyohjelmalla

Valitse aineisto otsikoineen maalaamalla se hiirella ja kopioimalla (Esim. ctrl-c). Vaihtoehtoisesti, Lataa CSV-tiedosto

2. Lisää Java-ohjelmoinnin alkeita. Muuttuja ja viittausmuuttuja (1/4) Muuttuja ja viittausmuuttuja (2/4)

Nspire CAS - koulutus Ohjelmiston käytön alkeet Pekka Vienonen

GREDDY PROFEC B SPEC II säätäminen

Autentikoivan lähtevän postin palvelimen asetukset

Rekursiolause. Laskennan teorian opintopiiri. Sebastian Björkqvist. 23. helmikuuta Tiivistelmä

Ohjelmoinnin peruskurssi Y1

Luku 6: Grafiikka. 2D-grafiikka 3D-liukuhihna Epäsuora valaistus Laskostuminen Mobiililaitteet Sisätilat Ulkotilat

Harjoitus 6 (viikko 42)

ALVin käyttöohjeet. Kuvaus, rajaus ja tallennus puhelimella ALVin -mobiilisovelluksen avulla dokumentit kuvataan, rajataan ja tallennetaan palveluun.

Määrittelydokumentti

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Harjoitus 4 (viikko 47)

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Sisällys. Yleistä attribuuteista. Näkyvyys luokan sisällä. Tiedonkätkentä. Aksessorit. 4.2

Toinen harjoitustyö. ASCII-grafiikkaa 2017

S Laskennallinen Neurotiede

Harjoituksen aiheena on tietokantapalvelimen asentaminen ja testaaminen. Asennetaan MySQL-tietokanta. Hieman linkkejä:

811312A Tietorakenteet ja algoritmit, , Harjoitus 6, Ratkaisu

Ohjelmoinnin perusteet Y Python

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Transkriptio:

OpenGL:n perusteet Osa 3: Teksturointi OpenGL on käyttöjärjestelmäriippumaton kirjasto 2D- ja 3D-grafiikan piirtoon. Tämä artikkelisarja opettaa sinulle 3D-grafiikan perusteet OpenGL:ää käyttäen. Esimerkeissä käytetään C\C++ kieltä. Tämä on artikkelisarjan kolmas osa. 1 Johdatus tekstuureihin Monitahokkaalla voidaan siis helposti jäljitellä kappaleen muotoa, mutta entäpä sen pintakuviota? Tietysti voimme värittää monitahokkaan glcolor3f()-funktion kutsuilla, mutta harvan kappaleen pinnan väritys on kauniin tasainen. Voisimme tietenkin jakaa monitahokkaan tahot yhä pienempiin ja pienempiin polygoneihin, jolloin saisimme aikaiseksi millaisen värikuvion tahansa, mutta vähänkään shakkilautaa monimutkaisemmissa kuvioissa polygonien määrä nousisi valtavan suureksi. Olisikin hyvä, jos voisimme päällystää monitahokkaan valmiilla kuvalla, joka sisältäisi tarvittavan pintakuvion. Juuri tätä varten ovat olemassa tekstuurit. Tekstuuri voidaan piirtää erikseen jollakin kuvankäsittelyohjelmalla tai se voidaan generoida ajon aikana. Esim. shakkilautakuvion tuottaminen algoritmillisesti ei ole kovin hankalaa. Asteroidin malli ilman tekstuuria ja sen kanssa. Oikealla käytetty tekstuuri. Kuinka sitten määritellään miten tekstuuri asettuu monitahokkaan pinnalle? Tämä tehdään tekstuurikoordinaattien avulla. Jokaiselle verteksille määritellään sen sijainnin lisäksi myös missä kohtaa tekstuuria se sijaitsee. Tekstuurin origo sijaitsee sen vasemmassa alakulmassa. X-akseli kasvaa oikealle ja Y-akseli ylöspäin. Siis tavallisen koordinaatiston tapaan. Huomattavaa kuitenkin on, että riippumatta tektuurin koosta tai sen leveyden ja korkeuden suhteesta oikean ylänurkan koordinaatit ovat aina ( 1, 1 ).

Tekstuurikoordinaatit annetaan OpenGL:lle gltexcoord2f()-funktiolla. Funktiota kutsutaan jokaiselle verteksille erikseen. Sen prototyyppi näyttää tältä: void gltexcoord2f(glfloat s, GLfloat t); s ja t ovat siis tekstuurikoordinaatin x ja y komponentit. Huomattavaa lisäksi on, että teksturointi täytyy myös laittaa päälle glenable()-funktiolla, jolle annetaan parametrina GL_TEXTURE_2D. Käytimme tässä siis 2D-tekstuureja. On myös muita tekstuurityyppejä kuten 1D-tekstuurit. Silloin koordinaatti annettaisiin funktiolla gltexcoord1f() ja teksturointi laitettaisiin päälle antamalla glenable()-funktiolle parametriksi GL_TEXTURE_1D. Kuten glvertex-sarjan funktiolla annetut koordinaatit kerrottiin modelview-matriisilla, niin gltexcoord-sarjan funktioilla annetut koordinaatit kerrotaan tekstuurimatriisilla. Tätä matriisia voi muokata samoin kuin modelview-matriisiakin. Tekstuurimatriisi valitaan glmatrixmode()-funktiolla, jolle annetaan parametrina GL_TEXTURE ja sitä voi muokata samoilla funktioilla kuin kaikkia muitakin matriiseja eli esim. gltranslatef() ja glrotatef(). Teksturoinnin hoitaa näytönohjaimen teksturointiyksikkö. Jotta teksturointi olisi mahdollista, pitää tekstuuri ensin sitoa tähän teksturointiyksikköön glbindtexture()- funktiolla. Sen prototyyppi näyttää tältä. void glbindtexture(glenum target, GLuint texture);

Target on sidottavan tekstuurin tyyppi ja texture sen tunnus. Vain yksi tekstuuri voi olla sidottuna kerrallaan (tästä on tosin poikkeus, josta puhumme kappaleessa 8)! Kun polygoni piirretään, teksturoidaan se sillä tekstuurilla, joka on sillä hetkellä sidottuna teksturointiyksikköön. Myös kaikki tekstuureihin vaikuttavat funktiot, joista tässä artikkelissa puhumme vaikuttavat siihen tekstuuriin, joka on sillä hetkellä sidottuna. 2 Tekstuuriobjektit OpenGL:ssä tekstuurit ovat näytönohjaimen muistissa sijaitsevia objekteja, jotka täytyy luoda ennen käyttöä. Tekstuurin luominen tehdään funktiolla glgentextures(). Sen prototyyppi näyttää tältä: void glgentextures(glsizei n, GLuint *textures); Parametri n kertoo kuinka monta tekstuuriobjektia luodaan ja parametri textures on osoitin taulukkoon, johon luotujen tekstuurien tunnukset laitetaan. Luotu tekstuuri on kuitenkin tyhjä, eikä sisällä mitään dataa. Jos kokeilisit teksturoida tyhjällä tekstuurilla saisit tulokseksi pelkkää valkoista väriä. Niinpä tekstuuriin täytyy ladata dataa. OpenGL:ssä on useita eri tekstuurityyppejä, mutta hyödyllisin niistä on 2D-tekstuuri, joka vastaa tavallista kuvaa. Tällainen tekstuuridata ladataan funktiolla glteximage2d(). Prototyyppi on tämän näköinen: void glteximage2d( GLenum target, GLint level, GLint components, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type,

const GLvoid *pixels); Parametrin target arvon pitää olla aina GL_TEXTURE_2D. Parametrien level ja border arvojen pitää olla nolla. Components kertoo tekstuuriin tulevien värikomponenttien määrän. Tämä on yleensä 3 (RGB). Joskus myös 4 (RGBA), jos alphakanava on mukana, mutta siitä enemmän kappaleessa läpinäkyvyys. Width ja height kertovat tekstuurin leveyden ja korkeuden. Näiden lukujen täytyy olla kakkosen potensseja eli 32, 64, 128, 256 jne. Näillä arvoilla on myös jokin yläraja, jonka saat selville funktiolla glgetintegerv(), parametrilla GL_MAX_TEXTURE_SIZE. Hyvin vanhoissa näytönohjaimissa raja on 256, kun taas uusissa 4096 tai jopa enemmän. Format kertoo tekstuurin datan muodon. Tämä on joko GL_RGB tai GL_RGBA jos alphakin on mukana. Viimeinen parametri on osoitin varsinaiseen ladattavaan dataan, joka on esim. piirretty kuvankäsittelyohjelmalla tai generoitu jollakin algoritmilla. Type-parametri kertoo tämän datan tyypin. Vaihtoehdot ovat: GL_UNSIGNED_BYTE, GL_BYTE, GL_UNSIGNED_SHORT, GL_SHORT, GL_UNSIGNED_INT, GL_INT, ja GL_FLOAT. Ladattavan datan pitää sisältää tekstuurin kaikkien pikselien värit tyyliin RGBRGBRGB... jne. Eli ensimmäisen pikselin värin Red, Green ja Blue komponentit, sitten seuraavan jne. Jos tekstuurissa on alphakanava mukana, pitää datan taas olla muodossa RGBARGBARGBA... jne. Jos datan tyyppi on GL_FLOAT pitää näiden arvojen olla välillä 0-1. Muuten suurin käytetyllä tyypillä esitettävissä oleva luku tulkitaan luvuksi 1 ja pienin luvuksi 0. Huomaa, että data ladataan siihen tekstuuriin, joka on sillä hetkellä sidottuna teksturointiyksikköön. Jos tekstuuri sisältää jo dataa, korvautuu se uudella. Kun tekstuuri käy turhaksi, se voidaan poistaa funktiolla gldeletetextures(glsizei n, const GLuint * textures). Parametrien merkitykset ovat samat kuin glgentextures()- funktiossakin, eli poistettavien tekstuurien määrä ja osoitin taulukkoon, joka sisältää poistettavien tekstuurien tunnukset.

3 Tekstuurin reunat ja suodatus Mitä mahtaakaan tapahtua, jos annetut tekstuurikoordinaatit menevät tekstuurin reunojen ulkopuolelle? Sen voit valita itse. Vaihtoehdot ovat joko korvata ulkopuoliset pikselit jollain vakio värillä (mustalla), korvata ne lähimmällä reunapikselillä tai alkaa toistaa pikseleitä tekstuurin toisesta reunasta. Reunakäyttäytyminen asetetaan funktiolla gltexparameteri(). Prototyyppi näyttää tältä: void gltexparameteri( GLenum target, GLenum pname, GLint param ); Ensimmäinen parametri on tekstuurin tyyppi (tässä tapauksessa GL_TEXTURE_2D). Toisen parametrin on oltava GL_TEXTURE_WRAP_S tai GL_TEXTURE_WRAP_T. Se ilmaisee asetetaanko käyttäytyminen vaaka vai pystyakselille. Voit siis laittaa kummallekkin akselille erilaisen. Viimeinen parametri on GL_CLAMP, GL_CLAMP_TO_EDGE, tai GL_REPEAT. Vaihtoehdot on kuvattu alla olevassa kuvassa. 3D-korttien alkuaikoina oli Voodoo-korttien ajureissa bugi, jonka takia GL_CLAMP käyttäytyi samoin kuin GL_CLAMP_TO_EDGE. Monet sovelluksen tekijät luottivat sokeasti tähän käyttäytymiseen tarkistamatta asiaa dokumentaatiosta ja näin ollen kun ohjelmia käytettiin korteilla, joissa tätä bugia ei ollut, oli grafiikka joskus virheellistä. Tämän ja sen takia, että GL_CLAMP:n oikeaoppinen käyttäytyminen on käytännössä hyödytön, monet näytönohjainvalmistajat jättivät mahdollisuuden laittaa tämä bugi päälle omissa näytönohjaimissaan.

Mahdollisuus laittaa GL_CLAMP-bugi päälle GeForce4-kortin ajureissa. Kun teksturoitua kappaletta katsotaan niin läheltä, että yksi tekstuurin kuvapiste eli tekseli näyttää suuremmalta kuin pikseli, kuva ns. puuroutuu eli muuttuu (lego)palikkamaiseksi. Asia voidaan korjata suodatuksella, joka saadaan päälle tutulla gltexparameteri()-funktiolla, joka saa toisena parametrinaan GL_TEXTURE_MAG_FILTER ja viimeisenä parametrinaan joko GL_NEAREST, jos suodatus halutaan pois päältä, tai GL_LINEAR jos suodatus halutaan päälle. Oletuksena suodatus on jo valmiina päällä. Vasemmalla ilman suodatusta ja oikealla sen kanssa. Vastaavasti kun kappale on niin kaukana, että yhden pikselin alalle mahtuu monta tekseliä alkaa kuva vilkkua. Tähänkin ongelmaan on ratkaisu. Sitä kutsutaan termillä mipmapping. Ideana on tallentaa tekstuurista monta erikokoista versiota ja valita niistä sitten se, joka lähinnä vastaa oikeaa kokoa. Mipmapping asetetaan päälle gltexparameteri()-funktiolla. Tällä kertaa keskimmäisenä parametrina on GL_TEXTURE_MIN_FILTER ja viimeisenä GL_LINEAR_MIPMAP_LINEAR. Lisäksi meidän on generoitava mipmap:it. OpenGL osaa tehdä tämän automaattisesti useammallakin eri tavalla, mutta helpoimmalla pääset kun lataat datan glteximage2d()- funktion sijasta glubuild2dmipmaps()-funktiolla. Sen prototyyppi on seuraavanlainen. int glubuild2dmipmaps(

GLenum target, GLint components, GLint width, GLint height, GLenum format, GLenum type, const void * data ); Parametrien merkitykset ovat samat kuin glteximage2d()-funktiollakin. 4 Läpinäkyvyys Vasemmalla ilman mipmap:eja ja oikealla niiden kanssa. Yksi kysymys, joka usein nousee esille on, kuinka jokin tietty tekstuurin väri saadaan läpinäkyväksi. Mitään väriä ei voida suoraan asettaa läpinäkyväksi, vaan värille täytyy määrittää tavallisten red, green ja blue komponenttien lisäksi vielä neljäs läpinäkyvyyskomponentti eli ns. alphakomponentti. Useimmat kuvankäsittelyohjelmat kuten Paint Shop Pro ja Photo Shop tukevat alphakanavaa. Kuvaformaateista esim. TGA ja PNG tukevat alphakanavaa. Voit myös generoida alphakanavan ajon aikana. Kun lataat tekstuurin, testaa onko pikselin väri se, jonka haluat läpinäkyväksi. Jos on aseta sen alpha nollaksi, muuten ykköseksi (tai jos et käytä GL_FLOAT-tyyppiä, niin käyttämäsi tyypin maksimi arvoon).

Kuvassa vasemmalla tekstuuri, joka sisältää kuvan avaruushirviöstä. Keskellä tämän tekstuurin alphakanava. Oikealla tekstuuri piirretty taustan päälle, niin että pikselit joiden alphan arvo on pienempi kuin 0.5 (musta) on jätetty piirtämättä. On kaksi tapaa saada aikaan läpinäkyvyys alphan avulla. Ne ovat GL_ALPHA_TEST ja GL_BLEND. Ne voidaan laittaa päälle glenable()-funktiolla antamalla kyseinen symboli parametrina. GL_ALPHA_TEST:n ideana on asettaa funktio, joka joko hyväksyy tai hylkää pikselin sen alphan arvon perusteella. Tämä funktio asetetaan glalphafunc()-funktiolla. Sen prototyyppi näyttää tältä: void glalphafunc(glenum func, GLclampf ref); Ensimmäinen parametri kertoo funktion. Sen mahdolliset arvot ovat GL_NEVER, GL_LESS, GL_EQUAL, GL_GREATER ja GL_ALWAYS. Nimestä pystyy helposti näkemään mitä ne tekevät. Viimeinen kertoo tämän funktion käyttämän vertailuarvon. Esim. jos haluaisimme, että pikseli piirretään vain kun sen alphan arvo on yli 0.5 käyttäisimme kutsua glalphafunc(gl_greater, 0.5);. GL_BLEND:n idea on hieman erilainen. Siinä pikselin lopullinen väri lasketaan sen alkuperäisestä väristä ja ikkunassa pikselin paikalla olevan aikaisemman pikselin väristä. Lopullinen väri lasketaan kaavalla S*uusiPikseli+D*vanhaPikseli. Parametrit S ja D asetetaan funktiolla glblendfunc(). Prototyyppi on seuraavan näköinen: glblendfunc(glenum sfactor, GLenum dfactor);

sfactor asettaa S parametrin ja sfactor D parametrin. Kummatkin arvot on valittava seuraavista: GL_ZERO, GL_ONE, GL_DST_COLOR, GL_SRC_COLOR, GL_ONE_MINUS_DST_COLOR, GL_ONE_MINUS_SRC_COLOR, GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA, GL_DST_ALPHA ja GL_ONE_MINUS_DST_ALPHA. Nimistä on helppo päätellä mitä ne ovat, kun panet merkille, että SCR tarkoittaa uutta pikseliä ja DST vanhaa pikseliä. Esim. jos haluaisimme tekstuurin alphakanavan määräävän pikselin läpinäkyvyyden käyttäisimme kutsua: glblendfunc(gl_scr_alpha, GL_ONE_MINUS_SCR_ALPHA). GL_BLEND siis mahdollistaa osittaisen läpinäkyvyyden toisin kuin GL_ALPHA_TEST. GL_ALPHA_TEST:llä ja GL_BLEND:llä on vielä yksi merkittävä ero. GL_ALPHA_TEST nimittäin jättää läpinäkyvät pikselit piirtämättä syvyyspuskuriin, kun taas GL_BLEND piirtää kaikki pikselit syvyyspuskuriin. Myös täysin läpinäkyvät. Olettaen tietenkin, että GL_DEPTH_TEST on päällä. Kaksiulotteista teksturoitua suorakaiteen muotoista osittain läpinäkyvää polygonia kutsutaan joskus kuvahahmoksi (englanniksi sprite). Ne ovat varsin käytännöllisia 2Dgrafiikassa. Kaksiulotteisten kuvien piirtoon löytyy OpenGL.stä ihan oma funktionsa: glbitmap(), mutta se on hitaampi ja vähemmän käytännöllinen kuin teksturoidun nelikulmion piirto, joten en suosittele sen käyttöä. 5 Texture environment Polygoneillehan voitiin määrätä väri glcolor3f()-funktiolla, mutta kuinka tämä väri sitten yhdistyy tekstuurin väriin? Tästä asiasta huolehtii texture environment. Texture environment on itse asiassa funktio, joka saa syötteenään varsinaisen värin ja tekstuurin värin ja laskee näistä pikselin lopullisen värin. Tämä funktio pitää valita muutamasta valmiista vaihtoehdosta. Texture environment:in funktio valitaan funktiolla gltexenvi(), jonka prototyyppi näyttää tältä: void gltexenvi(glenum target, GLenum pname, GLint param);

Ensimmäisen parametrin arvon on aina oltava GL_TEXTURE_ENV ja toisen GL_TEXTURE_ENV_MODE. Kolmas on asetettava funktio. Sen mahdolliset arvot ovat: GL_MODULATE, GL_DECAL ja GL_ADD. GL_MODULATE kertoo värit keskenään ja GL_ADD taas laskee ne yhteen. GL_DECAL sen sijaan interpoloi näiden arvojen välillä käyttäen viitteenä tekstuurin alphakanavaa. Alphan arvolla yksi väri on tekstuurin väri ja arvolla nolla varsinainen väri. Muilla arvoilla jossakin tässä välissä. Jos tekstuurissa ei ole alphakanavaa, katsotaan alphan arvon olevan yksi. 6 OpenGL:n laajennukset Ennen kuin voimme jatkaa, pitää meidän puhua jokunen sana OpenGL:n laajennuksista (englanniksi extensions). Näytönohjaimet kehittyvät koko ajan ja niihin ilmestyy uusia ominaisuuksia. Tämän takia myös niitä käyttävien rajapintojen on kehityttävä mukana. Direct3D:n tapauksessa tämä on ratkaistu julkaisemalla siitä uusi versio vuoden parin välein. Tällä uudella versiolla ei ole välttämättä mitään yhteistä vanhan kanssa ja koko rajapinta on pahimmillaan opeteltava uudestaan. OpenGL:ssä sen sijaan rakennetaan vanhan päälle. Aina, kun jokin uusi ominaisuus ilmestyy näytönohjaimiin, julkaistaan siitä laajennus OpenGL:ään. Laajennus tuo mukanaan nipun uusia funktioita ja/tai uusia parametrejä vanhoihin funktioihin. Joskus ei kumpaakaan. Laajennuksista pitää kirjaa SGI:n laajennusrekisteri osoitteessa: http://oss.sgi.com/projects/ogl-sample/registry/.

Lisäksi kaikkien näytönohjainvalmistajien kotisivuilta on löytyy sama rekisteri. Voidaksesi käyttää laajennuksia tarvitset myös uuden otsikkotiedoston glext.h. Saat sen osoitteesta: http://oss.sgi.com/projects/ogl-sample/abi/glext.h. Se mitä laajennuksia käytössä on, riippuu koneen näytönohjaimesta. Uudet näytönohjaimet tukevat lähes kaikkia laajennuksia, kun taas hyvin vanhat vain muutamaa. Saat selville tuetut laajennukset funktiolla glgetstring(), jolle annetaan parametrinä GL_EXTENSIONS. Se palauttaa osoittimen merkkijonoon, joka sisältää kaikkien tuettujen laajennusten nimet välilyönnillä eroteltuna. Laajennus ei siis tuo välttämättä mitään uusia funktioita tai parametrejä. Se vain sallii jotain sellaista, joka oli aikaisemmin kiellettyä. Hyvä esimerkki tästä on ARB_texture_non_power_of_two-laajennus, joka sallii käytettävän tekstuureja, joiden koko ei ole kakkosen potenssi. Eli jos glgetstring(gl_extensions) palauttamassa merkkijonossa esiintyy nimi GL_ARB_texture_non_power_of_two voit käyttää minkä kokoisia tekstuureja tahansa. Jos taas laajennus tuo uusia parametrejä ei laajennuksen käyttö ole hankalaa silloinkaan. Kaikki uudet symbolit on nimittäin määritelty tiedostossa glext.h. Hyvä esimerkki tällaisesta laajennuksesta on GL_ARB_texture_env_combine-laajennos, joka tuo texture environment:iin rutkasti lisää vaihtoehtoja perinteisten GL_MODULATE:n ja GL_ADD:n lisäksi. Vasta, kun laajennus tuo mukanaan uusia funktioita, on laajennuksen käyttö hieman hankalampaa. Funktioihin pitää nimittäin käydä noutamassa osoittimet. wglgetprocaddress() funktiolla. Sen prototyyppi näyttää tältä: PROC wglgetprocaddress( LPCSTR lpszproc //Name of the extension function ); Eli sille annetaan parametrinä uuden funktion nimi ja se palauttaa osoittimen siihen.

Laajennuksia on tällä hetkellä useampi sata, joten niiden hallinnointi käsin alkaa olla sulaa hulluutta. Tämän takia netti on pullollaan erilaisia kirjastoja laajennusten hallintaan. Niiden periaate on, että ne tarjoavat jonkin helppokäyttöisen funktion, joka lataa yhdellä kutsulla osoittimet kaikkiin näytönohjaimen tukemiin laajennuksiin. Lisäksi laajennusten dokumentaatiot on laadittu niin, että on mahdollista laatia ohjelma, joka generoi tarvittavan alustuskoodin pelkkää dokumenttia tutkimalla. Myös tälläisia ohjelmia löytyy valmiina paljon. Yksi hyvä on GLEW: http://glew.sourceforge.net/. Huomaa vielä, että jokaisella laajennuksen nimellä on jokin etuliite. Esim. ARB, EXT, NV tai ATI. ARB ja EXT ovat laajennuksia, joita kaikkien näytönohjainvalmistajien kortit tukevat. Kun taas muut ovat valmistajakohtaisia laajennuksia. Vältä niiden käyttöä, sillä ne ovat tuettuja yleensä vain yhden valmistajan kortilla. Pyri käyttämään ensisijaisesti ARB-laajennuksia ja toissijaisesti EXT-laajennuksia. 7 Eksoottisemmat tekstuurimuodot 1D- ja 2D-tekstuurien lisäksi OpenGL:ssä kaksi muutakin tekstuurimuotoa: 3D-tekstuurit (GL_TEXTURE_3D_EXT) ja cubemap-tekstuurit (GL_CUBE_MAP_EXT). Kummatkin nämä tekstuurimuodot ovat laajennuksia eli sinun pitää tarkistaa ennen niiden käyttöä, että käytössä oleva näytönohjain tukee niitä. 7.1 3D-tekstuurit 3D-tekstuurit ovat looginen jatko 1D- ja 2D-tekstuureille. 3D tekstuurit ovat laajennus OpenGL:ään ja tämän laajennuksen nimi on GL_EXT_texture3D. Eli niitä voidaan käyttää vain, jos glgetstring(gl_extensions)-kutsun palauttama merkkijono sisältää kyseisen nimen. Tämä laajennus tuo mukanaan yhden uuden funktion glteximage3dext(), jonka prototyyppi näyttää tältä: void glteximage3dext(enum target, int level, enum internalformat, sizei width,

sizei height, sizei depth, int border, enum format, enum type, const void* pixels); Meidän tarvitsee siis hakea osoitin tähän funktioon ennen sen käyttöä. Se tehdään seuraavasti: void (*glteximage3dext)(enum, int, enum, sizei, sizei, sizei, int, enum, enum, const void*)= wglgetprocaddress("glteximage3dext "); Jätin tyyppimuunnoksen selkeyden vuoksi tekemättä. Kun osoitin on haettu voidaan funktiota käyttää normaalisti. glteximage3dext()-funktio toimii aivan samoin kuin glteximage1d() ja glteximage2d():kin. Siinä vain on tekstuurin leveyden ja korkeuden lisäksi mukana syvyys. Lisäksi tekstuurikoorndinaatit pitää antaa gltexcoord3f()- funktiolla ja teksturointi laittaa päälle kutsulla glenable(gl_texture_3d_ext);. 7.2 Cube map-tekstuurit "Cube map"-tekstuuri on nimensä mukaisesti kuution muotoinen tekstuuri. Se muodostuu kuudesta eri 2D-tekstuurista, joista jokainen muodostaa kuution yhden sivun. Myös cube map on laajennus. Sen nimi on ARB_texture_cube_map. Cube map ei tuo uusia funktioita ainoastaan uusia parametrejä. Data cube map:iin ladataan glteximage2d()-funktiolla. Ainoa ero on, että ensimmäiseksi parametriksi tulee GL_TEXTURE_2D:n sijaan TEXTURE_CUBE_MAP_POSITIVE_X_ARB, TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,

TEXTURE_CUBE_MAP_POSITIVE_Z_ARB tai TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, riippuen siitä mikä kuution kuudesta eri sivusta halutaan asettaa. Vastaavasti cube map teksturointi laitetaan päälle glenable()- functiolla, jolle annetaan parametrina TEXTURE_CUBE_MAP_ARB. Tekstuurikoordinaatit toimivat cube map:issa hieman oudosti. Tekstuurikoordinaatti annetaan gltexcoord3f()-funktiolla, mutta koordinaattia vastaa kuutiolla piste, joka saadaan kuution keskipisteestä lähtevän gltexcoord3f()-funktiolla annetun vektorin suuntaisen suoran ja kuution leikkauspisteestä. 8 Multiteksturointi Multiteksturointi tarkoittaa mahdollisuutta päällystää polygoni usealla tekstuurilla yhtä aikaa. Näytönohjaimissa on ns. teksturointiyksikköjä, joista jokaiseen voi sitoa oman tekstuurin. Kun pikseli sitten piirretään vaikuttaa sen väriin jokainen teksturointiyksikkö. Teksturointiyksiköiden määrää kutsutaan näytönohjaimen pikseliliukuhihnan pituudeksi ja näytönohjainvalmistajat kilpailevat sen pituudella. Nykyisissä näytönohjaimissa voi olla jopa 8-16 teksturointiyksikköä, kun taas vanhoissa yleensä vain 2-4. Saat selville näytönohjaimen teksturointiyksiköiden määrän glgetintegerv()-funktiolla, jolle annetaan parametrina GL_MAX_TEXTURE_UNITS_ARB. Kuten muutkin nykyiset hienoudet on myös multiteksturointi laajennus. Multiteksturointi tuo OpenGL:ään useitakin uusia funktioita kuten: void glmultitexcoord1farb(enum texture, GLfloat s), void

glmultitexcoord2farb(enum texture, GLfloat s, GLfloat t), ja void glactivetexturearb(enum texture);. Jokaiseen teksturointiyksikköön voi olla sidottuna kerrallaan yksi tekstuuri. glbindtexture() sitoo tekstuurin kulloinkin aktiivisena olevaan teksturointi yksikköön. Yksi teksturointiyksiköistä voi olla aktiivisena kerrallaan ja aktiivinen yksikkö valitaan glactivetexturearb()-funktiolla, joka saa syötteenään GL_TEXTURE0_ARB, GL_TEXTURE1_ARB, GL_TEXTURE2_ARB jne. eli aktivoitavan teksturointi yksikön. Huomaa myös, että kutsu glenable(gl_texture_2d) laittaa teksturoinnin päälle siihen teksturointiyksikköön, joka on sillä hetkellä aktiivisena, eli funktiota on kutsuttava jokaiselle teksturointiyksikölle erikseen. Tekstuurikoordinaatit vuorostaan annetaan glmultitexcoord2farb()-funktiolla. Sen ensimmäinen parametri kertoo teksturointiyksikön samoin kuin glactivetexturearb()- funktiollakin ja loput tekstuurikoordinaatit. gltexcoord2f()-funktion kutsu vastaa glmultitexcoord2farb()-funktion kutsua, jonka ensimmäinen parametri on GL_TEXTURE0_ARB. Vielä viimeinen ongelma on, mikä sitten määrää kuinka nämä tekstuurit yhdistyvät? Vastaus on tietenkin teksture environment. gltextureenvi()-funktio asettaa texture environment funktion aktivoituna olevaan teksturointiyksikköön. Jokaisella teksturointiyksiköllä on siis oma texture environment funktio. Kun pikseli piirretään saa se aluksi glcolor3f()-funktiolla annetun värin. Sen jälkeen pikselin väriin vaikuttaa vuoronperään jokainen teksturointiyksikkö, jolle teksturointi on laitettu päälle, omalla texture environment funktiollaan. 9 Esimerkkiohjelma Vaikka tässä artikkelissa puhuimmekin OpenGL:än laajennuksista ja monista edistyneimmistä teksturointitekniikoista on esimerkkiohjelma varsin yksinkertainen. Se lataa levyltä kaksi tekstuuria tiedostoista alien.kuva ja tausta.kuva. Ohjelma piirtää kaksi nelikulmaista polygonia, joista toisen se teksturoi alien-tekstuurilla ja toisen tausta-

tekstuurillla. Data näissä tiedostoissa on ns. raakamuodossa eli se voidaan antaa sellaisenaan glteximage2d()-funktiolle. raakadatakuvia voi tallentaa yleisimmillä kuvankäsittelyohjelmilla. Tälläisten raakatiedostojen käyttö ei kuitenkaan ole suositeltavaa sillä ne eivät ole mitään standartiformaatteja ja ne eivät sisällä esimerkiksi tietoa kuvan koosta, joten se on tiedettävä erikseen. Yleensä kannattaakin käyttää jotain hyvää standardia tiedostomuotoa kuten TGA tai PNG. Näiden formaattien lataamisesta voisi kuitenkin kirjoittaa vaikka oman artikkelinsa, joten käytän tässä helpompaa raakadataformaattia. On olemassa valmiita kirjastoja, jotka osaavat lukea lukuisia kuvaformaaatteja. Yksi hyvä tällainen on Devil. Saadaksesi esimerkkiohjelman toimimaan sinun tarvitsee imuroida kuvatiedostot. Voit tehdä sen tästä: http://www.suomipelit.com/files/artikkelit/testi3.zip. Paketti sisältää myös oheisen lähdekoodin ja valmiiksi käännetyn version. #include <windows.h> #include <stdio.h> #include <math.h> #include <gl\gl.h> #include <gl\glu.h> //#include <gl\glext> // Ei tarvita tässä ohjelmassa // Määrittele laitekonteksti globaaliksi sitä nimittäin tarvitaan myös pääfunktiossa. HDC hdc; // Viestinkäsittelijä LRESULT CALLBACK WindowProc(HWND hwnd, UINT umsg, WPARAM wparam, LPARAM lparam) switch (umsg) // Koska piirrämme ikkunan sisällön pääsilmukassa jatkuvasti uudelleen // reakoimme WM_PAINT-viestiin vain tyhjentämällä ikkunan mustaksi. case WM_PAINT: PAINTSTRUCT p; BeginPaint(hwnd, &p); glclear(gl_color_buffer_bit); SwapBuffers(hdc); EndPaint(hwnd, &p); return 0; // Ikkuna yritetään sulkea kutsu PostQuitMessage()-funktiota. case WM_CLOSE: PostQuitMessage(0);

return 0; // Käsittele myös WM_SIZE se lähetetään ikkunalle aina kun sen kokoa muutetaan. // Tämä on oiva tilaisuus muuttaa viewport // oikean kokoiseksi peittämään koko ikkuna. case WM_SIZE: // Ikkunan uusi koko saadaan lparam parametrista LOWORD ja HIWORD makroilla. glviewport(0, 0, LOWORD(lParam), HIWORD(lParam)); return 0; // Viestiä ei käsitelty kutsu DefWindowProc()-funktiota. return DefWindowProc(hwnd, umsg, wparam, lparam); int luoikkuna(unsigned int leveys, unsigned int korkeus, char *otsikko) // Rekisteröi ikkunaluokka WNDCLASS wc; memset(&wc, 0, sizeof(wndclass)); wc.style = CS_HREDRAW CS_VREDRAW CS_OWNDC; wc.hcursor= LoadCursor(NULL, IDC_ARROW); wc.lpfnwndproc = (WNDPROC) WindowProc; wc.hinstance = GetModuleHandle(NULL); wc.lpszclassname = "OpenGLtutoriaali"; if (!RegisterClass(&wc)) return 0; // Luo ikkuna RECT r; r.left=getsystemmetrics(sm_cxscreen)/2-leveys/2; r.top=getsystemmetrics(sm_cyscreen)/2-korkeus/2; r.right=r.left+leveys; r.bottom=r.top+korkeus; AdjustWindowRectEx(&r, WS_CLIPSIBLINGS WS_CLIPCHILDREN WS_OVERLAPPEDWINDOW, FALSE, WS_EX_APPWINDOW); HWND hwnd; hwnd=createwindowex(ws_ex_appwindow, "OpenGLtutoriaali", otsikko, WS_CLIPSIBLINGS WS_CLIPCHILDREN WS_OVERLAPPEDWINDOW, r.left, r.top, r.right-r.left, r.bottom-r.top, NULL, NULL, GetModuleHandle(NULL), NULL); // Luo laitekonteksti hdc=getdc(hwnd); if (!hdc) return 0; // Valitse pikseliformaatti PIXELFORMATDESCRIPTOR pfd; memset(&pfd, 0, sizeof(pixelformatdescriptor)); pfd.nsize=sizeof(pixelformatdescriptor);

pfd.nversion=1; pfd.dwflags=pfd_draw_to_window PFD_SUPPORT_OPENGL PFD_DOUBLEBUFFER; pfd.ipixeltype=pfd_type_rgba; pfd.credbits=8; pfd.cgreenbits=8; pfd.cbluebits=8; pfd.calphabits=8; pfd.cstencilbits=8; pfd.cdepthbits=16; pfd.ilayertype=pfd_main_plane; int pixelformat; pixelformat=choosepixelformat(hdc, &pfd); if (!pixelformat) return 0; if (!SetPixelFormat(hdc, pixelformat, &pfd)) return 0; // Luo renderöintikonteksti HGLRC hrc; hrc=wglcreatecontext(hdc); if (!hrc) return 0; if (!wglmakecurrent(hdc, hrc)) return 0; // Tuo ikkuna näkyviin ShowWindow(hwnd, SW_SHOW); SetForegroundWindow(hwnd); SetFocus(hwnd); // Palauta onnistuminen return 1; // Pääfunktio int WINAPI WinMain(HINSTANCE hinstance, HINSTANCE hprevinstance, LPSTR lpcmdline, int ncmdshow) unsigned char *data; float angle=0; GLuint taustatekstuuri=0; GLuint alientekstuuri=0; FILE *tiedosto=null; unsigned int aika; unsigned int piirtoaika; unsigned int alkuaika; // Luo ikkuna if (!luoikkuna(800, 600, "OpenGL:n perusteet - Osa 3: Teksturointi")) return 0; // Varaa muistia väliaikaiselle datalle data=(unsigned char *)malloc(256*256*4*sizeof(unsigned char)); if (!data) return 0; // luo yksi tekstuuriobjekti glgentextures(1, &taustatekstuuri);

// Sido äsken luotu tekstuuri teksturointiyksikköön glbindtexture(gl_texture_2d, taustatekstuuri); // Lataa kuvadata tiedostosta. tiedosto=fopen("tausta.kuva", "rb"); if (!tiedosto) return 0; fread(data, 3, 128*128, tiedosto); fclose(tiedosto); // Lataa data tekstuuriin glteximage2d(gl_texture_2d, 0, 3, 128, 128, 0, GL_RGB, GL_UNSIGNED_BYTE, data); // Suodatus päälle gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // luo yksi tekstuuriobjekti glgentextures(1, &alientekstuuri); // Sido äsken luotu tekstuuri teksturointiyksikköön glbindtexture(gl_texture_2d, alientekstuuri); // Lataa kuvadata tiedostosta tiedosto=fopen("alien.kuva", "rb"); if (!tiedosto) return 0; fread(data, 4, 256*256, tiedosto); fclose(tiedosto); // Lataa data tekstuuriin. Huomaa, että tässä on nyt alpha mukana! glteximage2d(gl_texture_2d, 0, 4, 256, 256, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // Suodatus päälle gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Vapauta väliaikainen data free(data); // Teksturointi päälle glenable(gl_texture_2d); // Viestinkäsittelysilmukka alkuaika=gettickcount(); MSG msg; while(1) if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) if (msg.message==wm_quit) break; TranslateMessage(&msg); DispatchMessage(&msg); else // Käytämme GetTickCount()-funktiota, joka palauttaa ajan millisekunneissa, // laskemaan kuvan piirtämiseen kuluneen ajan. // Näin voimme ajastaa alienin pyörimään samalla nopeudella kaikilla kokeilla. aika=gettickcount(); piirtoaika=aika-alkuaika; if (piirtoaika>0)

alkuaika=aika; // Kasvata pyörityskulmaa hieman seuraavaa framea varten. // Alien pyörii nyt 0.06 astetta millisekunnissa // eli 1 kierroksen 6 sekunnissa angle+=0.06*piirtoaika; // Tyhjennä puskuri glclear(gl_color_buffer_bit); // Piirretään ensin tausta // Luo 2D koordinaatisto glmatrixmode(gl_projection); glloadidentity(); gluortho2d(0, 800, 0, 600); // Aseta modelview-matriisi glmatrixmode(gl_modelview); glloadidentity(); // Aseta tekstuurimatriisi niin, että taustaan saadaan hieman liikettä. glmatrixmode(gl_texture); glloadidentity(); gltranslatef(sin(0.01*angle), cos(0.01*angle), 0); // Ota alpha test pois päältä sitä ei tarvita taustassa gldisable(gl_alpha_test); // Sido taustatekstuuri glbindtexture(gl_texture_2d, taustatekstuuri); // Piirrä koko ikkunan peittävä nelikulmio glbegin(gl_quads); gltexcoord2f(0, 0); glvertex2f(0, 0); gltexcoord2f(6, 0); glvertex2f(800, 0); gltexcoord2f(6, 4); glvertex2f(800, 600); gltexcoord2f(0, 4); glvertex2f(0, 600); glend(); // piirretään sitten alieni // Aseta 3D-koordinaatisto glmatrixmode(gl_projection); glloadidentity(); gluperspective(60, 800.0/600.0, 1, 10); // Aseta modelview-matriisi glmatrixmode(gl_modelview); glloadidentity(); gltranslatef(0, 0, -2); // Siirrä alienia hieman kauemmaksi kamerasta glrotatef(angle, 0, 1, 0); // Pyöritä alienia y-akselin ympäri

// Aseta tekstuurimatriisi glmatrixmode(gl_texture); glloadidentity(); // laita alpha test päälle glenable(gl_alpha_test); // aseta alpha test niin että vain pikselit, // joiden alpha arvo on yli 0.5 piirretään glalphafunc(gl_greater, 0.5); // Sido alien tekstuuri glbindtexture(gl_texture_2d, alientekstuuri); // Piirrä alien glbegin(gl_quads); gltexcoord2f(0, 0); glvertex3f(-1, 1, 0); gltexcoord2f(1, 0); glvertex3f( 1, 1, 0); gltexcoord2f(1, 1); glvertex3f( 1, -1, 0); gltexcoord2f(0, 1); glvertex3f(-1, -1, 0); glend(); // Vaihda puskuri näytölle. SwapBuffers(hdc); return 0; 10 Loppusanat Tässä artikkelissa opit teksturoimaan. Seuraavassa ja viimeisessä osassa puhumme valoista ja varjoista. Raportoithan kaikki tästä artikkelista löytämäsi virheet (niin kirjoitus-, kuin asiavirheetkin) osoitteeseen markus.ilmola@pp.inet.fi, niin korjaan ne mahdollisimman nopeasti. Myös kaikki kommentit ja kysymykset ovat tervetulleita.

11 Liite 1: TGA-tiedoston lataus Käytimme esimerkkiohjelmassamme raakadatakuvia, koska niiden lataus ei vaadi mitään ylimääräisiä toimenpiteitä. Niiden käyttö ei kuitenkaan ole suositeltavaa, koska ne eivät sisällä mitään tietoa kuvan tyypistä, koosta jne. Seuraavaksi luomme funktion, joka lataa TGA-kuvia. TGA on siitä hyvä formaatti, että se tukee alphakanavaa, eikä ole kovin hankala ladatakkaan. Yksinkertaisuuden vuoksi tekemämme funktio ei tue pakattuja tai paletillisia kuvia. Eli kun tallennat kuvan tarkista, että sen värisyvyys on 24 bittiä ja muoto pakkaamaton. TGA-tiedostojen speksit löydät esim. osoitteesta www.wotsit.org. Funktio palauttaa osoittimen kuvadataan, ja tallentaa sen leveyden, korkeuden ja värikomponenttien määrän (3 tai 4) muuttujiin width, height ja components. Koska funktio varaa muistin datalle täytyy data vapauttaa free()-funktiolla, kun se käy turhaksi. unsigned char *loadtgafile(const char *filename, unsigned int *width, unsigned int *height, unsigned int *components) // Avaa tiedosto FILE *file=null; file = fopen(filename, "rb"); if (!file) fprintf(stderr,"can't find file %s!\n", filename); return NULL; // Ensimmäinen tavu kertoo kuvatunnistekentän pituuden unsigned char idlen=0; fread(&idlen, 1, 1, file); // Toinen tavu kertoo sisältääkö kuva paletin (jota emme tässä tue). unsigned char colormap=0; fread(&colormap, 1, 1, file); if (colormap!=0) fprintf(stderr,"the file %s has a color map witch are not supported!\n", filename); fclose(file); return NULL; // Kolmas tavu kertoo kuvan tyypin. unsigned char type=0; fread (&type, 1, 1, file); if (type!=2) // Tuemme tässä tyyppiä 2 (pakkaamaton)

fprintf(stderr,"the file %s has unsupported type (%d)!\n", filename, type); fclose(file); return NULL; // 12-14 tavut kertovat kuvan leveyden ja 14-16 tavut kuvan korkeuden unsigned char lo, hi; fseek(file, 12, SEEK_SET); fread(&lo, 1, 1, file); fread(&hi, 1, 1, file); *width=makeword(lo, hi); fread(&lo, 1, 1, file); fread(&hi, 1, 1, file); *height=makeword(lo, hi); // 16 tavu kertoo kuvan värisyvyyden 24=RGB ja 32=RGBA unsigned char bits=0; fseek(file, 16, SEEK_SET); fread(&bits, 1, 1, file); if (bits!=24 && bits!=32) fprintf(stderr,"the file %s has unsupported color depth (%d)!\n", filename, bits); fclose(file); return NULL; *components=bits/8; // Hyppää yli kuvatunniste, jonka koon luimme alussa fseek(file, 18+idLen, SEEK_SET); // lue data unsigned char *data=null; unsigned int i; unsigned char temp; data=(unsigned char *)malloc(*width * *height * *components); if (!data) fprintf(stderr,"no memory for data in file %s!\n", filename); fclose(file); return NULL; for (i=0; i<*width * *height; i++) fread(data + i * *components, *components, 1, file); // Tiedosto sisältää datan muodossa BRG(A), vaihdamme sen muotoon RGB(A). temp=data[*components * i]; data[*components * i]=data[*components * i + 2]; data[*components * i + 2]=temp; // Vähän näkökulmasta riippuen TGA tiedoston sisältämän kuvan // voidaan sanoa olevan ylösalaisin, joten haluamme kääntää sen. unsigned int x, y; for (y=0; y<*height/2; y++)

for (x=0; x<*components * *width; x++) temp=data[y * (*components * *width) + x]; data[y * (*components * *width) + x]=data[((*height - 1) - y)*(*components * *width) + x]; data[((*height - 1) - y) * (*components * *width) + x]=temp; // Kaikki ok fclose(file); return data; Nyt voit käyttää funktiota seuraavasti: unsigned char *data; unsigned int w, h, c; unsigned int tekstuuri; // luo yksi tekstuuriobjekti glgentextures(1, &tekstuuri); // Sido äsken luotu tekstuuri teksturointiyksikköön glbindtexture(gl_texture_2d, tekstuuri); // Lataa kuvadata tiedostosta. data=loadtgafile("tiedostonnimi.tga", &w, &h, &c); if (!data) tiedoston_lataus_epäonnistui; // Lataa data tekstuuriin if (c==3) // Ei alphakanavaa glteximage2d(gl_texture_2d, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, data); else // Alphakanava mukana glteximage2d(gl_texture_2d, 0, 4, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); // Suodatus päälle gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); gltexparameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); free(data); // Dataa ei enää tarvita.