Lekce 13

Lekce 13 - BitmapovΘ fonty

╚asto kladenß otßzka t²kajφcφ se OpenGL znφ: "Jak zobrazit text?". V₧dycky jde namapovat texturu textu. Bohu₧el nad nφm mßte velmi malou kontrolu. A pokud nejste dob°φ v blendingu, v∞tÜinou skonΦφte smixovßnφm s ostatnφmi obrßzky. Pokud byste cht∞li znßt lehΦφ cestu k v²stupu textu na jakΘkoli mφsto s libovolnou barvou nebo fontem, potom je tato lekce urΦit∞ pro vßs. BitmapovΘ fonty jsou 2D pφsma, kterΘ nemohou b²t rotovßny. V₧dy je uvidφte zep°edu.

Mo₧nß si °eknete co je tak t∞₧kΘho na v²stupu textu. M∙₧ete spustit grafick² editor, vepsat text do obrßzku, nahrßt ho jako texturu, zapnout blending a potΘ namapovat na polygon. Ale tφm uberete Φas procesoru. V zßvislosti na typu filtrovßnφ m∙₧e v²sledek vypadat rozmazan∞ nebo jako posklßdan² z kostiΦek. Pokud by m∞l alfa kanßl, skonΦφ smφchan² s objekty na obrazovce. Jist∞ vφte kolik r∙zn²ch font∙ je dostupn²ch v systΘmu. V tomto tutorißlu se nauΦφte jak je pou₧φvat. Nejen, ₧e bitmapovΘ fonty vypadajφ stokrßt lΘpe ne₧ text na textu°e, ale m∙₧ete je jednoduÜe m∞nit za b∞hu programu. Nenφ t°eba d∞lat texturu pro ka₧dΘ slovo nebo nßpis, kter² chcete vypsat. StaΦφ jen jeden p°φkaz. Sna₧il jsem se vytvo°it tuto funkci co nejjednoduÜÜφ. VÜechno co musφte ud∞lat je napsat glPrint("Hello, world!"). Podle dlouhΘho ·vodu m∙₧ete °φci, ₧e jsem s tφmto tutorißlem dost spokojen². Trvalo mi p°ibli₧n∞ hodinu a p∙l napsat tento program. ProΦ tak dlouho? Proto₧e ve skuteΦnosti nejsou dostupnΘ ₧ßdnΘ informace o pou₧φvßnφ bitmapov²ch font∙, pokud samoz°ejm∞ nemßte rßdi MFC. Abych udr₧el vÜe jednoduchΘ, rozhodl jsem se, ₧e by bylo p∞knΘ napsat jej v k pochopenφ jednoduchΘm C k≤du.

Malß poznßmka: Tento k≤d je specifick² pro Windows. Pou₧φvß wgl funkce Windows pro vytvo°enφ fontu. Apple mß pravd∞podobn∞ agl podporu, kterß by m∞la d∞lat tu samou v∞c a X mß glx. NaneÜt∞stφ nemohu zaruΦit, ₧e tento k≤d je p°enositeln². Pokud mß n∞kdo na platform∞ nezßvisl² k≤d pro kreslenφ font∙ na obrazovku, poÜlete mi jej a jß napφÜi jin² tutorißl o fontech:

ZaΦneme typick²m k≤dem z lekce 1. P°idßme hlaviΦkov² soubor stdio.h pro vstupn∞ v²stupnφ operace, stdarg.h pro rozbor textu a konvertovßnφ prom∞nn²ch do textu a koneΦn∞ math.h, tak₧e m∙₧eme pohybovat textem po obrazovce s pou₧itφm funkcφ SIN a COS.

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

#include <math.h>// HlaviΦkov² soubor pro matematickou knihovnu

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

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

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

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

#include <gl\glaux.h>// HlaviΦkov² soubor pro Glaux 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 3 novΘ prom∞nnΘ. V base ulo₧φme Φφslo prvnφho vytvo°enΘho display listu. Ka₧d² znak bude pot°ebovat vlastnφ, tak₧e jich bude relativn∞ dost. Znaku 'A' p°i°adφme Φφslo 65, 'B' 66, 'C' 67 atd. Lehce usoudφte, ₧e 'A' bude ulo₧eno v base + 65 ('A' je 65 znak Ascii tabulky). Dßle p°idßme 2 ΦφtaΦe, kterΘ pou₧ijeme k pohybu textu po obrazovce s pou₧itφm sin∙ a kosin∙. Budou slou₧it i ke generovßnφ barvy znak∙ (vφce dßle).

GLuint base;// ╚φslo zßkladnφho display listu znak∙

GLfloat cnt1;// Pro pohyb a barvu textu

GLfloat cnt2;// Pro pohyb a barvu textu

Nßsledujφcφ funkce vytvo°φ font - asi nejt∞₧Üφ Φßst. "HFONT font" °ekne Windows, ₧e budeme manipulovat s fonty Windows. Vytvo°enφm 96 display list∙ definujeme base. Po skonΦenφ tΘto operace v nφ bude ulo₧eno Φφslo prvnφho listu.

GLvoid BuildFont(GLvoid)// Vytvo°enφ fontu

{

HFONT font;// Prom∞nnß fontu

base = glGenLists(96);// 96 znak∙

Vytvo°φme font. Prvnφ parametr specifikuje velikost. VÜimn∞te si, ₧e je to zßpornΘ Φφslo. Vlo₧enφm znamΘnka mφnus °ekneme Windows, aby naÜlo pφsmo podle v²Üky ZNAKU. Pokud bychom pou₧ili kladnΘ, hledalo by se podle v²Üky BU╥KY.

font = CreateFont(-24,// V²Üka

UrΦφme Üφ°ku bu≥ky. Zadßnφm nuly Windows pou₧ije implicitnφ hodnotu. KonkrΘtnφ hodnotou vytvo°φme font ÜirÜφ.

0,// èφ°ka

┌hel escapement natoΦφ font. Nenφ to zrovna nejlepÜφ vlastnost. Kdybyste nepou₧ili 0, 90, 180 nebo 270 stup≥∙, font by se pravd∞podobn∞ o°ezal rßmeΦkem.

0,// ┌hel escapement

0,// ┌hel orientace

TuΦnost fontu je u₧iteΦn² parametr. Lze pou₧φt Φφsla 0 a₧ 1000 nebo n∞kterou z p°eddefinovan²ch hodnot. FW_DONTCARE (0), FW_NORMAL (400), FW_BOLD (700) a FW_BLACK (900). Je jich samoz°ejm∞ vφce, ale myslφm si, ₧e tyto Φty°i bohat∞ staΦφ (pop°. pou₧ijte nßpov∞du MSDN). ╚φm v∞tÜφ hodnotu pou₧ijete, tφm bude tuΦn∞jÜφ.

FW_BOLD,// TuΦnost

FALSE,// Kurzφva

FALSE,// Podtr₧enφ

FALSE,// P°eÜkrtnutφ

Znakovß sada popisuje typ znak∙, kterΘ chcete pou₧φt. Nap°. CHINESEBIG5_CHARSET, GREEK_CHARSET, RUSSIAN_CHARSET, DEFAULT_CHARSET atd. ANSI je jedinß, kterou pou₧φvßm, nicmΘn∞ DEFAULT by koneckonc∙ mohlo pracovat takΘ. (Pokud rßdi pou₧φvßte fonty Webdings nebo Wingdings pou₧ijte SYMBOL_CHARSET.).

ANSI_CHARSET,// Znakovß sada

P°esnost v²stupu °φkß Windows jakou znakovou sadu pou₧φt, majφ-li dv∞ stejnß jmΘna. Je-li vφce mo₧n²ch font∙ OUT_TT_PRECIS vybere TRUETYPE verzi, kterß vypadß mnohem lΘpe - p°edevÜφm, kdy₧ se zv∞tÜφ. Zadat m∙₧ete takΘ OUT_TT_ONLY_PRECIS, kterß v₧dy pou₧ije TrueType font.

OUT_TT_PRECIS,// P°esnost v²stupu (TrueType)

P°esnost o°ezßnφ je typ o°ezßnφ, kter² se pou₧ije, kdy₧ se font dostane ven z o°ezßvacφho regionu.

CLIP_DEFAULT_PRECIS,// P°esnost o°ezßnφ

Do v²stupnφ kvality m∙₧ete zadat PROOF, DRAFT, NONANTIALIASED, DEFAULT nebo ANTIALIASED (mΘn∞ hranatΘ).

ANTIALIASED_QUALITY,// V²stupnφ kvalita

Nastavφme rodinu a pitch. Do pitch lze zadat DEFAULT_PITCH, FIXED_PITCH a VARIABLE_PITCH. Do rodiny FF_DECORATIVE, FF_MODERN, FF_ROMAN, FF_SCRIPT, FF_SWISS, FF_DONTCARE. Zkuste si s nimi pohrßt.

FF_DONTCARE | DEFAULT_PITCH,// Rodina a pitch

Nakonec zadßme jmΘno fontu. Spus¥te MS Word nebo jin² textov² editor a najd∞te si jmΘno pφsma, kterΘ se vßm lφbφ.

"Courier New");// JmΘno fontu

Vybereme font do DC (device context - kontext za°φzenφ) a vytvo°φme 96 display list∙ poΦφnajφce 32 (v Ascii tabulce jsou p°ed 32 netiÜtitelnΘ znaky, 32 - mezera). M∙₧ete sestavit vÜech 256 zadßnφm Φφsla 256 do glGenList() (v²Üe - na zaΦßtku tΘto funkce). Ujist∞te se, ₧e sma₧ete vÜech 256 list∙ po skonΦenφ programu (funkce KillFont(GLvoid)) a samoz°ejm∞ musφte napsat v nßsledujφcφm p°φkazu mφsto 32 -> 0 a mφsto 96 -> 255 (viz. dalÜφ lekce o fontech).

SelectObject(hDC, font);// V²b∞r fontu do DC

wglUseFontBitmaps(hDC, 32, 96, base);// Vytvo°φ 96 znak∙, poΦφnaje 32 v Ascii

}

Nßsledujφcφ k≤d je krßsn∞ jednoduch². Sma₧e 96 vytvo°en²ch display list∙ z pam∞ti poΦφnaje prvnφm, kter² je urΦen v "base". Nejsem si jist², jestli by to Windows ud∞lali automaticky. Jeden °ßdek za jistotu stojφ.

GLvoid KillFont(GLvoid)// Sma₧e font

{

glDeleteLists(base, 96);// Sma₧e vÜech 96 znak∙ (display list∙)

}

A te∩ p°ichßzφ na °adu funkce, kv∙li nφ₧ je napsßna tato lekce. Volß se ·pln∞ stejn∞ jako klasickß printf("Hello, world!"); s tφm rozdφlem, ₧e na zaΦßtek p°idßte gl a p°ed zßvorkou uberete f :-]

GLvoid glPrint(const char *fmt, ...)// Klon printf() pro OpenGL

{

Prvnφ °ßdek alokuje pam∞¥ pro 256 znak∙. Jak²si °et∞zec, kter² nakonec vypφÜeme na obrazovku. Druhou prom∞nnou tvo°φ ukazatel do argument∙ funkce, kter² jsme p°i volßnφ zadali s °et∞zcem k≤d tΘto lekce. ( printf("%d %d", i, j) - to znßte, ne?)

chartext[256];// Uklßdß °et∞zec

va_listap;// Pointer do argument∙ funkce

DalÜφ dva °ßdky zkouÜejφ, jestli byl zadßn text. Pokud ne fmt ukazuje na nic (NULL) a tudφ₧ se nic nevypφÜe.

if (fmt == NULL)// Byl p°edßn text?

return;// Konec

Nßsledujφcφ k≤d konvertuje veÜkerΘ symboly (%d, %f...) v °et∞zci na konkrΘtnφ hodnoty. Po ·prav∞ bude vÜe ulo₧eno v text.

va_start(ap, fmt);// Rozbor °et∞zce

vsprintf(text, fmt, ap);// Zam∞nφ symboly za konkrΘtnφ Φφsla

va_end(ap);// V²sledek je ulo₧en v text

P°φkaz glListBase(base-32) je na vysv∞tlenφ trochu obtφ₧n∞jÜφ. ╪ekn∞me, ₧e vykreslujeme znak 'A', kter² je reprezentovßn 65 (v Ascii). Bez glListBase(base-32) OpenGL nevφ, kde mß najφt tento znak. Mohlo by vyhledat display list 65, ale pokud by se base rovnalo 1000, tak by 'A' bylo ulo₧eno v display listu 1065. Tak₧e nastavenφm base na poΦßteΦnφ bod, OpenGL bude v∞d∞t, odkud vzφt ten sprßvn² display list. OdeΦφtßme 32, proto₧e jsme nevytvo°ili prvnφch 32 display list∙, tudφ₧ je p°eskoΦφme.

glPushAttrib(GL_LIST_BIT);// Ulo₧φ souΦasn² stav display list∙

glListBase(base - 32);// Nastavφ zßkladnφ znak na 32

Zavolßme funkci glCallLists(), kterß najednou zobrazuje vφce display list∙. strlen(text) vrßtφ poΦet znak∙ v °et∞zci a tφm i poΦet k zobrazenφ. Dßle pot°ebujeme znßt typ p°edßvanΘho parametru (poslednφ). Ani te∩ nebudeme vklßdat vφce ne₧ 256 znak∙, tak₧e pou₧ijeme GL_UNSIGNED_BYTE (byte m∙₧e nab²vat hodnot 0-255, co₧ je p°esn∞ to, co pot°ebujeme). V poslednφm parametru p°edßme text. Ka₧d² display list vφ, kde je pravß hrana toho p°edchozφho, Φφm₧ zamezφme nakupenφ znak∙ na sebe, na jedno mφsto. P°ed zaΦßtkem kreslenφ nßsledujφcφ znaku se p°esune o tuto hodnotu doprava (glTranslatef()). Nakonec nastavφme GL_LIST_BIT zp∞t na hodnotu majφcφ p°ed volßnφm glListBase().

glCallLists(strlen(text), GL_UNSIGNED_BYTE, text);// Vykreslφ display listy

glPopAttrib();// Obnovφ p∙vodnφ stav display list∙

}

Jedinß zm∞na v inicializaΦnφm k≤du je p°φkaz volajφcφ BuildFont().

int InitGL(GLvoid)// VÜechna nastavenφ OpenGL

{

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

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

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

glEnable(GL_DEPTH_TEST);// Povolφ hloubkovΘ testovßnφ

glDepthFunc(GL_LEQUAL);// Typ hloubkovΘho testovßnφ

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// NejlepÜφ perspektivnφ korekce

BuildFont();// Vytvo°φ font

return TRUE;

}

P°ejdeme k vykreslovßnφ. Po obvykl²ch inicializacφch se p°esuneme o 1 jednotku do obrazovky. BitmapovΘ fonty pracujφ lΘpe p°i pou₧itφ kolmΘ (ortho) projekce ne₧ p°i perspektivnφ. NicmΘn∞ kolmß vypadß h∙°e, tudφ₧ provedeme translaci do obrazovky. Po p°esunu o 1 jednotku dovnit°, budeme moci umφstit text kamkoli od -0.5 do +0.5 na ose x. Po p°esunu o 10 bychom mohli vykreslovat na pozice od -5.0 do +5.0. Nikdy nem∞≥te velikost textu a naprosto nikdy nepou₧φvejte zm∞nu m∞°φtka glScale(x,y,z). Chcete-li mφt font v∞tÜφ Φi menÜφ musφte na to myslet p°i vytvß°enφ.

int DrawGLScene(GLvoid)// Vykreslovßnφ

{

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

glLoadIdentity();// Reset matice

glTranslatef(0.0f,0.0f,-1.0f);// P°esun o 1 do obrazovky

Dßle nastavφme barvu textu. V tomto p°φpad∞ pou₧φvßm dva ΦφtaΦe. ╚ervenß slo₧ka se urΦuje podle kosinu prvnφho ΦφtaΦe. Hodnoty se m∞nφ od -1.0 do +1.0. Zelenß slo₧ku vypoΦφtßme podle sinu druhΘho ΦφtaΦe. Rozsahy jsou stejnΘ jako v p°edchozφm p°φpad∞. K modrΘ barv∞ jsou pou₧ity oba ΦφtaΦe s kosinem. Hodnoty nßle₧ejφ od 0.5 do 1.5, tedy v²sledek operace nebude nikdy 0 a text bude v₧dy viditeln².

// Pulzovßnφ barev zßvislΘ na pozici

glColor3f(1.0f*float(cos(cnt1)),1.0f*float(sin(cnt2)),1.0f-0.5f*float(cos(cnt1+cnt2)));

K urΦenφ pozice pou₧ijeme nov² p°φkaz. St°ed z∙stal na 0.0, ale asi jste si vÜimli, ₧e schßzφ pozice osy z. Po p°esunu o jednotku do obrazovky je lev² nejvzdßlen∞jÜφ viditeln² bod -0.5 a prav² +0.5. Proto₧e se v₧dy text vykresluje zleva doprava, p°esuneme o 0.45 doleva. Tφm bude vycentrovßn na st°ed. Pou₧itß matematika vykonßvß stejnou funkci jako p°i nastavovßnφ barvy. Na ose x se text pohybuje od -0.5 do -0.4 (odeΦetli jsme 0.45). Tφm udr₧φme text v₧dy viditeln². Na ose y se hranice nachßzejφ na -0.35 a +0.35.

glRasterPos2f(-0.45f+0.05f*float(cos(cnt1)), 0.32f*float(sin(cnt2)));// Pozice textu

VypφÜeme text. Tuto funkci jsem navrhl jako super snadnou a u₧ivatelsky p°φjemnou. Vypadß jako volßnφ printf() ze stdio.h zk°φ₧enΘ s OpenGL. Text se vykreslφ na pozici, kam jsme p°esunuli p°ed chvφlφ. Pod°et∞zec %7.2f oznamuje vypisovßnφ obsahu prom∞nnΘ. SedmiΦka urΦuje, maximßlnφ dΘlku Φφsla a dvojka up°es≥uje poΦet desetinn²ch mφst. f znaΦφ desetinnΘ Φφslo (float). Je mi samoz°ejm∞ jasnΘ, ₧e pokud ovlßdßte jazyk C, tak je to pro vßs hraΦka. Konvence jsou stejnΘ jako u klasickΘ printf(). Pokud to bude nutnΘ m∙₧ete se podφvat do nßpov∞dy MSDN.

glPrint("Active OpenGL Text With NeHe - %7.2f", cnt1);// V²pis textu

Nakonec zb²vß inkrementovßnφ ΦφtaΦe, aby se m∞nila pozice a barva.

cnt1+=0.051f;

cnt2+=0.005f;

return TRUE;

}

Poslednφ k≤d, kter² se provede p°ed opuÜt∞nφm programu je smazßnφ fontu volßnφm KillFont().

//Konec funkce KillGLWindow(GLvoid)

if(!UnregisterClass("OpenGL",hInstance))

{

MessageBox(NULL,"Could Not Unregister Class.","SHUTDOWN ERROR",MB_OK | MB_ICONINFORMATION);

hInstance=NULL;

}

KillFont();//Smazßnφ fontu

}

Hotovo... Na internetu jsem hledal podobn² tutorißl, ale nic jsem nenaÜel. Mo₧nß jsem prvnφ, kter² pφÜe na podobnΘ tΘma. VÜe je mo₧nΘ. U₧ijte si v²pis textu a Ü¥astnΘ k≤dovßnφ.

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

ZdrojovΘ k≤dy

Lekce 13

<<< Lekce 12 | Lekce 14 >>>