OpenGL (9.) - Generování textur a vylepšení FarClip Distance
Tak jsem se dal do psaní dalšího článku o OpenGL. Dnes jsem pro vás připravil automatické generování textur, výpočet fps a vylepšený Far-clip distance (omezeni viditelné vzdálenosti).
Generování textur
Na serveru FlipCode.com jsem narazil na velmi zajímavý článek nazvaný Terrain Texture Generation. Technika je zde pojmenována jako "procedural texture generation" a není vůbec složitá. Máme bitmapu s odstínem šedé pro generování výšky trojůhelníků (černá barva=nížiny, bílá barva=hory). Dále máme několik (v mém případě 3) bitmap, ve kterých je vlastně charakteristika každého prostředí: (nížina: žlutá bitmapa=písky, hory: vrásčitá bílo-šedá bitmapa=skály, střed: nazelenalá barva=vegetace. Teď, když máme nachystány všechny bitmapy, si můžeme spočítat procentuální složku bitmapy pro každý bod. K tomu nám poslouží následující funkce:
function texfactor(h1:TGLFloat; h2:integer): TGLFloat;
var percent: TGLFloat;
begin
percent := (128 - ABS(h2 - h1)) / 128;
if percent < 0 then percent := 0
else if percent>1 then percent := 0;
result := percent;
end;
![]() |
| Popis obrázku |
Jako první parametr funkce zadáme hlavní výškový bod prostředí, pro které počítáme složku (nížiny=0, střed=128, hory=256), a do druhého parametru dosadíme výšku mapy. Funkce nám "vyhodí" procentní hodnotu každé složky pro dosazený bod. Budeme muset získat všechny bitmapy a postupně si je uložíme do polí buffer1-buffer3:
LoadTexture('snow.jpg'); //Funkce nacte texturu snehu
buffer1:=buffer;
LoadTexture('grass.jpg'); //Funkce nacte texturu travy
buffer2:=buffer;
LoadTexture('sand.jpg'); //Funkce nacte texturu pisku
buffer3:=buffer;
Teď z těchto tří bitmap vypočteme jednu výslednou bitmapu, která obsahuje složky nížin, vegetace a hor podle výškového uspořádání:
for j:=1 to 256 do
for i:=1 to 256 do begin
hmap_height := Map[j,i].red;
tex_fact[1] := texfactor(256, hmap_height);
tex_fact[2] := texfactor(128, hmap_height);
tex_fact[3] := texfactor(0, hmap_height);
old_red[1] := buffer1[i,j].red;
old_green[1] := buffer1[i,j].green;
old_blue[1] := buffer1[i,j].blue;
old_red[2] := buffer2[i,j].red;
old_green[2] := buffer2[i,j].green;
old_blue[2] := buffer2[i,j].blue;
old_red[3] := buffer3[i,j].red;
old_green[3] := buffer3[i,j].green;
old_blue[3] := buffer3[i,j].blue;
new_red := round((tex_fact[1]*old_red[1]) +
(tex_fact[2]*old_red[2]) + (tex_fact[3]*old_red[3]));
new_green := round((tex_fact[1]*old_green[1]) +
(tex_fact[2]*old_green[2]) + (tex_fact[3]*old_green[3]));
new_blue := round((tex_fact[1]*old_blue[1]) +
(tex_fact[2]*old_blue[2]) + (tex_fact[3]*old_blue[3]));
buffer[i,j].red := new_red;
buffer[i,j].blue := new_blue;
buffer[i,j].green := new_green;
end;
A takto vygenerovanou texturu nastavíme jako texturu 1, která je nastavená jako textura pro krajinu:
glGenTextures(1, @textura[1]);
glBindTexture(GL_TEXTURE_2D, textura[1]);
glTexParameterf(GL_Texture_2D,GL_Texture_Wrap_S,GL_REPEAT);
glTexParameterf(GL_Texture_2D,GL_Texture_Wrap_T,GL_REPEAT);
glTexParameterf(GL_Texture_2D,GL_Texture_Mag_Filter,GL_Linear);
glTexParameterf(GL_Texture_2D,GL_Texture_Min_Filter,GL_Linear);
glTexImage2d(GL_Texture_2D,0,3,Velikost+1,Velikost+1,
0,GL_RGB,GL_Unsigned_byte,@buffer);
Teď ukázka jak to funguje ve skutečnosti. Zdroják dnešního snažení si můžete stáhnout dole.
![]() | ![]() | ![]() |
| Počáteční bitmapa odstínů šedé | Vygenerovaná textura | Takhle to vypadá nasazené do krajiny |
Nejlepší na této metodě je, že k tvorbě jednoho levelu potřebujeme pouze jeden obrázek s odstíny šedé, který určuje výšku každého bodu a tím zároveń i texturu, kterou celý "svět" potáhneme.
![]() |
Vylepšení Far-Clip distance
V proceduře, která zjišŤuje kolize jsem si zároveň vypočítal nejmenší vzdálenost každého trojúhelníku ve scéně ke kameře a tuto hodnotu jsem uchoval pro pozdější rozhodování.
if i=0 then begin //Zjisti min. vzdalenost trojuhelnika od kamery if (Akamera<Bkamera) AND (Akamera<Ckamera) then Min:=Akamera; if (Bkamera<Akamera) AND (Bkamera<Ckamera) then Min:=Bkamera; if (Ckamera<Akamera) AND (Ckamera<Bkamera) then Min:=Ckamera; //Hodnoty o vzdalenosti trojuhelnika od kamery dosadi do pole //pro mozne budouci serazeni od nejvzdalenejsiho vzdalenost[count].Id:=count; vzdalenost[count].Min:=Min; end;
Taky jsem tuto proceduru vylepšil v tom, že se detekce kolizí počítá jen pro trojúhelníky, které jsou vzdáleny od kamery méně než 20 jednotek. Tímto se zvýšila fps alespoň o 30fps.
if vzdalenost[count].Min<30 then begin
...
end;
A konečně vykreslíme jen ty trojúhelníky, které mají od kamery menší vzdálenost než je hodnota Far-Clip. Tyto příkazy jsou mnohem efektivnější, nežli ořezávání scény pomocí řezů ClipPlane.
//Vykresleni sceny po trojuhelnicich
for i:=1 to MaxTriangles do //Vykresli trojuhelniky podle poradoveho cisla
if vzdalenost[i].min<FarClip then begin
glBindTexture(GL_TEXTURE_2D, textura[1]); //nacte texturu
glTexCoordPointer(2, GL_FLOAT, 0, @CoordPointer[i]); //nacte tex. koord.
glVertexPointer(3, GL_FLOAT, 0, @VertexPointer[i]); //nacte pole bodu
glDrawElements(GL_TRIANGLES, 4, GL_UNSIGNED_INT, @Indices[i]);
end;
Tenhle příklad je vhodný jen pro omezení viditelné vzdálenosti "do dálky", ale všechny trojúhelníky vzdálené méně než 20 jednotek se vykreslují i přesto, že jsou nakonec mimo obrazovku (za kamerou, vedle kamery). Proto je vhodné (a já to také někdy popíšu) také determinovat zobrazování i těch trojúhelníků které budou ležet mimo viditelnou oblast. To lze (snad) udělat snadno pomocí zjištění toho nejmenšího úhlu, který svírá vektor jednoho ze tří bodů trojúhelníka se směrovým vektorem kamery. Myslím, že to tak půjde...
![]() |
Výpočet fps
Pro výpočet obnovovací frekvence nám dobře poslouží následující procedura, kterou jsem objevil kdesi na netu:
procedure TForm1.CalcFPS(CStep: integer);
var
i: TLargeInteger;
FrameTime: double;
begin
if CStep = 1 then begin //Calculate start Frequency and start Counter
QueryPerformanceCounter(i);
StartCount := i;
QueryPerformanceFrequency(i);
StartFreq := i;
StartCount := StartCount / StartFreq;
end;
if CStep = 2 then begin //Calculate Counter before draw scene
QueryPerformanceCounter(i);
Count1 := i / StartFreq;
end;
if CStep = 3 then begin //Calculate Counter after draw scene
QueryPerformanceCounter(i);
Count2 := i / StartFreq;
FrameTime := (Count2 / StartCount) - (Count1 / StartCount);
FPS := 1 / FrameTime; //calculate FPS
FPS := FPS / StartCount;
Label1.Caption:=inttostr(round(FPS))+'fps / ';
end;
end;
Musíme také mít nadefinovány tyto nové proměnné:
StartFreq: TLargeInteger; StartCount,Count1, Count2, FPS: double; FPSi: integer;
Při inicializaci zavoláme proceduru CalcFPS s parametrem 1...
CalcFPS(1);
A poté jen vložíme do místa, kudy program prochází při každém počítání scény, já jsem jej umístil do procedury TForm1.ZobrazitScenu:
if FPSi = 2 then begin //Vypocet FPS
CalcFPS(2);
FPSi := 3;
end else begin
CalcFPS(3);
FPSi := 2;
end;
Už v samotné proceduře je přidáno Label1.caption:=inttostr(round(FPS))+'fps';, takže nemusíme nikterak čekat na výsledek a ten se nám zobrazí v Label1.
![]() |
Celý zdroják včetně obrázků si můžete stáhnout Tady (229 kB).
Vyšlo 20.01.2002, v blogu: 0 1 2 3 4 5 6 7 8
Děkuji, že jste se rozhodl(a) přečíst tento článek. Budu rád i za komentář. Pokud Vás tento článek zaujal a rádi byste jej doporučili ostatním, podpořte mně prosím tím, že věnujete minutku svého času a uděláte mi reklamu na linkuj.cz, vybrali.sme.sk či jagg.cz. Přeji příjemné čtení
Poslední články
- Jak na tvorbu her - Na co máme myslet při programování hry?
- 3D Studio MAX (10.) - Izometrický pohled ve 3D Studiu MAX
- Adobe Photoshop (4.) - Chromový text
- 3D Studio MAX (11.) - Tvorba realistické jeskyně
- Zoner Callisto (2.) - Export do PDF
- OpenGL (11.) - Nová detekce kolizí
- OpenGL (10.) - Nová knihovna geometry.pas
- OpenGL (9.) - Generování textur a vylepšení FarClip Distance
- Zoner Callisto (1.) - Export do GIFu
- 3D Studio MAX (9.) - Vytváření odlesků vodní hladiny
- OpenGL (8.) - Slovníček pojmů v OpenGL
- OpenGL (7.) - Detekce kolizí, načtení mapy a textury v jpg souboru
- OpenGL (6.) - Co je to MiniGL
- OpenGL (5.) - Urychlení pomocí glDrawElements
- Delphi (7.) - Syst. repro a autodestrukce souboru






