DirectX9 - 4. návod - Světla
DirectX9 - 4. návod - Světla
4. Světla
Cíl
Příprava
InitD3D - Vytvoření vykreslovacího zařízení
InitGeometry - Vytvoření geometrie scény
Cleanup - Uvolnění alokovaných proměnných
SetupMatrices - Nastaví transformační matice
SetupLights - Nastaví materiál a světla scény
Render - Funkce pro vykreslení
MsgProc - Zachytávání a zpracování zpráv okna
WinMain - Vstupní bod naší aplikace
Poznámka na závěr
Příklad je zde ke staženíCíl
Vykreslování 3D geometrie je mnohem zajímavější, když jsou do scény přidány dynamické světla. Abychom ale použili taková světla v D3D, musíme provést několik věcí. Tak nejprve musíme objektu nastavit materiál (ten udává barvu povrchu, barvu odlesků, lesklost povrchu, atd). Poté musí geometrie objektu obsahovat tzv. normálové vektory jsou to kolmice k jednotlivým místům povrchu objektu), díky kterým se dá velmi rychle spočítat úhel dopadajícího světla a tudíž i míru osvětlení. Daný normálový vektor se váže k danému vertexu.Světla mohou být těchto typů: Směrové (directional, prochází celou scénou v jednom směru, např slunce), Bodové (point, světlo vychází z jednoho konkrétního bodu a šíří se všemi směry, např svíčka) a Kuželové (spotlight, světlo se šíří z bodu jedním směrem nejsilněji jinými směry postupně slaběji, např baterka). Posledním druhem světla, které stojí už trochu mimo, je ambientní (osvětluje všechno ve scéně bez ohledu na vzdálenost, úhel dopadu atd.)
nahoru
Příprava
Založíme si projekt (viz. Nultý tutorial). A můžeme začít psát zdrojový text... Nejdříve naincludujeme potřebnou DirectX hlavičku a připravíme si globální proměnné.
#include <d3dx9.h>
// Globální proměnné
LPDIRECT3D9 g_pD3D = NULL; // Nutné k vytvoření D3DDevice
LPDIRECT3DDEVICE9 g_pd3dDevice = NULL; // Naše vykreslovací zařízení
LPDIRECT3DVERTEXBUFFER9 g_pVB = NULL; // Buffer na uložení vertexů
// Struktura pro náš konkrétní vertex. K pozici v prostoru potřebujeme
// ještě onen normálový vektor
struct CUSTOMVERTEX
{
D3DXVECTOR3 position; // 3D pozice pro daný vertex
D3DXVECTOR3 normal; // Normálový vektor v daném místě povrchu
};
// Naše FVF, které popisuje výše zmíněnou strukturu vertexu
#define D3DFVF_CUSTOMVERTEX (D3DFVF_XYZ|D3DFVF_NORMAL)
nahoru
InitD3D - Vytvoření vykreslovacího zařízení
HRESULT InitD3D( HWND hWnd )V této funkci, na rozdíl od jejích verzí v předchozích příkladech, vytvoříme ještě z-buffer a na jejím konci ho také zapneme. Mimoto také vypneme culling, abychom viděli obě strany povrchu.
// Vytvoříme D3D objekt
if( NULL == ( g_pD3D = Direct3DCreate9( D3D_SDK_VERSION ) ) )
return E_FAIL;
// Nastavíme strukturu k vytvoření D3DDevice. Protože jíž budeme používat
// komplexnější geometrii, vytvoříme nově i z-buffer
D3DPRESENT_PARAMETERS d3dpp;
ZeroMemory( &d3dpp, sizeof(d3dpp) );
d3dpp.Windowed = TRUE;
d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD;
d3dpp.BackBufferFormat = D3DFMT_UNKNOWN;
d3dpp.EnableAutoDepthStencil = TRUE;
d3dpp.AutoDepthStencilFormat = D3DFMT_D16;
// Vytvoříme D3DDevice
if( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, hWnd,
D3DCREATE_SOFTWARE_VERTEXPROCESSING,
&d3dpp, &g_pd3dDevice ) ) )
{
return E_FAIL;
}
// Vypneme culling (potřebujeme totiž vidět obě strany povrchu objektu)
g_pd3dDevice->SetRenderState( D3DRS_CULLMODE, D3DCULL_NONE );
// A zapneme z buffer
g_pd3dDevice->SetRenderState( D3DRS_ZENABLE, TRUE );
return S_OK;
nahoru
InitGeometry - Vytvoření geometrie scény
HRESULT InitGeometry()V této funkci jsme si připravili v minulém příkladu jeden polygon. Nyní si již vytvoříme o něco komplexnější objekt, který ale ještě neumíme načítat ze souboru a proto si ho zatím pouze vygenerujeme algoritmem. Tím objektem bude pro jednoduchost válec. Při generování si krom pozice vypočítáme už i normálové vektory.
// Nejprve si vytvoříme vertex buffer
if( FAILED( g_pd3dDevice->CreateVertexBuffer( 50*2*sizeof(CUSTOMVERTEX),
0, D3DFVF_CUSTOMVERTEX,
D3DPOOL_DEFAULT, &g_pVB,
NULL )))
{
return E_FAIL;
}
// Nyní ho naplníme. Zde je pro jednoduchost zvoleno algoritmické
// vygenerování válce, včetně normálových vektorů. Jak načíst již
// připravené modely si ukážeme příště.
CUSTOMVERTEX* pVertices;
if( FAILED( g_pVB->Lock( 0, 0, (void**)&pVertices, 0 ) ) )
return E_FAIL;
for( DWORD i=0; i<50; i++ )
{
FLOAT theta = (2*D3DX_PI*i)/(50-1);
pVertices[2*i+0].position = D3DXVECTOR3(sinf(theta),-1.0f,cosf(theta));
pVertices[2*i+0].normal = D3DXVECTOR3(sinf(theta), 0.0f,cosf(theta));
pVertices[2*i+1].position = D3DXVECTOR3(sinf(theta), 1.0f,cosf(theta));
pVertices[2*i+1].normal = D3DXVECTOR3(sinf(theta), 0.0f,cosf(theta));
}
g_pVB->Unlock();
return S_OK;
nahoru
Cleanup - Uvolnění alokovaných proměnných
VOID Cleanup()I tato funkce byla podrobněji nastíněna v 1. příkladu. A oproti tomu minulému se nezměnilo opravdu nic.
if( g_pVB != NULL )
g_pVB->Release();
if( g_pd3dDevice != NULL )
g_pd3dDevice->Release();
if( g_pD3D != NULL )
g_pD3D->Release();
nahoru
SetupMatrices - Nastaví transformační matice
VOID SetupMatrices()Tato funkce byla již také vysvětlena. Starala se o nastavení tří matic (světa, pohledu a projekce) a to dělá stále. Konkrétně v tomto případě otáčí matici světa (tedy objekt) podle osy X, matici pohledu (tedy kameru) má stále na stejném místě a matice projekce je nastavená na zorný úhel Pi/4 radiánů, poměr stran 1:1, a vykreslovat budeme vše ve vzdálenosti od 1.0 do 100.0.
// Nastavení matice světa (resp. kam a jak umístit objekt)
D3DXMATRIXA16 matWorld;
D3DXMatrixIdentity( &matWorld );
D3DXMatrixRotationX( &matWorld, timeGetTime()/500.0f );
g_pd3dDevice->SetTransform( D3DTS_WORLD, &matWorld );
// Nastavíme matici pohledu (resp. kamery)
D3DXVECTOR3 vEyePt( 0.0f, 3.0f,-5.0f );
D3DXVECTOR3 vLookatPt( 0.0f, 0.0f, 0.0f );
D3DXVECTOR3 vUpVec( 0.0f, 1.0f, 0.0f );
D3DXMATRIXA16 matView;
D3DXMatrixLookAtLH( &matView, &vEyePt, &vLookatPt, &vUpVec );
g_pd3dDevice->SetTransform( D3DTS_VIEW, &matView );
// A nastavíme matici projekce (tedy jak se 3D scéna převede na 2D
// souřadnice okna)
D3DXMATRIXA16 matProj;
D3DXMatrixPerspectiveFovLH( &matProj, D3DX_PI/4, 1.0f, 1.0f, 100.0f );
g_pd3dDevice->SetTransform( D3DTS_PROJECTION, &matProj );
nahoru
SetupLights - Nastaví materiál a světla scény
VOID SetupLights()Zcela nová funkce. Tato má za úkol nastavit vše ohledně světel a metariálů ve scéně. Nejprve si připravíme materiál. V tomto případě má pouze ambientní a difuzní složku a je čistě bílý. Může být v jednu chvíli použit pouze jeden materiál. Pokud Vás teď napadá množství textur a barevných polygonů, je vždy třeba vykreslování takových objektů rozdělit na vykreslení jednotlivých sad polygonů se stejnými parametry. Později si to vysvětlíme, ale pro tentokrát se zatím musíme spokojit s tímto jednoduchým případem bez textur a rozličných materiálů.
Poté si přidáme dvě světla (žluté a červené). Obě jsou směrová, tedy svítí skrze celou scénu ve stejném směru a intenzitě. Druhé světlo si přidáme proto, abyste měli představu, jak se více světel ve scéně vytvoří. Můžeme mít až 8 světel naráz. Pokud jich potřebujeme více, musíme již použít pixel-shadery, ale to nás čeká až někdy v budoucnu.
S touto funkcí doporučuji hodně experimentovat, získáte tak o světlech lepší povědomí.
// Nastavení materiálu
D3DMATERIAL9 mtrl;
ZeroMemory( &mtrl, sizeof(D3DMATERIAL9) );
mtrl.Diffuse.r = mtrl.Ambient.r = 1.0f;
mtrl.Diffuse.g = mtrl.Ambient.g = 1.0f;
mtrl.Diffuse.b = mtrl.Ambient.b = 1.0f;
mtrl.Diffuse.a = mtrl.Ambient.a = 1.0f;
g_pd3dDevice->SetMaterial( &mtrl );
// Nastavíme žluté směrové světlo. A posledním řádkem ho zapneme.
D3DLIGHT9 light;
ZeroMemory( &light, sizeof(D3DLIGHT9) );
light.Type = D3DLIGHT_DIRECTIONAL;
light.Diffuse.r = 1.0f;
light.Diffuse.g = 1.0f;
light.Diffuse.b = 0.0f;
light.Direction = D3DXVECTOR3(0.707f, 0.707f, 0.0f);
g_pd3dDevice->SetLight( 0, &light );
g_pd3dDevice->LightEnable( 0, TRUE );
// Ještě přidáme jedno červené z jiného směru
light.Type = D3DLIGHT_DIRECTIONAL;
light.Diffuse.r = 1.0f;
light.Diffuse.g = 0.0f;
light.Diffuse.b = 0.0f;
light.Direction = D3DXVECTOR3(-0.707f, 0.0f, 0.707f);
g_pd3dDevice->SetLight( 1, &light );
g_pd3dDevice->LightEnable( 1, TRUE );
// Nakonec zapneme hardwarové osvětlování a stínování
g_pd3dDevice->SetRenderState( D3DRS_LIGHTING, TRUE );
// Nakonec přidáme ještě složku ambientního světla
g_pd3dDevice->SetRenderState( D3DRS_AMBIENT, 0x00202020 );
nahoru
Render - Funkce pro vykreslení
VOID Render()Již známá funkce. Je zde navíc čištění z-bufferu a zavolání funkce SetupLights, která nastaví materiály a světla ve scéně.
// Vyčistíme back buffer (do něho budeme kreslit) a ještě z buffer
g_pd3dDevice->Clear( 0, NULL, D3DCLEAR_TARGET|D3DCLEAR_ZBUFFER,
D3DCOLOR_XRGB(0,0,255), 1.0f, 0 );
// Začneme vykreslování
if( SUCCEEDED( g_pd3dDevice->BeginScene() ) )
{
// Nastavíme světla a materiály
SetupLights();
// Nastavíme matice
SetupMatrices();
// Vykreslíme obsah vertex bufferu
g_pd3dDevice->SetStreamSource( 0, g_pVB, 0, sizeof(CUSTOMVERTEX) );
g_pd3dDevice->SetFVF( D3DFVF_CUSTOMVERTEX );
g_pd3dDevice->DrawPrimitive( D3DPT_TRIANGLESTRIP, 0, 2*50-2 );
// A ukončíme vykreslování
g_pd3dDevice->EndScene();
}
// Nyní už jen zobrazíme obsah back bufferu na "obrazovku" (front bufferu)
g_pd3dDevice->Present( NULL, NULL, NULL, NULL );
nahoru
MsgProc - Zachytávání a zpracování zpráv okna
LRESULT WINAPI MsgProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )Na konec klasicky dvě funkce pro režii Windows. Tentokrát se zde opravdu ničeho nového nedočkáme a proto jen uvádím jejich zdrojový text.
switch( msg )
{
case WM_DESTROY:
Cleanup();
PostQuitMessage( 0 );
return 0;
}
return DefWindowProc( hWnd, msg, wParam, lParam );
nahoru
WinMain - Vstupní bod pro naši aplikaci
INT WINAPI WinMain( HINSTANCE hInst, HINSTANCE, LPSTR, INT )Poslední a hlavní funkce, po spuštění se volá právě ona.
// Registrace třídy okna
WNDCLASSEX wc = { sizeof(WNDCLASSEX), CS_CLASSDC, MsgProc, 0L, 0L,
GetModuleHandle(NULL), NULL, NULL, NULL, NULL,
"Dx9 Tutorial 4", NULL };
RegisterClassEx( &wc );
// Vytvoření okna aplikace
HWND hWnd = CreateWindow( "Dx9 Tutorial 4", "Dx9 4 - Světla",
WS_OVERLAPPEDWINDOW, 100, 100, 300, 300,
NULL, NULL, wc.hInstance, NULL );
// Inicializujeme Direct3D
if( SUCCEEDED( InitD3D( hWnd ) ) )
{
// Vytvoříme geometrii
if( SUCCEEDED( InitGeometry() ) )
{
// Zobrazíme okno
ShowWindow( hWnd, SW_SHOWDEFAULT );
UpdateWindow( hWnd );
// Vstoupíme do smyčky zpracování zpráv
MSG msg;
ZeroMemory( &msg, sizeof(msg) );
while( msg.message!=WM_QUIT )
{
if( PeekMessage( &msg, NULL, 0U, 0U, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
Render();
}
}
}
// Odregistrujeme třídu okna
UnregisterClass( "Dx9 Tutorial 4", wc.hInstance );
return 0;
nahoru
Poznámka na závěr
Tento text je téměř přesným opisem výsledného zdrojového textu. Vynechány byly částečně komentáře, které nahrazuje tento text. Zdrojový text ke stažení na konci této stránky ale ony komentáře obsahuje a umožní Vám to lepší orientaci i bez stálého připojení na tyto stránky.Dále byly vynechány složené závorky jednotlivých těl funkcí.
nahoru
Příklad je zde ke stažení
- Příklad #4 Src - Zdrojové texty s projektem pro Microsoft Visual Studio 2005
- Příklad #4 Exe - Spustitelný, již zkompilovaný program
nahoru
| Jan Zelený | 5.7.2007 |
© 2008 Jan Zelený | monade.cz





