Každý, kdo někdy programoval částicové systémy, se jistě setkal s problémem, jak zařídit, aby byly polygony viditelné z jakéhokoli směru. Nebo-li, aby se nikdy nestalo, že při natočení kamery kolmo na rovinu částice, nebyla vidět pouze tenká linka. Složitý problém, ultra jednoduché řešení...
Existuje několik cest, z nichž každá vede ke zdárnému cíli, nicméně nejefektivnějším bude pravděpodobně rozšíření GL_ARB_point_sprite. My jí však nepůjdeme a to hned ze dvou důvodů. Prvním a hlavním je, že můj Radeon 7000 už začíná být trochu dýchavičný a toto rozšíření bohužel nepodporuje. Neměl bych kde ověřit funkčnost kódu. Druhý, který ale v podstatě souvisí s prvním, je, že když používáte extensiony, měli byste zajistit i běh na softwaru.
V tomto článku si tedy naprogramujeme vše ručně. Pokud vás zajímá možnost s extensiony a měla by vás zajímat, protože odebírá procesoru spoustu práce, podívejte se na zdrojový kód "With Extension Support" 44. NeHe Tutoriálu, ve kterém se toto rozšíření používá.
Pravděpodobně znáte jedno hodně nenáročné řešení, které je šetrné k procesoru, ale bohužel ne vždy jde použít. Zdrojový kód by mohl vypadat například následovně (mimochodem 9. NeHe Tutoriál).
glLoadIdentity();// Reset matice
glTranslatef(0.0f, 0.0f, -20.0f);// Posun do obrazovky
glRotatef(rot_x, 1.0f, 0.0f, 0.0f);// Naklopení
glRotatef(rot_y, 0.0f, 1.0f, 0.0f);// Pootočení
glTranslatef(pos_x, 0.0f, 0.0f);// Ještě jeden posun
glRotatef(-rot_y, 0.0f, 1.0f, 0.0f);// Zruší pootočení
glRotatef(-rot_x, 1.0f, 0.0f, 0.0f);// Zruší naklopení
// Polygony jsou přiklopené k ose z -> rendering
...tím, že jsme za druhým glTranslatef() v inverzním pořadí zavolali všechny rotace ještě jednou, ale s opačným úhlem, zůstaly translace zachovány, ale rotace se vyrušily. Když budeme tedy vykreslovat polygony rovnoběžně s osou z, budou automaticky přiklopené. A teď k tomu problému: Jak určit rotace při používání např. gluLookAt()?
Takže třetí cesta... ne každý ví, já to tedy nevěděl, že jsou v modelview matici uloženy přímo souřadnice vektorů (up, right, front) kamery. Stačí je vzít a použít... :o)
[right_x, up_x, front_x, 0] [right_y, up_y, front_y, 0] [right_z, up_z, front_z, 0] [ t_x, t_y, t_z, 1]
Napíšeme jednoduchou funkci, jíž se budou předávat souřadnice v prostoru, na kterých se má částice vykreslit, a její velikost. Myslím, že komentáře postačí...
// Quad vždy přiklopený ke kameře (částice apod.)
void DrawBillboardedQuad(float x, float y, float z, float width, float height)
{
width /= 2.0f;// Polovina velikosti
height /= 2.0f;
float mat[16];
glGetFloatv(GL_MODELVIEW_MATRIX, mat);
// QVector zapouzdřuje operace s 3D vektorem
QVector<float> right(mat[0], mat[4], mat[8]);
right.Normalize();
right *= width;
QVector<float> up(mat[1], mat[5], mat[9]);
up.Normalize();
up *= height;
// Vykreslení
glBegin(GL_QUADS);
glVertex3f(x + (-right.GetX() - up.GetX()),
y + (-right.GetY() - up.GetY()),
z + (-right.GetZ() - up.GetZ()));
glVertex3f(x + ( right.GetX() - up.GetX()),
y + ( right.GetY() - up.GetY()),
z + ( right.GetZ() - up.GetZ()));
glVertex3f(x + ( right.GetX() + up.GetX()),
y + ( right.GetY() + up.GetY()),
z + ( right.GetZ() + up.GetZ()));
glVertex3f(x + (-right.GetX() + up.GetX()),
y + (-right.GetY() + up.GetY()),
z + (-right.GetZ() + up.GetZ()));
glEnd();
}
Ještě se lze setkat s natáčením pouze okolo jedné osy. Typickým příkladem je oheň, stromy a podobně. To se dělá například takto...
// Natáčí objekt pouze vzhledem k ose y (stromy, oheň apod.)
void DrawBillboardedQuad(float x, float y, float z, float width, float height)
{
width /= 2.0f;// Polovina velikosti
height /= 2.0f;
QVector<float> pos(x, y + height, z);
// pozice_kamery je vektor, který obsahuje aktuální umístění kamery
QVector<float> a;// Vektor od kamery k billboardu
a.SetX(pozice_kamery.GetX() - pos.GetX());
a.SetY(pozice_kamery.GetY() - pos.GetY());
a.SetZ(pozice_kamery.GetZ() - pos.GetZ());
a.Normalize();
QVector<float> b;// Standardní up vektor
b.SetX(0.0f);
b.SetY(1.0f);
b.SetZ(0.0f);
// b.Normalize();// [0,1,0] je už normalizované
// Vektorový součin těchto vektorů (vektor kolmý k oběma najednou)
QVector<float> c(a.Cross(b));
c.Normalize();
// Neguje směr, aby byl vidět čelní face
QVector<float> right;
right = -c * width;
QVector<float> up;
up.SetX(0.0f);
up.SetY(1.0f * height);
up.SetZ(0.0f);
// Vykreslení
glBegin(GL_QUADS);
glVertex3f(pos.GetX() + (-right.GetX() - up.GetX()),
pos.GetY() + (-right.GetY() - up.GetY()),
pos.GetZ() + (-right.GetZ() - up.GetZ()));
glVertex3f(pos.GetX() + ( right.GetX() - up.GetX()),
pos.GetY() + ( right.GetY() - up.GetY()),
pos.GetZ() + ( right.GetZ() - up.GetZ()));
glVertex3f(pos.GetX() + ( right.GetX() + up.GetX()),
pos.GetY() + ( right.GetY() + up.GetY()),
pos.GetZ() + ( right.GetZ() + up.GetZ()));
glVertex3f(pos.GetX() + (-right.GetX() + up.GetX()),
pos.GetY() + (-right.GetY() + up.GetY()),
pos.GetZ() + (-right.GetZ() + up.GetZ()));
glEnd();
}
Abych to shrnul, velice krátký článeček, ale může se hodit...
napsal: Michal Turek - Woq <WOQ (zavináč) seznam.cz>