Lekce 9 - Pohyb bitmap ve 3D prostoru

Tento tutoriál vás naučí pohyb objektů ve 3D prostoru a kreslení bitmap bez černých míst, zakrývajících objekty za nimi. Jednoduchou animaci a rozšířené použití blendingu. Teď byste už měli rozumět OpenGL velmi dobře. Naučili jste se vše od nastavení OpenGL okna, po mapování textur za použití světel a blendingu. To byl první tutoriál pro středně pokročilé. A pokračujeme dále...

#include <windows.h>// Hlavičkový soubor pro Windows

#include <stdio.h>// Hlavičkový soubor pro standardní vstup/výstup

#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

Twinkle určuje, zda se používá třpytivý efekt a tp indikuje stisk klávesy T.

bool twinkle;// Třpytivý efekt

bool tp;// Stisknuto T?

Num určuje kolik hvězd bude zobrazeno na obrazovce. Je definováno jako konstanta, takže ho můžete měnit libovolně, ale jen v tomto řádku. Nezkoušejte měnit hodnotu num později v kódu, pokud nechcete přivodit katastrofu.

const num=50;// Počet zobrazovaných hvězd

Deklarujeme strukturu, v níž budeme uchovávat informace o jednotlivých hvězdách.

typedef struct// Struktura hvězdy

{

int r, g, b;// Barva

GLfloat dist,// Vzdálenost od středu

angle;// Úhel natočení

} stars;// Jméno struktury je stars

Každá položka v poli star obsahuje objekt struktury stars, tj. pět hodnot popisujících hvězdu.

stars star[num];// Pole hvězd o velikosti num

Dále vytvoříme proměnné pro nastavení vzdálenosti pozorovatele (zoom) a úhlu pozorování (tilt). Deklarujeme proměnnou spin natáčející hvězdy okolo osy z, což bude vypadat jako by se otáčely okolo své současné pozice. Loop je řídící proměnná cyklu, který použijeme pro nakreslení všech padesáti hvězd. Texture[1] ukládá jednu černobílou texturu.

GLfloat zoom=-15.0f;// Hloubka v obrazovce

GLfloat tilt=90.0f;// Úhel pohledu

GLfloat spin;// Natočení hvězd

GLuint loop;// Řídící proměnná cyklu

GLuint texture[1];// Ukládá texturu

Hned po předcházejícím kódu přidáme kód pro nahrání textury. Nebudu jej znovu opisovat. Je to ten samý jako v lekci 6, 7 a 8. Bitmapa, kterou tentokrát nahrajeme je nazvána star.bmp. Textura bude používat lineární filtrování.

if(TextureImage[0]=LoadBMP("Data/Tim.bmp"))// Loading bitmapy

V tomto projektu nebudeme používat hloubkové testování, takže pokud používáte kód z lekce 1, ujistěte se, že jste odstranili volání glDepthFunc(GL_LEQUAL); a glEnable(GL_DEPTH_TEST); jinak získáte velmi špatné výsledky. Nicméně v tomto kódu používáme mapování textur, takže se ujistěte, že jste přidali řádky, které nejsou v lekci 1. Všimněte si že povolujeme mapování textur a blending.

int InitGL(GLvoid)// Všechna nastavení OpenGL

{

if (!LoadGLTextures())// Nahraje textury

{

return FALSE;

}

glEnable(GL_TEXTURE_2D);// Zapne texturové mapování

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

glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);// Nejlepší perspektivní korekce

glBlendFunc(GL_SRC_ALPHA,GL_ONE);// Typ blendingu pro průhlednost

glEnable(GL_BLEND);// Zapne blending

Následující kód je nový. Nastaví počáteční úhel, vzdálenost a barvu každé hvězdy. Všimněte si jak je jednoduché změnit hodnoty ve struktuře. Smyčka projde všech 50 hvězd.

for (loop=0; loop<num; loop++)// Inicializuje hvězdy

{

star[loop].angle=0.0f;// Všechny mají na začátku nulový úhel

Počítám vzdálenost pomocí aktuální hvězdy (hodnoty proměnné loop), kterou dělím maximálním počtem hvězd. Poté násobím výsledek pěti. V podstatě to posune každou hvězdu o trochu dále než tu předcházející. Když je loop 50 (poslední hvězda), loop děleno num je 1.0f. Příčina proč násobím pěti je, že 1*5= 5 a to je okraj obrazovky. Nechci aby hvězdy nebyly zobrazené takže 5.0f je perfektní. Pokud nastavíte hodnotu proměnné zoom hlouběji do obrazovky, můžete použít hodnotu větší než 5.0f, ale hvězdy budou menší (z důvodu perspektivy). Všimněte si, že barva každé hvězdy je tvořena pomocí náhodných hodnot od 0 do 255. Můžete se divit jak můžeme použít tak velké hodnoty, když normálně jsou hodnoty barev od 0.0f do 1.0f. Když nastavujeme barvu, použijeme funkci glColor4ub namísto glColor4f. ub znamená unsigned byte, který může nabývat hodnot od 0 do 255. V tomto programu je jednodušší použít byty než generovat desetinné hodnoty.

star[loop].dist=(float(loop)/num)*5.0f;// Vzdálenost od středu

star[loop].r=rand()%256;// Barva

star[loop].g=rand()%256;// Barva

star[loop].b=rand()%256;// Barva

}

return TRUE;

}

Na řadu přichází vykreslování.

int DrawGLScene(GLvoid)// Vykreslování

{

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);// Smaže obrazovku a hloubkový buffer

glBindTexture(GL_TEXTURE_2D, texture[0]);// Výběr textury

for (loop=0; loop<num; loop++)// Prochází jednotlivé hvězdy

{

glLoadIdentity();// Reset matice

glTranslatef(0.0f,0.0f,zoom);// Přesun do obrazovky o zoom

glRotatef(tilt,1.0f,0.0f,0.0f);// Naklopení pohledu

Teď pohneme hvězdou. První věc kterou uděláme je pootočení okolo osy y. Další řádek kódu posune hvězdu na ose x. Normálně to znamená posun na pravou stranu obrazovky, ale protože jsme pootočili výhled okolo osy y, kladná hodnota osy x může být kdekoli.

glRotatef(star[loop].angle,0.0f,1.0f,0.0f);// Rotace o úhel konkrétní hvězdy

glTranslatef(star[loop].dist,0.0f,0.0f);// Přesun vpřed na ose x

Hvězda je ve skutečnosti plochá textura. Pokud nakreslíte plochý čtyřúhelník a namapujete na něj texturu, bude to vypadat dobře. Bude čelem k vám, jak má. Ale když scénu pootočíte o 90 stupňů okolo osy y, textura bude čelem k levé nebo pravé straně obrazovky a vy uvidíte pouze tenkou linku, což nechceme. Chceme aby hvězdy byly pořád čelem k nám nezávisle na natočení a naklopení. Uděláme to zrušením všech rotací v opačném pořadí těsně předtím než vykreslíme hvězdu. Pootočíme zpět zadáním invertovaného úhlu pro rotaci a poté zrušíme naklopení opět pomocí záporného úhlu. Protože jsme dříve posunuli počátek, tak je na pozici ve které jsme ji chtěli. Změnili jsme její polohu, ale texturu stále vidíme správně zepředu.

glRotatef(-star[loop].angle,0.0f,1.0f,0.0f);// Zrušení pootočení

glRotatef(-tilt,1.0f,0.0f,0.0f);// Zrušení naklopení

Jestliže je twinkle TRUE nakreslíme na obrazovku nerotující hvězdu. Pro získání rozdílných barev vezmeme maximální počet hvězd (num) a odečteme číslo aktuální hvězdy (loop), poté odečteme 1, protože loop nabývá hodnot od 0 do num-1. Tímto způsobem získáme hvězdy rozdílných barev. Není to právě nejlepší způsob, ale je efektivní. Poslední hodnota je alfa hodnota. Čím je nižší, tím je hvězda průhlednější. Pokud projde kód podmínkou, bude každá hvězda nakreslena dvakrát. To zpomalí program. O kolik závisí na vašem počítači, ale výsledek bude stát za to - smísí se barvy dvou hvězd. Protože se nenatáčí, budou vypadat, jako by byly animované. Všimněte si jak je jednoduché přidat barvu do textury. Třebaže je textura černobílá, dostaneme takovou barvu, jakou zvolíme před vykreslením.

if (twinkle)// Pokud je zapnutý třpytivý efekt

{

glColor4ub(star[(num-loop)-1].r,star[(num-loop)-1].g,star[(num-loop)-1].b,255);

glBegin(GL_QUADS);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);

glEnd();

}

Teď vykreslíme hlavní hvězdu. Jediný rozdíl od předcházejícího kódu je, že tato hvězda je natočena okolo osy z a má jinou barvu (viz. indexy).

glRotatef(spin,0.0f,0.0f,1.0f);

glColor4ub(star[loop].r,star[loop].g,star[loop].b,255);

glBegin(GL_QUADS);

glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f,-1.0f, 0.0f);

glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f,-1.0f, 0.0f);

glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 0.0f);

glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 0.0f);

glEnd();

Pootočíme hvězdu zvětšením hodnoty proměnné spin. Poté změníme úhel každé hvězdy o loop/num. To znamená, že vzdálenější hvězdy se otáčí rychleji. Nakonec snížíme vzdálenost hvězdy od středu, takže to vypadá, že jsou nasávány doprostřed.

spin+=0.01f;// Pootočení hvězd

star[loop].angle+=float(loop)/num;// Zvýšení úhlu hvězdy

star[loop].dist-=0.01f;// Změna vzdálenosti hvězdy od středu

Zkontrolujeme zda hvězda dosáhla středu. Pokud se tak stane, dostane novou barvu a je posunuta o 5 jednotek od středu, takže může opět začít svou cestu jako nová hvězda.

if (star[loop].dist<0.0f)// Dosáhla středu

{

star[loop].dist+=5.0f;// Nová pozice

star[loop].r=rand()%256;// Nová barva

star[loop].g=rand()%256;// Nová barva

star[loop].b=rand()%256;// Nová barva

}

}

return TRUE;

}

Přidáme kód zjišťující stisk klávesy T. Přejděte k funkci WinMain(). Najděte řádek SwapBuffers(hDC). Píšeme za něj.

// Funkce WinMain()

SwapBuffers(hDC);// Prohození bufferů

if (keys['T'] && !tp)// T - třpytivý efekt

{

tp=TRUE;

twinkle=!twinkle;

}

if (!keys['T'])// Uvolnění T

{

tp=FALSE;

}

if (keys[VK_UP])// Šipka nahoru - nakloní obraz

{

tilt-=0.5f;

}

if (keys[VK_DOWN])// Šipka dolu - nakloní obraz

{

tilt+=0.5f;

}

if (keys[VK_PRIOR])// PageUp - zvětší hloubku

{

zoom-=0.2f;

}

if (keys[VK_NEXT])// PageDown - zmenší hloubku

{

zoom+=0.2f;

}

A máme hotovo. Naučili jste se jednoduchou, ale celkem efektní animaci.

napsal: Jeff Molofee - NeHe <nehe (zavináč) connect.ab.ca>
přeložil: Milan Turek <nalim.kerut (zavináč) email.cz>

Zdrojové kódy

Lekce 9

<<< Lekce 8 | Lekce 10 >>>