Lekce 24

Lekce 24 - V²pis OpenGL rozÜφ°enφ, o°ezßvacφ testy a textury z TGA obrßzk∙

V tΘto lekci se nauΦφte, jak zjistit, kterß OpenGL rozÜφ°enφ (extensions) podporuje vaÜe grafickß karta. VypφÜeme je do st°edu okna, se kter²m budeme moci po stisku Üipek rolovat. Pou₧ijeme klasick² 2D texturov² font s tφm rozdφlem, ₧e texturu vytvo°φme z TGA obrßzku. Jeho nejv∞tÜφmi p°ednostmi jsou jednoduchß prßce a podpora alfa kanßlu. Odbourßnφm bitmap u₧ nebudeme muset inkludovat knihovnu glaux.

Tento tutorißl je daleko od prezentace grafickΘ nßdhery, ale nauΦφte se n∞kolik nov²ch v∞cφ. Pßr lidφ se m∞ ptalo na OpenGL rozÜφ°enφ a na to, jak zjistit, kterΘ jsou podporovßny konkrΘtnφm typem grafickΘ karty. Mohu sm∞le °φci, ₧e s tφmto po doΦtenφ nebudete mφt nejmenÜφ problΘmy. TakΘ se dozvφte, jak rolovat Φßstφ scΘny bez toho, aby se ovlivnilo jejφ okolφ. Pou₧ijeme o°ezßvacφ testy (scissor testing). Dßle si ukß₧eme, jak vykreslovat linky pomocφ line strips a co je d∙le₧it∞jÜφ, kompletn∞ odbourßme knihovnu glaux, kterou jsme pou₧φvali kv∙li texturßm z bitmapov²ch obrßzk∙. Budeme pou₧φvat Targa (TGA) obrßzky, se kter²mi se snadno pracuje a kterΘ podporujφ alfa kanßl.

ZaΦneme programovat. Prvnφ v∞cφ, kterΘ si vÜimneme u vklßdßnφ hlaviΦkov²ch soubor∙ je, ₧e neinkludujeme knihovnu glaux (glaux.h). TakΘ nep°ilikujeme soubor glaux.lib. U₧ nebudeme pracovat s bitmapami, tak₧e tyto soubory v projektu nepot°ebujeme.

#include <windows.h>// HlaviΦkov² soubor pro Windows

#include <stdio.h>// HlaviΦkov² soubor pro standardnφ vstup/v²stup

#include <stdarg.h>// HlaviΦkov² soubor pro funkce s prom∞nn²m poΦtem parametr∙

#include <string.h>// HlaviΦkov² soubor pro prßci s °et∞zci

#include <gl\gl.h>// HlaviΦkov² soubor pro OpenGL32 knihovnu

#include <gl\glu.h>// HlaviΦkov² soubor pro Glu32 knihovnu

HDC hDC = NULL;// Privßtnφ GDI Device Context

HGLRC hRC = NULL;// Trval² Rendering Context

HWND hWnd = NULL;// Obsahuje Handle naÜeho okna

HINSTANCE hInstance;// Obsahuje instanci aplikace

bool keys[256];// Pole pro uklßdßnφ vstupu z klßvesnice

bool active = TRUE;// Ponese informaci o tom, zda je okno aktivnφ

bool fullscreen = TRUE;// Ponese informaci o tom, zda je program ve fullscreenu

P°idßme prom∞nnΘ. Scroll bude pou₧ito pro rolovßnφ Φßstφ scΘny nahoru a dol∙. Druhß prom∞nnß, maxtokens, bude uklßdat zßznam kolik rozÜφ°enφ je podporovßno grafickou kartou. Base u₧ tradiΦn∞ ukazuje na display listy fontu. Do swidth a sheight nagrabujeme aktußlnφ velikost okna, pomohou nßm vypoΦφtat koordinßty pro o°ezßnφ oblasti okna, kterΘ umo₧nφ rolovßnφ.

int scroll;// Pro rolovßnφ okna

int maxtokens;// PoΦet podporovan²ch rozÜφ°enφ

GLuint base;// Zßkladnφ display list fontu

int swidth;// èφ°ka o°ezanΘ oblasti

int sheight;// V²Üka o°ezanΘ oblasti

NapφÜeme strukturu, kterß bude uklßdat informace o nahrßvanΘm TGA obrßzku. Pointer imageData bude ukazovat na data, ze kter²ch vytvo°φme obrßzek. Bpp oznaΦuje barevnou hloubku (bits per pixel), kterß m∙₧e b²t 24 nebo 32, podle p°φtomnosti alfa kanßlu. Width a height definuje rozm∞ry. Do texID vytvo°φme texturu. Celou strukturu nazveme TextureImage.

typedef struct// Struktura textury

{

GLubyte *imageData;// Data obrßzku

GLuint bpp;// Barevnß hloubka obrßzku

GLuint width;// èφ°ka obrßzku

GLuint height;// V²Üka obrßzku

GLuint texID;// Vytvo°enß textura

} TextureImage;// JmΘno struktury

V tomto programu budeme pou₧φvat pouze jednu texturu, tak₧e vytvo°φme pole textur o velikosti jedna.

TextureImage textures[1];// Jedna textura

Na °adu p°ichßzφ asi nejobtφ₧n∞jÜφ Φßst - nahrßvßnφ TGA obrßzku a jeho konvertovßnφ na texturu. Musφm jeÜt∞ poznamenat, ₧e k≤d nßsledujφcφ funkce umo₧≥uje loadovat bu∩ 24 nebo 32 bitovΘ nekomprimovanΘ TGA soubory. Zabralo dost Φasu zprovoznit k≤d, kter² by pracoval s ob∞ma typy. Nikdy jsem ne°ekl, ₧e jsem gΘnius. Rßd bych poukßzal, ₧e ·pln∞ vÜechno nenφ z mΘ hlavy. Spoustu opravdu dobr²ch nßpad∙ jsem zφskal proΦφtßnφm internetu. Pokusil jsem se je zkombinovat do funkΦnφho k≤du, kter² pracuje s OpenGL. Nic snadnΘho, nic extrΘmn∞ slo₧itΘho!

Funkci p°edßvßme dva parametry. Prvnφ ukazuje do pam∞ti, kam ulo₧φme texturu. Druh² urΦuje diskovou cestu k souboru, kter² chceme nahrßt.

bool LoadTGA(TextureImage *texture, char *filename)// Do pam∞ti nahraje TGA soubor

{

Pole TGAheader[] definuje 12 byt∙. Porovnßme je s prvnφmi 12 bity, kterΘ naΦteme z TGA souboru - TGAcompare[], abychom se ujistili, ₧e je to opravdu Targa obrßzek a ne n∞jak² jin².

GLubyte TGAheader[12] = { 0,0,2,0,0,0,0,0,0,0,0,0 };// Nekomprimovanß TGA hlaviΦka

GLubyte TGAcompare[12];// Pro porovnßnφ TGA hlaviΦky

Header[] uklßdß prvnφch Üest D┘LEÄIT▌CH byt∙ z hlaviΦky souboru (Üφ°ka, v²Üka, barevnß hloubka).

GLubyte header[6];// Prvnφch 6 u₧iteΦn²ch byt∙ z hlaviΦky

Do bytesPerPixel p°i°adφme v²sledek operace, kdy vyd∞lφme barevnou hloubku v bitech osmi, abychom zφskali barevnou hloubku v bytech na pixel. ImageSize definuje poΦet byt∙, kterΘ jsou zapot°ebφ k vytvo°enφ obrßzku (Üφ°ka*v²Üka*barevnß hloubka).

GLuint bytesPerPixel;// PoΦet byt∙ na pixel pou₧it² v TGA souboru

GLuint imageSize;// Uklßdß velikost obrßzku p°i alokovßnφ RAM

Temp umo₧nφ prohodit byty dßle v programu. A koneΦn∞ poslednφ prom∞nnou pou₧ijeme ke zvolenφ sprßvnΘho parametru p°i vytvß°enφ textury. Bude zßviset na tom, zda je TGA 24 nebo 32 bitovß. V p°φpad∞ 24 bit∙ p°edßme GL_RGB a mßme-li 32 bitov² obrßzek pou₧ijeme GL_RGBA. Implicitn∞ p°edpoklßdßme, ₧e je obrßzek 32 bitov², tudφ₧ do type p°i°adφme GL_RGBA.

GLuint temp;// Pomocnß prom∞nnß

GLuint type = GL_RGBA;// Implicitnφm GL m≤dem je RGBA (32 BPP)

Pomocφ funkce fopen() otev°eme TGA soubor filename pro Φtenφ v binßrnφm m≤du (rb). Nßsleduje v∞tvenφ if, ve kterΘm d∞lßme hned n∞kolik v∞cφ najednou. Nejprve testujeme jestli soubor obsahuje data. Pokud tam ₧ßdnß nejsou, vrßtφme false. Obsahuje-li informace, p°eΦteme prvnφch dvanßct byt∙ do TGAcompare. Pou₧ijeme funkci fread(), kterß po jednom bytu naΦte ze souboru file dvanßct byt∙ (sizeof(TGAcompare)) a v²sledek ulo₧φ do TGAcompare. Vracφ poΦet p°eΦten²ch byt∙, kterΘ porovnßme se sizeof(TGAcompare). M∞lo by jich b²t, jak tuÜφte :-), dvanßct. Pokud jsme bez potφ₧φ doÜli a₧ tak daleko, porovnßme funkcφ memcmp() pole TGAheader a TGAcompare. Nebudou-li stejnΘ zav°eme soubor a vrßtφme false, proto₧e se nejednß o TGA obrßzek. Do header nakonec naΦteme dalÜφch Üest byt∙. P°i chyb∞ op∞t zav°eme soubor a funkci ukonΦφme.

FILE *file = fopen(filename, "rb");// Otev°e TGA soubor

if(file == NULL || // Existuje soubor?

fread(TGAcompare,1,sizeof(TGAcompare),file) != sizeof(TGAcompare) ||// Poda°ilo se naΦφst 12 byt∙?

memcmp(TGAheader,TGAcompare,sizeof(TGAheader)) != 0 ||// Majφ pot°ebnΘ hodnoty?

fread(header,1,sizeof(header),file) != sizeof(header))// Pokud ano, naΦte dalÜφch Üest byt∙

{

if (file == NULL)// Existuje soubor?

return false;// Konec funkce

else

{

fclose(file);// Zav°e soubor

return false;// Konec funkce

}

}

Pokud program proÜel k≤dem bez chyby mßme dost informacφ pro definovßnφ n∞kter²ch prom∞nn²ch. Prvnφ bude Üφ°ka obrßzku. ProblΘm spoΦφvß v tom, ₧e toto Φφslo je rozd∞leno do dvou byt∙. Ni₧Üφ byte m∙₧e nab²vat 256 hodnot (8 bit∙), tak₧e vynßsobφme vyÜÜφ byte 256 a k n∞mu p°iΦteme ni₧Üφ. Zφskali jsme Üφ°ku obrßzku. Stejn²m postupem dostaneme i v²Üku, akorßt pou₧ijeme jinΘ indexy v poli.

texture->width = header[1] * 256 + header[0];// Zφskß Üφ°ku obrßzku

texture->height = header[3] * 256 + header[2];// Zφskß v²Üku obrßzku

Zkontrolujeme jestli je Üφ°ka i v²Üka v∞tÜφ ne₧ nula. Pokud ne zav°eme soubor a vrßtφme false. Zßrove≥ zkontrolujeme i barevnou hloubku, kterou hledßme v header[4]. Musφ b²t bu∩ 24 nebo 32 bitovß.

if(texture->width <= 0 ||// Platnß Üφ°ka?

texture->height <= 0 ||// Platnß v²Üka?

(header[4] != 24 && header[4] != 32))// Platnß barevnß hloubka?

{

fclose(file);// Zav°e soubor

return false;// Konec funkce

}

SpoΦφtali a zkontrolovali jsme Üφ°ku a v²Üku, m∙₧eme p°ejφt k barevnΘ hloubce v bitech a bytech a velikosti pam∞ti pot°ebnΘ k ulo₧enφ dat obrßzku. U₧ vφme, ₧e v header[4] je barevnß hloubka v bitech na pixel. P°i°adφme ji do bpp. Jeden byte se sklßdß z 8 bit∙. Z toho plyne, ₧e barevnou hloubku v bytech zφskßme d∞lenφm bpp osmi. Velikost dat obrßzku zφskßme vynßsobenφm Üφ°ky, v²Üky a byt∙ na pixel.

texture->bpp = header[4];// Bity na pixel (24 nebo 32)

bytesPerPixel = texture->bpp / 8;// Byty na pixel

imageSize = texture->width * texture->height * bytesPerPixel;// Velikost pam∞ti pro data obrßzku

Pot°ebujeme alokovat pam∞¥ pro data obrßzku. Funkci malloc() p°edßme po₧adovanou velikost. M∞la by vrßtit ukazatel na zabranΘ mφsto v RAM. Nßsledujφcφ if mß op∞t n∞kolik ·loh. V prvΘ °ad∞ testuje sprßvnost alokace. Pokud p°i nφ n∞co nevyÜlo, ukazatel mß hodnotu NULL. V takovΘm p°φpad∞ zav°eme soubor a vrßtφme false. NicmΘn∞ pokud se alokace poda°ila, tak pomocφ fread() naΦteme data obrßzku a ulo₧φme je do prßv∞ alokovanΘ pam∞ti. Pokud se data nepoda°φ zkopφrovat, uvolnφme pam∞¥, zav°eme soubor a ukonΦφme funkci.

texture->imageData = (GLubyte *)malloc(imageSize);// Alokace pam∞ti pro data obrßzku

if(texture->imageData == NULL ||// Poda°ilo se pam∞¥ alokovat?

fread(texture->imageData, 1, imageSize, file) != imageSize)// Poda°ilo se kopφrovßnφ dat?

{

if(texture->imageData != NULL)// Byla data nahrßna?

free(texture->imageData);// Uvolnφ pam∞¥

fclose(file);// Zav°e soubor

return false;// Konec funkce

}

Pokud se a₧ dote∩ nestalo nic, Φφm bychom ukonΦovali funkci, mßme vyhrßno. Stojφ p°ed nßmi, ale jeÜt∞ jeden ·kol. Formßt TGA specifikuje po°adφ barevn²ch slo₧ek BGR (modrß, zelenß, Φervenß) narozdφl od OpenGL, kterΘ pou₧φvß RGB. Pokud bychom neprohodili Φervenou a modrou slo₧ku, tak vÜechno, co mß b²t v obrßzku modrΘ by bylo ΦervenΘ a naopak. Deklarujeme cyklus, jeho₧ °φdφcφ prom∞nnß i nab²vß hodnot od nuly do velikosti obrßzky. Ka₧d²m pr∙chodem se zv∞tÜuje o 3 nebo o 4 v zßvislosti na barevnΘ hloubce. (24/8=3, 32/8=4). Uvnit° cyklu prohodφme R a B slo₧ky. Modrß je na indexu i a Φervenß i+2. Modrß by byla na i+1, ale s tou nic ned∞lßme, proto₧e je umφst∞nß sprßvn∞.

for(GLuint i=0; i < int(imageSize); i += bytesPerPixel)// Prochßzφ data obrßzku

{

temp = texture->imageData[i];// B ulo₧φme do pomocnΘ prom∞nnΘ

texture->imageData[i] = texture->imageData[i + 2];// R je na sprßvnΘm mφst∞

texture->imageData[i + 2] = temp;// B je na sprßvnΘm mφst∞

}

Po tΘto operaci mßme v pam∞ti ulo₧en obrßzek TGA ve formßtu, kter² podporuje OpenGL. Nic nßm nebrßnφ, abychom zav°eli soubor. U₧ ho k niΦemu nepot°ebujeme.

fclose(file);// Zav°e soubor

M∙₧eme zaΦφt vytvß°et texturu. Tento postup je v principu ·pln∞ stejn², jako ten, kter² jsme pou₧φvali v minul²ch tutorißlech. Po₧ßdßme OpenGL o vygenerovßnφ jednΘ textury na adrese texture[0].textID, kterou jsme zφskali p°edßnφm parametru ve funkci InitGL(). Pokud bychom cht∞li vytvo°it druhou texturu z jinΘho obrßzku TGA, tak se tato funkci v∙bec nezm∞nφ. V InitGL() bychom provedli volßnφ dvakrßt, ale s jin²mi parametry. Programujeme obecn∞ji...

glGenTextures(1, &texture[0].texID);// Generuje texturu

Zvolφme prßv∞ vytvß°enou texturu za aktußlnφ a nastavφme jφ lineßrnφ filtrovßnφ pro zmenÜenφ i zv∞tÜenφ.

glBindTexture(GL_TEXTURE_2D, texture[0].texID);// Zvolφ texturu

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);// Lineßrnφ filtrovßnφ

glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);// Lineßrnφ filtrovßnφ

Zkontrolujeme, jestli je textura 24 nebo 32 bitovß. V prvnφm p°φpad∞ nastavφme type na GL_RGB (bez alfa kanßlu), jinak ponechßme implicitnφ hodnotu GL_RGBA (s alfa kanßlem). Pokud bychom test neprovedli, program by se s nejv∞tÜφ pravd∞podobnostφ zhroutil.

if (texture[0].bpp == 24)// Je obrßzek 24 bitov²?

{

type = GL_RGB;// Nastavφ typ na GL_RGB

}

Te∩ koneΦn∞ sestavφme texturu. Jako obvykle, tak i tentokrßt, pou₧ijeme funkci glTexImage2D(). Mφsto ruΦnφho zadßnφ typu textury (GL_RGB, GL_RGBA) p°edßme hodnotu pomocφ prom∞nnΘ. JednoduÜe °eΦeno: Program sßm detekuje, co mß p°edat.

glTexImage2D(GL_TEXTURE_2D, 0, type, texture[0].width, texture[0].height, 0, type, GL_UNSIGNED_BYTE, texture[0].imageData);// Vytvo°φ texturu

return true;// VÜechno je v po°ßdku

}

ReSizeGLScene() nastavuje pravo·hlou projekci. Sou°adnice [0; 1] jsou lev²m hornφm rohem okna a [640; 480] prav²m dolnφm. Dostßvßme rozliÜenφ 640x480. Na zaΦßtku nastavφme globßlnφ prom∞nnΘ swidth a sheight na aktußlnφ rozm∞ry okna. P°i ka₧dΘm p°esunutφ nebo zm∞n∞ velikosti okna se aktualizujφ. Ostatnφ k≤d znßte.

GLvoid ReSizeGLScene(GLsizei width, GLsizei height)// Zm∞na velikosti a inicializace OpenGL okna

{

swidth = width;// èφ°ka okna

sheight = height;// V²Üka okna

if (height == 0)// ZabezpeΦenφ proti d∞lenφ nulou

{

height = 1;// Nastavφ v²Üku na jedna

}

glViewport(0,0,width,height);// Resetuje aktußlnφ nastavenφ

glMatrixMode(GL_PROJECTION);// Zvolφ projekΦnφ matici

glLoadIdentity();// Reset matice

glOrtho(0.0f,640,480,0.0f,-1.0f,1.0f);// Pravo·hlß projekce 640x480, [0; 0] vlevo naho°e

glMatrixMode(GL_MODELVIEW);// Zvolφ matici Modelview

glLoadIdentity();// Reset matice

}

Inicializace OpenGL se minimalizovala. Z∙stala z nφ jenom kostra. Nahrajeme TGA obrßzek a vytvo°φme z n∞j texturu. V prvnφm parametru je urΦeno, kam ji ulo₧φme a v druhΘm diskovß cesta k obrßzku. Vrßtφ-li funkce z jakΘhokoli d∙vodu false, inicializace se p°eruÜφ, program zobrazφ chybovou zprßvu a ukonΦφ se. Pokud byste cht∞li nahrßt druhou nebo i dalÜφ textury pou₧ijte volßnφ n∞kolik. Podmφnka se logicky ORuje.

int InitGL(GLvoid)// Nastavenφ OpenGL

{

if (!LoadTGA(&textures[0], "Data/Font.TGA"))// Nahraje texturu fontu z TGA obrßzku

{

return false;// P°i chyb∞ ukonΦφ program

}

Po ·sp∞ÜnΘm nahrßnφ textury vytvo°φme font. Je d∙le₧itΘ upozornit, ₧e se BuildFont() musφ volat a₧ po funkci LoadTGA(), proto₧e pou₧φvß jφ vytvo°enou texturu. Dßle nastavφme vyhlazenΘ stφnovßnφ, ΦernΘ pozadφ, povolφme mazßnφ depth bufferu a zvolφme texturu fontu.

BuildFont();// Sestavφ font

glShadeModel(GL_SMOOTH);// VyhlazenΘ stφnovßnφ

glClearColor(0.0f, 0.0f, 0.0f, 0.5f);// ╚ernΘ pozadφ

glClearDepth(1.0f);// Nastavenφ hloubkovΘho bufferu

glBindTexture(GL_TEXTURE_2D, textures[0].texID);// Zvolφ texturu

return TRUE;// Inicializace v po°ßdku

}

P°ejdeme k vykreslovßnφ. ZaΦneme deklarovßnφm prom∞nn²ch. O ukazateli token zatφm jen tolik, ₧e bude uklßdat °et∞zec jednoho podporovanΘho rozÜφ°enφ a cnt je pro zjiÜt∞nφ jeho po°adφ.

int DrawGLScene(GLvoid)// Vykreslovßnφ

{

char* token;// Uklßdß jedno rozÜφ°enφ

int cnt = 0;// ╚φtaΦ rozÜφ°enφ

Sma₧eme obrazovku a hloubkov² buffer. Potom nastavφme barvu na st°edn∞ tmav∞ Φervenou a do hornφ Φßsti okna vypφÜeme slova Renderer (jmΘno grafickΘ karty), Vendor (jejφ v²robce) a Version (verze). D∙vod, proΦ nejsou vÜechny umφst∞ny 50 pixel∙ od okraje na ose x, je ten, ₧e je nezarovnßvßme doleva, ale doprava.

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Sma₧e obrazovku a hloubkov² buffer

glColor3f(1.0f,0.5f,0.5f);// ╚ervenß barva

glPrint(50,16,1,"Renderer");// V²pis nadpisu pro grafickou kartu

glPrint(80,48,1,"Vendor");// V²pis nadpisu pro v²robce

glPrint(66,80,1,"Version");// V²pis nadpisu pro verzi

Zm∞nφme Φervenou barvu na oran₧ovou a nagrabujeme informace z grafickΘ karty. Pou₧ijeme funkci glGetString(), kterß vrßtφ po₧adovanΘ °et∞zce. Kv∙li glPrint() p°etypujeme v²stup funkce na char*. V²sledek vypφÜeme doprava od nadpis∙.

glColor3f(1.0f,0.7f,0.4f);// Oran₧ovß barva

glPrint(200,16,1,(char *)glGetString(GL_RENDERER));// V²pis typu grafickΘ karty

glPrint(200,48,1,(char *)glGetString(GL_VENDOR));// V²pis v²robce

glPrint(200,80,1,(char *)glGetString(GL_VERSION));// V²pis verze

Definujeme modrou barvu a dol∙ na scΘnu vypφÜeme NeHe Productions.

glColor3f(0.5f,0.5f,1.0f);// Modrß barva

glPrint(192,432,1,"NeHe Productions");// V²pis NeHe Productions

Kolem prßv∞ vypsanΘho textu vykreslφme bφl² rßmeΦek. Resetujeme matici, proto₧e v glPrint() se volajφ funkce, kterΘ ji m∞nφ. Potom definujeme bφlou barvu.

glLoadIdentity();// Reset matice

glColor3f(1.0f,1.0f,1.0f);// Bφlß barva

Vykreslovßnφ linek pomocφ GL_LINE_STRIP je velmi jednoduchΘ. Prvnφ bod definujeme ·pln∞ vpravo, 63 pixel∙ (480-417=63) nad spodnφm okrajem okna. Druh² vertex umφstφme ve stejnΘ v²Üce, ale vlevo. OpenGL je spojφ p°φmkou. T°etφ bod posuneme dol∙ do levΘho dolnφho rohu. OpenGL op∞t zobrazφ linku, tentokrßt mezi druh²m a t°etφm bodem. ╚tvrt² bod pat°φ do pravΘho dolnφho rohu a k pßtΘmu projedeme v²chozφm vertexem nahoru. UkonΦφme triangle strip, abychom mohli zaΦφt vykreslovat z novΘ pozice a stejn²m zp∙sobem vykreslφme druhou Φßst rßmeΦku, ale tentokrßt naho°e.

Asi jste pochopili, ₧e pokud vykreslujeme vφce na sebe navazujφcφch p°φmek, tak LINE_STRIP uÜet°φ spoustu zbyteΦnΘho k≤du, kter² vznikß opakovan²m definovßnφm vertex∙ p°i obyΦejnΘm GL_LINES.

Po°adφ zadßvßnφ bod∙

glBegin(GL_LINE_STRIP);// ZaΦßtek kreslenφ linek

glVertex2d(639,417);// 1

glVertex2d(0,417);// 2

glVertex2d(0,480);// 3

glVertex2d(639,480);// 4

glVertex2d(639,128);// 5

glEnd();// Konec kreslenφ

glBegin(GL_LINE_STRIP);// ZaΦßtek kreslenφ linek

glVertex2d(0,128);// 6

glVertex2d(639,128);// 7

glVertex2d(639,1);// 8

glVertex2d(0,1);// 9

glVertex2d(0,417);// 10

glEnd();// Konec kreslenφ

Nßm neznßmß funkce glScissor(x, y, v, Ü) vytvß°φ n∞co, co by se dalo popsat jako okno. Pokud zapneme GL_SCISSOR_TEST, bude se o°ezßvat okolφ tΘto Φßsti obrazovky, tudφ₧ se objekty budou moci vykreslovat pouze uvnit° definovanΘho obdΘlnφku. UrΦφme ho parametry p°edan²mi funkci. V naÜem p°φpad∞ je to prvnφ pixel na ose x ve v²Üce 13,5% (0,135...f) od spodnφho okraje. dßle bude 638 pixel∙ Üirok² (swidth-2) a 59,7% (0,597...f) v²Üky okna vysok². Druh²m °ßdkem povolφme o°ezßvacφ testy. M∙₧ete se pokusit vykreslit obrovsk² obdΘlnφk p°es celΘ okno, ale uvidφte pouze Φßst v neo°ezanΘ oblasti. zbytek dosud nakreslenΘ scΘny z∙stane nezm∞n∞n. Perfektnφ p°φkaz!

glScissor(1, int(0.135416f*sheight), swidth-2, int(0.597916f*sheight));// Definovßnφ o°ezßvacφ oblasti

glEnable(GL_SCISSOR_TEST);// Povolφ o°ezßvacφ testy

Na °adu p°ichßzφ asi nejt∞₧Üφ Φßst tΘto lekce - vypsßnφ podporovan²ch OpenGL rozÜφ°enφ. V prvnφ fßzi je musφme zφskat. Pomocφ funkce malloc() alokujeme buffer pro °et∞zec znak∙ text. P°edßvß se jφ velikost po₧adovanΘ pam∞ti. Strlen() spoΦφtß poΦet znak∙ °et∞zce vrßcenΘho glGetString(GL_EXTENSIONS). P°iΦteme k n∞mu jeÜt∞ jeden znak pro '\0', kter² uzavφrß ka₧d² c-ΘΦkovsk² °et∞zec. Strcpy() zkopφruje °et∞zec podporovan²ch rozÜφ°enφ do prom∞nnΘ text.

char* text = (char *)malloc(strlen((char *)glGetString(GL_EXTENSIONS))+1);// Alokace pam∞ti pro °et∞zec

strcpy(text,(char *)glGetString(GL_EXTENSIONS));// Zkopφruje seznam rozÜφ°enφ do text

Nynφ jsme do text nagrabovali z grafickΘ karty °et∞zec, kter² vypadß n∞jak takto: "GL_ARB_multitexture GL_EXT_abgr GL_EXT_bgra". Pomocφ strtok() z n∞j vyjmeme v po°adφ prvnφ rozÜφ°enφ. Funkce pracuje tak, ₧e prochßzφ °et∞zec a v p°φpad∞, ₧e najde mezeru zkopφruje p°φsluÜnou Φßst z text do token. Prvnφ hodnota token tedy bude "GL_ARB_multitexture". Zßrove≥ se vÜak zm∞nφ i text. Prvnφ mezera se nahradφ odd∞lovaΦem. Vφce dßle.

token = strtok(text, " ");// Zφskß prvnφ pod°et∞zec

Vytvo°φme cyklus, kter² se zastavφ tehdy, kdy₧ v token nezbudou u₧ ₧ßdnΘ dalÜφ informace - bude se rovnat NULL. Ka₧d²m pr∙chodem inkrementujeme ΦφtaΦ a zkontrolujeme, jestli je jeho hodnota v∞tÜφ ne₧ maxtokens. Touto cestou velice snadno zφskßme maximßlnφ hodnotu v ΦφtaΦi, kterou vyu₧ijeme p°i rolovßnφ po stisku klßves.

while(token != NULL)// Prochßzφ podporovanß rozÜφ°enφ

{

cnt++;// Inkrementuje ΦφtaΦ

if (cnt > maxtokens)// Je maximum menÜφ ne₧ hodnota ΦφtaΦe?

{

maxtokens = cnt;// Aktualizace maxima

}

V tΘto chvφli mßme v token ulo₧enΘ prvnφ rozÜφ°enφ. Jeho po°adovΘ Φφslo napφÜeme zelen∞ do levΘ Φßsti okna. VÜimn∞te si, ₧e ho na ose x napφÜeme na sou°adnici 0. Tφm bychom mohli zlikvidovat lev² (bφl²) rßmeΦek, kter² jsme u₧ vykreslili, ale proto₧e mßme zapnutΘ o°ezßvßnφ, pixely na nule nebudou modifikovßny. Na ose y zaΦφnßme kreslit na 96. Abychom nevykreslovali vÜechno na sebe, p°iΦφtßme po°adφ nßsobenΘ v²Ükou textu (cnt*32). P°i vypisovßnφ prvnφho rozÜφ°enφ se cnt==1 a text se nakreslφ na 96+(32*1)=128. U druhΘho je v²sledkem 160. TakΘ odeΦφtßme scroll. Implicitn∞ se rovnß nule, ale po stisku Üipek se jeho hodnota m∞nφ. Umo₧nφme tφm rolovßnφ o°ezanΘho okna, do kterΘho se vejde celkem dev∞t °ßdek (v²Üka okna/v²Üka textu = 288/32 = 9). Zm∞nou scrollu m∙₧eme zm∞nit offset textu a tφm ho posunout nahoru nebo dol∙. Efekt je podobn² filmovΘmu projektoru. Film roluje tak, aby v jednom okam₧iku byl vid∞t v₧dy jen jeden frame. Nem∙₧ete vid∞t oblast nad nebo pod nφm i kdy₧ mßte v∞tÜφ plßtno. Objektiv sehrßvß stejnou roli jako o°ezßvacφ testy.

glColor3f(0.5f,1.0f,0.5f);// Zelenß barva

glPrint(0, 96+(cnt*32)-scroll, 0, "%i", cnt);// Po°adφ aktußlnφho rozÜφ°enφ

Po vykreslenφ po°adovΘho Φφsla zam∞nφme zelenou barvu za ₧lutou a koneΦn∞ vypφÜeme text ulo₧en² v prom∞nnΘ token. Vlevo se zaΦne na padesßtΘm pixelu. Pou₧ijeme-li p°φklad v²Üe, budou vypadat prvnφ °ßdky takto:

1 GL_ARB_multitexture
2 GL_EXT_abgr
3 GL_EXT_bgra

glColor3f(1.0f,1.0f,0.5f);// Älutß barva

glPrint(50,96+(cnt*32)-scroll,0,token);// VypφÜe jedno rozÜφ°enφ

Po zobrazenφ prvnφho rozÜφ°enφ pot°ebujeme p°ipravit p∙du pro dalÜφ pr∙chod cyklem. Nejprve zjistφme, jestli je v text jeÜt∞ n∞jakΘ dalÜφ rozÜφ°enφ. Namφsto op∞tovnΘho volßnφ token = strtok(text, " "), napφÜeme token = strtok(NULL, " "); NULL urΦuje, ₧e se mß hledat DALè═ pod°et∞zec a ne vÜechno provßd∞t od znova. V naÜem p°φklad∞ jsem v²Üe napsal, ₧e se mezera nahradφ odd∞lovaΦem - "GL_ARB_multitextureodd∞lovaΦGL_EXT_abgr GL_EXT_bgra". Najdeme tedy odd∞lovaΦ a a₧ od n∞j se bude hledat dalÜφ mezera. PotΘ se do token zkopφruje pod°et∞zec mezi odd∞lovaΦem a mezerou (GL_EXT_abgr) a text bude modifikovßn na "GL_ARB_multitextureodd∞lovaΦGL_EXT_abgrodd∞lovaΦGL_EXT_bgra". Po dosa₧enφ konce textu se token nastavφ na NULL a cyklus se ukonΦφ.

token = strtok(NULL, " ");// Najde dalÜφ rozÜφ°enφ

}

Tφm jsme ukonΦili vykreslovßnφ, ale jeÜt∞ nßm zb²vß po sob∞ uklidit. Vypneme o°ezßvacφ testy a uvolnφme dynamickou pam∞¥ - informace zφskanΘ pomocφ glGetString(GL_EXTENSIONS) ulo₧enΘ v RAM. P°φÜt∞ a₧ budeme volat DrawGLScene() se pam∞¥ op∞t alokuje a provedou se znovu vÜechny rozbory °et∞zc∙.

glDisable(GL_SCISSOR_TEST);// Vypne o°ezßvacφ testy

free(text);// Uvolnφ dynamickou pam∞¥

P°φkaz glFlush() nenφ bezpodmφneΦn∞ nutn², ale myslφm, ₧e je dobr² nßpad se o n∞m zmφnit. NejjednoduÜÜφ vysv∞tlenφ je takovΘ, ₧e oznßmφ OpenGL, aby dokonΦilo, co prßv∞ d∞lß (n∞kterΘ grafickΘ karty nap°. pou₧φvajφ vyrovnßvacφ pam∞ti, jejich₧ obsah se tφmto poÜle na v²stup). Pokud si n∞kdy vÜimnete mihotßnφ nebo blikßnφ polygon∙, zkuste p°idat na konec vÜeho vykreslovßnφ volßnφ glFlush(). Vyprßzdnφ renderovacφ pipeline a tφm zamezφ mihotßnφ, kterΘ vznikß tehdy, kdy₧ program nemß dostatek Φasu, aby dokonΦil rendering.

glFlush();// Vyprßzdnφ renderovacφ pipeline

return TRUE;// VÜechno v po°ßdku

}

Na konec KillGLWindow() p°idßme volßnφ KillFont, kterΘ sma₧e display listy fontu.

// Konec KillGLWindow()

KillFont();// Sma₧e font

}

V programu testujeme stisk Üipky nahoru a dol∙. V obou p°φpadech p°iΦteme nebo odeΦteme od scroll dvojku, ale pouze tehdy, pokud bychom nerolovali mimo okno. U Üipky nahoru je situace jednoduchß - nula je v₧dy nejni₧Üφ mo₧nΘ rolovßnφ. Maximum u Üipky dol∙ zφskßme nßsobenφm v²Üky °ßdku a poΦtu rozÜφ°enφ. Devφtku odeΦφtßme, proto₧e se v jednom okam₧iku vejde na scΘnu dev∞t °ßdk∙.

// Funkce WinMain()

if (keys[VK_UP] && (scroll > 0))// èipka nahoru?

{

scroll -= 2;// Posune text nahoru

}

if (keys[VK_DOWN] && (scroll < 32*(maxtokens-9)))// èipka dol∙?

{

scroll += 2;// Posune text dol∙

}

Doufßm, ₧e byl pro vßs tento tutorißl zajφmav². Ji₧ vφte, jak zφskat informace o v²robci, jmΘnu a verzi grafickΘ karty a takΘ, kterß OpenGL rozÜφ°enφ podporuje. M∞li byste v∞d∞t, jak pou₧φt o°ezßvacφ testy a nemΘn∞ d∙le₧itou v∞cφ je nahrßvßnφ TGA mφsto bitmapov²ch obrßzk∙ a jejich konverze na textury.

napsal: Jeff Molofee - NeHe
p°elo₧il: Michal Turek - Woq

ZdrojovΘ k≤dy

Lekce 24

<<< Lekce 23 | Lekce 25 >>>