Navigálás a 3D-s térben
0. Folytassuk az előzőekben elkezdett programot!
Eddig egy semmi jele nincs annak a programunkban, hogy a háromszög valóban térben van.
Bővítsük a programot, hogy a háromszög magától forogjon az Y-tengely körül!
1. Adjunk hozzá egy onFrameMove eseménykezelőt az osztályunkhoz!
2. Ebben hozzunk létre egy D3DXMATRIX és a D3DXMatrixRotationY függvény segítségével készítsünk belőle forgatási mátrixot! A kész transzformációs mátrixot a IDirect3DDevice9::SetTransform függvénnyel adjuk át DirectX-nek!
void Triangle::onFrameMove( double fTime, float fElapsedTime ) { static const float rotSpd = 0.5f; D3DXMATRIX rot; D3DXMatrixRotationY(&rot, (float)fTime*rotSpd); _device->SetTransform(D3DTS_VIEW, &rot); }
Pár furcsaság:
- Miért csak fél háromszög látszik?
- Miért tűnik el néha teljesen?
A) Miért csak fél háromszög látszik?
A "világ" a Z-tengely mentén csak 0-tól 1-ig "tart". Ami ezen kívül kerül, azt levágja a DirectX.
1. megoldási ötlet: Hozzuk létre hátrébb a háromszöget! Módosítsuk a onCreateDevice-ban a létrehozást!
vs[0].v = D3DXVECTOR3(0,0.5f,0.5f); vs[1].v = D3DXVECTOR3( 0.5f,-0.5f,0.5f); vs[2].v = D3DXVECTOR3(-0.5f,-0.5f,0.5f);
Probléma: Így nem a tengelye körül forog a háromszög. (És még mindíg kimászik!)
2. megoldási ötlet: Maradjon mégis az eredeti helyén a háromszög, forgassuk meg, és utána toljuk hátrébb! Ehhez kell egy új mátrix, és a kettő szorzatát kell átadni a DirectX-nek.
void Triangle::onFrameMove( double fTime, float fElapsedTime ) { static const float rotSpd = 0.5f; D3DXMATRIX rot, trans; D3DXMatrixRotationY(&rot, (float)fTime*rotSpd); D3DXMatrixTranslation(&trans, 0, 0, 0.5f); rot *= trans; _device->SetTransform(D3DTS_VIEW, &rot); }
Megjegyzések:
- A szorzást spórolásból hajtottuk végre közvetlenül a rot mátrixon.
- Fontos a szorzás sorrendje! Fordítva pont ugyan azt kapnánk, mint az első kísérletnél. A transzformációk hatásai balról jobbra érvényesülnek. Itt például: Forgass, majd told el!
B) Miért tűnik el néha teljesen?
A hátlapeldobás miatt, amikor a háromszög "hátulról" látszik (amikor a csúcsainak a sorrendje az óra járásával ellentétes), akkor a rendszer nem rajzolja ki. Ez állítható a D3DRS_CULLMODE állapot segítségével.
_device->SetRenderState(D3DRS_CULLMODE, D3DCULL_NONE);
Rendben, hogy most belefér a háromszög a "világba", de akkor nem is használhatók a [0, 1]-en kívüleső pontok???
De igen, ha valamilyen vetítést alkalmazunk.
1. Kapcsoljuk ki a forgatást most egy darabig! (Pl. kommentezzük ki a forgatás kódját)
2. Rajzoljuk ki kétszer a hátomszöget, az egyiket mélyebbre, mint a másikat!
void Triangle::onFrameRender( double fTime, float fElapsedTime ) { D3DXMATRIX m; _device->Clear(0, 0, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER, D3DCOLOR_XRGB(107, 63, 160), 1, 0); if ( SUCCEEDED(_device->BeginScene()) ) { _device->SetStreamSource(0, _vbuffer, 0, sizeof(VERTEXFORMAT)); _device->SetFVF(FVF); D3DXMatrixTranslation(&m, -0.5f, 0, 0); _device->SetTransform(D3DTS_WORLD, &m); _device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); D3DXMatrixTranslation(&m, 0.5f, 0, 0.5f); _device->SetTransform(D3DTS_WORLD, &m); _device->DrawPrimitive(D3DPT_TRIANGLELIST, 0, 1); _device->EndScene(); } }
Futassuk a programot!
Hiába van az egyik háromszög hátrébb, mint a másik, mind kettő ugyanakkora.
Ez néha jó lehet (pl műszaki rajzoknál), de általában nem ezt szeretnénk, hanem perspektívikus ábrázolást.
3. Hozzunk létre egy perspektívikus transzformációt a onResetDevice-ban és adjuk át a DirectX-nek!
D3DXMATRIX pers; D3DXMatrixPerspectiveFovLH(&pers, 3.1415f*45/180, (float)getWidth()/getHeight(), 0.1f, 10); _device->SetTransform(D3DTS_PROJECTION, &pers);
Valami történt, de egyenlőre nem látszik, hogy mi is.
4. Vigyük hátrébb a nézőpontunkat! Ehhez ismét bővítsük a onResetDevice-ot!
D3DXMATRIX look; D3DXVECTOR3 eye = D3DXVECTOR3(0,0,-2); D3DXVECTOR3 at = D3DXVECTOR3(0,0,0); D3DXVECTOR3 up = D3DXVECTOR3(0,1,0); D3DXMatrixLookAtLH(&look, &eye, &at, &up); _device->SetTransform(D3DTS_VIEW, &look);
Megjegyzések:
- A D3DXMatrixPerspectiveFovLH 3. paramétere miatt a háromszögek nem torzulnak többé.
- A 4. lépés eltolását természtesen D3DXMatrixLookAtLH helyett D3DXMatrixTranslation-nel is meg lehet adni.