Direct 3D - Point sprites
Pravdepodobne vám tento názov nič nehovorí. Hľadal som nejaký prijateľný slovenský preklad, ale nakoniec som sa rozhodol, že lepšie bude zostať pri pôvodnom. Keď si spustíte projekt, možno vás napadne, o čom bude táto lekcia…
Teória
Point sprites je technika, pri ktorej sú vykresľované iba body. Každý bod však obsahuje textúru. Znie to trocha čudne, ale princíp je jednoduchý. Predstavte si, že by ste potrebovali vytvoriť dážď alebo sneženie. Každá snehová vločka by bol štvorec so štyrmi vertexami (TriangleStrip) a priehľadnou textúrou (obrázok vločky). Aby sneženie vyzeralo prirodzene budete potrebovať 500 vločiek. To je spolu 2000 vertexov a to scéna ešte neobsahuje žiadne objekty. Využitím point sprites bude jedna vločka tvorená jedným vertexom. Preto sa hlavne využívajú pri rôznych efektoch s malými čiastočkami, tzv. particle systems. Jeden taký môžete nájsť aj v priloženom projekte. Veľmi pekný príklad nájdete aj v DX8 SDK.
Keďže point sprite (ďalej už len PS) nie je nič iné ako vertex aj jeho deklarácia je rovnaká:
Private Type ParticleVertex X As Single Y As Single Z As Single color As Long tu As Single tv As Single End Type 'Vertex shader pre particle Const FVF_PARTICLEVERTEX = (D3DFVF_XYZ Or D3DFVF_DIFFUSE Or D3DFVF_TEX1) Dim Fontana(PocetPartic - 1) As ParticleVertex
Súradnice normálového vektoru sa teraz nepoužívajú. PS je VŽDY orientovaný ku kamere. To prirodzene vyplýva aj z faktu, že ich poloha (a tým aj orientácia) je určená iba jedným vertexom. Ďalším krokom je inicializácia:
'Zapnutie renderovania point sprites D3DDevice.SetRenderState D3DRS_POINTSPRITE_ENABLE, 1 'D3D teraz moze zmenit velkost particle D3DDevice.SetRenderState D3DRS_POINTSCALE_ENABLE, 1 'Nastavenie velkosti particle D3DDevice.SetRenderState D3DRS_POINTSIZE, FtoDW(0.02) Set ParticleTexture = D3DX.CreateTextureFromFileEx(D3DDevice, App.Path & "\particle.bmp", 32, 32, D3DX_DEFAULT, 0, D3DFMT_A1R5G5B5, D3DPOOL_DEFAULT, D3DX_FILTER_LINEAR, D3DX_FILTER_LINEAR, &HFFFF00FF, ByVal 0, ByVal 0)
Tieto tri príkazy pre renderovacie zariadenie stačia na inicializáciu celého PS enginu. Hodnota D3DRS_POINTSPRITE_ENABLE je nastavená na True. Potom tu a tv súradnice textúry vertexu nie je potrebné zadávať. Sú automaticky doplnené tak, aby bola použitá celá textúra. V prípade hodnoty False je treba súradnice nastaviť ručne, tak ako pri obyčajnom vertexe.
Ako si PS zachovávajú svoju veľkosť udáva D3DRS_POINTSCALE_ENABLE. Ak je True, potom D3D mení ich veľkosť v závislosti od Z vzdialenosti od kamery a hodnoty D3DRS_POINTSIZE. V opačnom prípade je veľkosť konštantná. Posledný parameter je D3DRS_POINTSIZE. Jeho defaultná hodnota je 1. Často krát ale potrebujeme PS zmenšiť, preto toto číslo bude v rozhraní 0 až 1 (tedy desatinné - Single). A tu nastáva problém. Keď sa pozriete na predpis funkcie SetRenderState, zistíte, že posledný paremeter je typu Long a my sem potrebujeme dosadiť Single. Keby ste sem napísali napr. 0.05, Visual Basic odtrhne 0 od 05 a vráti iba nulu. Našťastie je tu jeden trik ako VB oklamať. So zásobníkom D3DXBuffer sme sa už stretli v lekcii Zložité objekty. Má jednu skvelú vlastnosť: môžete do neho vložiť, ale aj vybrať podobné typy premenných pokiaľ majú rovnakú bitovú dĺžku. A práve Long aj Single spotrebuje presne 4 bajty. Potom môžeme do bufferu uložiť číslo Single a vybrať Long, pričom obsah zostane zachovaný.
Private Function FtoDW(f As Single) As Long Dim buf As D3DXBuffer Dim l As Long Set buf = D3DX.CreateBuffer(4) D3DX.BufferSetData buf, 0, 4, 1, f D3DX.BufferGetData buf, 0, 4, 1, l FtoDW = l End Function
BufferSetData, BufferGetData
object - D3DxBuffer, do ktorého sa majú uložiť/vybrať data
Index - index, pod kt. budú/sú data uložené
TypeSize - veľkosť dát
TypeCount - počet premenných
Data - data na uloženie/vybratie
Poslednou fázou je renderovanie:
D3DDevice.SetVertexShader FVF_PARTICLEVERTEX 'Vypnutie zapisovania do Z-bufferu D3DDevice.SetRenderState D3DRS_ZWRITEENABLE, 0 D3DDevice.SetTexture 0, ParticleTexture D3DDevice.DrawPrimitiveUP D3DPT_POINTLIST, PocetPartic, Fontana(0), Len(Fontana(0)) D3DDevice.SetRenderState D3DRS_ZWRITEENABLE, 1
Nebol vytvorený VertexBuffer, preto sa všetko renderuje priamo z poľa Fontana(). Funkcia DrawPrimitiveUP má teraz parameter D3DPT_POINTLIST, pretože renderujeme body a nie trojuholníky. Predtým je však potrebné vypnúť zapisovanie do Z-bufferu. Inak dôjde k chybe pri transparentnosti (čierne okolie bodov).
Je dôležité zachovať poradie renderovaných objektov! Na poradí objektov nezáleží, pokiaľ je zapnutý Z-buffer. Ako ste si mali možnosť všimnúť, pri PS sa vypína. Potom sa môže stať, že PS budú prekreslené iným objektom, ktorý by mal byť až za nimi. Ak napr. chcete, aby PS boli vždy navrchu, renderujte ich až ako posledné. A ešte jedno upozornenie: Je lepšie vytvoriť druhú deklaráciu vertexu v prípade, že chcete použiť objekty s iným typom vertexu (napr. vertex, ktorý obsahuje aj normálový vektor). Ale nezabudnite počas renderovania meniť vertex shader. To je všetko, čo potrebujete vedieť na použite PS.
V projekte sú ešte dve ďalšie procedúry, ktoré sa starajú o celú animáciu. Prvá proc. SetParticle nastavuje začiatočnú pozíciu a farbu (aj s alfa kanálom) particle. Ďalej vytvára vektor náhodného smeru, podľa ktorého sa particle po celý čas pohybuje.
Fontana(i).X = 0 Fontana(i).Y = -2 Fontana(i).Z = 0 Fontana(i).color = D3DColorARGB(ZacFarba.a * 255,_ ZacFarba.r * 255, ZacFarba.g * 255, ZacFarba.b * 255) 'vygeneruje sa vektor nahodneho smeru pohyb(i).X = ((10 * Rnd) - 5) / 4000 pohyb(i).Y = (((10 * Rnd)) - 1) / 300 pohyb(i).Z = ((10 * Rnd) - 5) / 4000
Procedúra UpdateParticle je volaná pri každom novom frame. Nie je to moc dobré, pretože rýchlosť animácie závisí od FPS, ale pre ukážku PS to stačí. Jej úlohou je meniť polohu a farbu particle. Nachádza sa tu jedna nová funkcia - D3DXColorLerp. Vytvára lineárnu interpoláciu (prechod) medzi dvoma farbami. To, či bude výsledná farba bližšie k začiatočnej alebo koncovej farbe, určuje interpolant.
D3DXColorLerp
COut - výsledná farba
C1 - začiatočná farba
C2 - koncová farba
s - interpolant z intervalu <0,1>
For i = 0 To PocetPartic - 1 Fontana(i).X = Fontana(i).X + pohyb(i).X Fontana(i).Y = Fontana(i).Y + pohyb(i).Y Fontana(i).Z = Fontana(i).Z + pohyb(i).Z D3DXColorLerp AktFarba, ZacFarba, KoncFarba, (Fontana(i).Y + 2) / 5 Fontana(i).color = D3DColorARGB(AktFarba.a * 255, AktFarba.r * 255, AktFarba.g * 255, AktFarba.b * 255) If Fontana(i).Y < -2 Then SetParticle (i) Next i For i = 0 To PocetPartic - 1 pohyb(i).Y = pohyb(i).Y - Gravitacia / 2000 Next i
Farba particle sa mení podľa výšky, v ktorej sa nachádza (Y súradnice). Interpolant je tedy Y súradnica. Particle má začiatočnú farbu, keď je najnižšie a koncovú, keď je najvyššie. Ak nejaká particle klesne pod -2 je resetovaná na pôvodnú hodnotu. Nakoniec funkcia zmenšuje Y súradnicu vektoru pohybu každej particle. Tým prestáva stúpať a začne postupne klesať, akoby pôsobila gravitácia.