játékfejlesztés.hu
FórumGarázsprojectekCikkekSegédletekJf.hu versenyekKapcsolatokEgyebek
Legaktívabb fórumozók:
Asylum:    5448
FZoli:    4892
Kuz:    4455
gaborlabor:    4449
kicsy:    4304
TPG:    3402
monostoria:    3284
DMG:    3172
HomeGnome:    2919
Matzi:    2520

Pretender:    2498
szeki:    2440
Seeting:    2306
Geri:    2188
Orphy:    1893
Joga:    1791
Bacce:    1783
MaNiAc:    1735
ddbwo:    1625
syam:    1491
(C++) DirectX programozás 17. - Direct3D 6. (Texture Blending) 2006.07.10 23:08


Texture Blending

Ezt a címet nem is magyarítanám, mert nem néztem utána a magyar nevének, inkább elmagyaráznám mit is jelent. A blending egy operáció. Ezt egy operátor definiálja, annak pedig van két argumentuma. Ez így tudom, hogy bonyolult, de példával megvilágosodik: ez az operátor lehet nemes egyszerűséggel például összeadás. Ekkor a két argumentuma az összeadás két tagja. Most a címből kiindulva elgondolkodhatunk azon, mi köze van ennek a textúrázáshoz? Nagyon egyszerű, ugyanis a textúra egyes pixeljeivel is tudunk műveleteket végezni. Azaz például két textúra pixeljét összeszorozni, vagy egy textúra pixeljeit egy színnel keverni(ez is szorzás). Az előbbire nagyon jó példa a statikus megvilágítás, közismert nevén a lightmapping. Erről majd később, de még ebben a cikkben. Természetesen ez az operátor tágan értelmezhető, hiszen mondhatjuk azt is, hogy az operátor legyen az első argumentum kiválasztása.

Ezek után kijelenthetjük, hogy a blending egy „gép”, aminek van egy kimenete és két bemenete, és a kimeneten ad valamit a bemenetektől függően, vagy akár azoktól függetlenül, bár ez utóbbi ritka. Ezt így ábrázolhatjuk:


Középen látható egy trapézzel ábrázolva az operátor. Namost jön a következő nyilvánvaló kérdés, hogy csak két argumentumot lehet variálni? Nem, ez nem igaz. Pontosabban egy operátornál igen, de definiálhatunk több operátort is, aminek az egyik argumentuma lehet egy másik operátor kimenete. Például:


Tehát például így képzelhetjük el. A Direct3D összesen 8 operátort enged, ezeket sorszámozzuk, 0-tól 7-ig. Ez volt az a titokzatos szám a textúrázásnál. Most egyszerűen megismerjük a blending programozását.

Tudnunk kell, hogy igazából nem 8 operátor definiálhatunk, hanem 8 stage-et(szintet, esetleg csatornát), majd minden szinten egy operátort, illetve minden szinthez rendelhetünk egy textúrát, méghozzá a

lpD3D_Device->SetTexture(0,lpD3D_Texture);

függvénnyel, de ez persze csak példa. Első paraméterrel megadjuk, hogy melyik szinthez akarunk rendelni textúrát, második paraméterrel pedig hogy milyen textúrát akarunk hozzárendelni. De mivel több textúrát rendelhetünk így egy testhez, ezért elsőlépésben meg kell adnunk minden szinthez a textúrakoordinátákat. Vegyük példának, hogy 3 szintet használunk, így három textúrakoordinátát kell megadnunk, minden szinthez egyet. Ehhez speciális vertexformátum kell, pl:

#define CUSTOM_VERTEX (D3DFVF_XYZ|D3DFVF_TEX3)

Itt három textúrakoordinátát adunk meg, és megmondtuk, hogy transzformálatlan vertexről van szó. Ekkor nyílván a vertexstruktúra így alakul:

struct CUSTOMVERTEX {
      FLOAT x,y,z;
      FLOAT tu1,tv1;
      FLOAT tu2,tv2;
      FLOAT tu3,tv3;
};


És ennek szellemében kell megadnunk a testeket meghatározó vertexeket is.

Most akkor nézzünk először egy nagyon egyszerű példát, amivel semmit nem csinálunk lényegében, mert ezek az alapbeállítások. De azért nézzük meg őket! Legyen akkor a feladat egy sima textúrázás. Ez nyílván egyszerű blendinget fog igényelni, mivel csak egyetlen szintre lesz szükségünk, ez pedig legyen a 0-dik szint. Itt megadunk egy textúrát, majd megmondjuk, hogy a végső textúra csak ebből a textúrából álljon. Azaz:

lpD3D_Device->SetTexture(0,lpD3D_Texture);
lpD3D_Device->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
lpD3D_Device->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);

Az elsőt már ismerjük, ott tehát a nulladik szintre megadtunk egy textúrát. A másodikkal definiálunk egy színoperátort(azért színoperátor, mert például van alpha csatornára is operátor), méghozzá az operátor nem csinál semmit, csak a kimenetén az egyes argumentumot(ezt jelenti a D3DTOP_SELECTARG1) adja ki. A harmadik sorban pedig megadjuk, hogy az első argumentum a jelenlegi szinten megadott textúra legyen. Így egy test textúrázásánál a Direct3D megkeresi a legnagyobb használt szintet. Ez most a nulladik szint, mert alapértelmezésben a nullánál nagyobb szintek D3DTOP_DISABLE-re vannak állítva, ez pedig azt jelenti, hogy nem használt. Egyébként ha egy szintre ezt állítjuk be, akkor minden felette lévő szint ki lesz kapcsolva.

A kimenet természetesen az éppen használt legmagasabb szint kimenete lesz. Mivel ez most a nulladik szint, ezért ennek a kimenete lesz a textúra. Megnézzük az operátort, ez azt mondja, hogy az egyes argumentum fog kimenni a kimenetén, tehát a testre a sima textúra kerül.

Most nézzünk egy izgalmasabb esetet, azaz amikor a testre egy textúrát feszítünk, de a textúrát kiszinezzük valamilyen színre. Ekkor először is minden vertexhez meg kell adni egy színt illetve egy textúrakoordinátát:

#define CUSTOM_VERTEX (D3DFVF_XYZ|D3DFVF_DIFFUSE |D3DFVF_TEX1)

Így már megadhatjuk a vertexstruktúrát:

struct CUSTOMVERTEX {
      FLOAT x,y,z;
      DWORD color;
      FLOAT tu,tv;
};

Ezután már megadhatunk egy akármilyen testet, a megadásától most eltekintenék. Tehát akkor most beállíthatjuk a nulladik szintre a textúrát:

lpD3D_Device->SetTexture(0,lpD3D_Texture);

Ez a textúra most bármi lehet. Ennek a textúrának a pontjait szeretnénk változtatni a színnel. Hogy hogyan? Lényegében modulálni fogjuk, ami szorzást jelent(akárcsak a hiradástechnikában, ott például az amplitúdómoduláció egy függvény szinuszhullámmal vett szorzatát jelenti). Azaz a textúra pixeljeinek színét összeszorozzuk a vertexek színeiből kapott interpolált színekkel(ez azt jelenti, hogy test pontjainak színével, amit a nyílván a vertexek színeiből kapunk színátmenetet képezve). Ezt egy sima szorzóoperátorral érjük el:

lpD3D_Device->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_MODULATE);
lpD3D_Device->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_DIFFUSE);
lpD3D_Device->SetTextureStageState(0,D3DTSS_COLORARG2,D3DTA_TEXTURE);

Ezt az operátort a nulladik szinten adtuk meg, és látszik, hogy szorzóoperátor(MODULATE). Az operátor két argumentuma a textúra és a szín. Nyílván a D3DTSS_COLORARG2 a második argumentumot jelenti. Ekkor renderelésnél a test a textúrával lesz becsomagolva, de a textúra kap egy színárnyalatot is, méghozzá a vertexeknél megadott színtől függően.

Ezt az előbbi feladatot megoldhatnánk kevésbé elegánsan is, csupán gyakorlásnak. Azaz használhatunk hozzá két szintet is, méghozzá a nulladik és az első szintet. Ekkor a kimenet az első szint operátorának a kimenete lesz. Akkor nézzük, definiáljuk a textúrát a nulladik szintre:

lpD3D_Device->SetTexture(0,lpD3D_Texture);

Gondolom logikus, hogy azt szeretnénk, hogy az nulladik szint kimenete a textúra legyen, hiszen azt fogjuk az első szinten modulálni a színnel. Ekkor a nulladik szint operátora és az első argumentuma:

lpD3D_Device->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
lpD3D_Device->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);

Ennek az eredményét szeretnénk továbbvinni a következő szintre, azaz az első szinten definiálunk egy operátort, méghozzá egy modulálást, majd az egyik paramétere a szín lesz, a másik pedig az őt megelőző szint(ez most a nulladik) eredménye:

lpD3D_Device->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
lpD3D_Device->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_DIFFUSE);
lpD3D_Device->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_CURRENT);

Itt a D3DTA_CURRENT azt jelenti, hogy a második argumentum az előző szint eredménye. Ez tehát ugyanazt csinálja, mint az előzőekben megvalósított egyszintű színnel való árnyalás. Természetesen ha az első szinten nem modulálnánk, hanem csupán kiválasztanánk az első argumentumot, akkor a testet simán színnel töltenénk ki, ugyanis az nulladik szint kárba menne. Ezt is lehet, csak felesleges.

Most pedig megnézzük a példaprogramban is megoldott példát. Csináljunk lightmappinget, azaz rakjunk egymásra két textúrát, az egyik az alaptextúra, a másik pedig fekete-fehér, és azt írja le, hogy a textúra hol és mennyire van megvilágítva. Azaz példán:



+



=



Remélem a képsorozatból érthető. Ez azért jó, mert így gyorsan jeleníthetünk meg nem változó, azaz statikus árnyékokat.

Ehhez csupán kell két szint, nyilván a nulladik szint kimenete az alaptextúra, az első szint kimenete pedig a lightmap és az előző szint modulációja:

lpD3D_Device->SetTextureStageState(0,D3DTSS_COLOROP,D3DTOP_SELECTARG1);
lpD3D_Device->SetTextureStageState(0,D3DTSS_COLORARG1,D3DTA_TEXTURE);
lpD3D_Device->SetTextureStageState(1,D3DTSS_COLOROP,D3DTOP_MODULATE);
lpD3D_Device->SetTextureStageState(1,D3DTSS_COLORARG1,D3DTA_CURRENT);
lpD3D_Device->SetTextureStageState(1,D3DTSS_COLORARG2,D3DTA_TEXTURE);

Gondolom ez érthető. Ekkor megjelenítés előtt meg kell adni a szintekhez tartozó textúrákat:

lpD3D_Device->SetTexture(0,lpD3D_Texture);
lpD3D_Device->SetTexture(1,lpD3D_Lightmap);

Így már tényleg a fentihez hasonló dolog fog megjelenni. Ez található az alábbi forráskódban:

d3d5.exe


2006. február 25.

Crusader


Kapcsolódó linkek:

A cikksorozat további részei:

Értékelés: 0

Új hozzászólás
Nincs megjegyzés