PDA

Pogledaj cijelu verziju : TUTORIAL >> DirectX 10 (ten) & C++



RayDX
03-07-2008, 16:52
DirectX 10 predstavljen je kao revolucija gaming industrije i katalizator za next-gen grafiku. Dakako, Microsoftov PR je malo pretjerao, no moram priznati, ovo vrijeme koje sam proveo proučavajući navedeni API, sa programerske strane naletio sam na mnoge zanimljive i pohvalne preinake koje su zatekle DirectX kao grafički API. A i sama arhitektura dozvoljava uz malo truda postići zapanjujuće rezultate. Volio bih napomenuti da je DirectX10 od početka kovan da radi samo u simbiozi sa Vistom, dakle, svaki pokušaj da se kôd kompilira i pokrene izbacit će error jer nemate potrebne .dllove ukoliko niste na Visti. Ja se osobno koristim dokumentacijom DirectXa ( June 2008 ) i svoje sposobnosti da to posložim kako treba, pa ću pokušati vama to u obliku tutoriala jer nema drugačijih kvalitetnih izvora. Jedino možda 2 knjige dostupne na Amazonu.

DirectX 10

http://pce.hostili.com/images/Directx10nvidiahvoo.jpg

Pa vrijeme je da malo zavirimo u novi svijeta DirectXa 10. Prema definiciji danoj od Microsofta Direct3D10 grafički pipeline je kompletni redizajn stare arhitekture, izgrađen ispočetka i u hardveru i sofveru da bi mogao pogoniti novu generaciju igara i 3D multimedijalnih aplikacija. Zvuči poprilično epski i primamljivo, a nije toliko ni daleko od istine. Također koristi Windows Vista Display Driver Model koji omogućuje različita poboljšanja i performanse poput recimo virtualne GPU memorije. Ne zamarajte se ovim, kasnije ću pojasniti detalje.

Mnogi se pitaju što zapravo taj novi DX10 API donosi i kako će poboljšati razvoj odnosno igranje igara na novom, sad već pristupačnom dx10 hardveru? Naravno, tu je i pitanje koja je razlika između dx9 i dx10. Na to ćemo dati odgovor uskoro. Što se tiče novosti, jedna od najbitnijih je uvođenje gemetry shadera koji omogućuje developerima da procesiraju cjelovite primitivne objekte jer ne djeluje više na pojedinom vertexu, nego na više njih odjednom. Cijeli pipeline podijeljen je na 5 neizmjenjivih state objekata, samim time omogućujući poboljšanu i bržu konfiguraciju pipelinea. Od ostalog možemo spomenuti termine poput konstantnih spremnika, novi tipovi resursa, runtime podijeljen u slojeve, potpuna integracija HLSLa, povećan broj render targeta, tekstura, samplera i još jedna lijepa stvar je nepostojeća granica za duljina shadera.

Dakako, ljudi koji nisu upoznati sa DirectX API-jem ovo zvuči kao aramejski jezik, no to ćemo nastojati promijeniti. Osim ovih novina, mnogi se pitaju, što mi to DirectX10 nudi, a da dx9 to već nema. Pa pogledajmo što se dogodilo sa DX10-kom naspram onoga što smo viđali u DX9-ci. Da skratimo priču na bitne elemente, promjene su sljedeće:

- fixed function pipeline uklonjen
- D3D CAPS uklonjen
- stroži menadžment kod pristupa resursima, statesima uređaja, shader konstantama i sl.
- API ulazna točka promijenjena zbog uvođenja virtualne GPU memorije (Map() umjesto Lock())
- primitive topology ( način na koji se povezuju vertexi) je sad eksplicitni state ("izoliran" od Draw poziva)
- Eksplicitne shader konstante su sada spremljene u konstante spremnike
- stvaranje shadera u potpunosti odrađeno u HLSLu, te sam HLSL compiler nalazi se u DLLu Direct3D10ke
- nova programabilna faza - geometry shader
- BeginScene i EndScene uklonjeno

Uklanjanje fixed function pipelinea

Iako je Direct3D 9 podržavao u potpunosti programabilni pipeline, mnoštvo dijelova ostalo je povezano uz fixed function pipeline. Upoznavati vas sa fixed function pipelineom više, dakle, nije potrebno, tako da nećemo ulaziti u to. Većina bitnih elemenata koji su prije bili dostupni kroz fixed function pipeline preseljeni su u shadere.

-----------------------------------

Vjerojatno dolazite do zaključka da je DX10 sada u potpunosti nekompatibilan sa DX9 softverom i hardverom jer je cijela arhitektura promijenjena. Razlog da DirectX10 nije došao na XP je očigledan, no Microsoft nudi nekoliko rješenja kod dizajna enginea tako da se inicijalizacija i korištenje oba API-a podijeli u slojeve kao što se npr. radi apstrakcija render enginea npr. u Irrlichtu gdje se mogu koristiti dvije implementacije za renderiranje poput DX9ke ili OpenGLa. Samo je malo čudno da se dva APIa iz isto obitelji više ne poklapaju. Ali to će se u budućnosti pokazati kao dobra ili jako jako loša stvar.

Ovo bi trebao biti dovoljno kratak i solidan uvod u DirectX10, no prije nego li se bacimo na samo programiranje, posvetit ćemo jedan post detaljnijoj arhitekturi DirectX10 API-ja te geometry shadera. Definitivno ima mnoštvo stvari za pokriti ali natrpati korisnika informacijama koje odmah neće razumjeti nije nikakvo postignuće, tako da ćemo sistematično podijeliti ovaj API kako budemo učili i pokrivat nove koncepte na što jednostavniji i čišći način.

cerberi
04-07-2008, 10:28
naaajs, ovo će biti više nego...korisno i zanimljivo, jer kako si i sam rekao, nema previše (u biti nema ni jednog) free, boljeg tutoriala za dx10 osim onih od microsofta koje dobiješ uz dx sdk, a to je više manje samo inicijalizacija dx-a...

svaka ti čast Ray, samo tako nastavi... :bravo:

RayDX
19-08-2008, 22:36
Postavimo najprije neke stvari kako spada. Ja definitivno nisam guru što se tiče DirectXa 10, niti imam posebno velikog iskustva sa navedenim API-jem. Tijekom pisanja ovog tutoriala, definitivno ću i ja mnogo toga naučiti, no probat ću iskoristiti ono što znam da svima pojasnim šture stvari koje se dobiju uz DirectX dokumentaciju. A nakon toga ću nastaviti sa jačim stvarima. Imam već dobru bazu za početi tutorial, dx10 sdk tutoriali koje ću refinirati i liniju po liniju objašnjavati, što čemu služi i tako. S time na umu, nastavimo.

Ono što trebate znati/imati:

Prosječno znanjeS- C++ i osnove Win32 API-ja
CompilerS- Visual Studio ili besplatna varijanta navedenog, Visual C++ Express Edition.
Operacijski sustavS- Windows Vista
DownloadS- DirectX SDK i postaviti ga u compileru za korištenje
DodatnoS- neko piće i lagana glazba u pozadini
HardwareS- DirectX10-capable grafička kartica

OSNOVNI FRAMEWORK


// Početni framework - main.cpp

#include <windows.h>

// Globalne varijable
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;


// Prototipi funkcija
HRESULT InitWindow ( HINSTANCE hInstance, int nCmdShow );
LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM );


// Ulazna točka u aplikaciju
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;

// Glavna petlja, poruke
MSG msg = { 0 };
while( GetMessage( &msg, NULL, 0, 0 ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}

return ( int )msg.wParam;
}


// Inicijalizacija prozora
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Ispunjavanje WNDCLASSEX strukture
WNDCLASSEX wcex;
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"WindowClass";
wcex.hIconSm = 0;

// Registriranje ispunjene klase
if( !RegisterClassEx( &wcex ) )
return E_FAIL;

// Napravi prozor aplikacije
g_hInst = hInstance;

// RECT setup
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );

// Napravi prozor
g_hWnd = CreateWindow( L"WindowClass",
L"Direct3D10 Tutorial - Osnovni framework",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
rc.right - rc.left,
rc.bottom - rc.top,
NULL,
NULL,
hInstance,
NULL );


if( !g_hWnd )
return E_FAIL;

ShowWindow( g_hWnd, nCmdShow );

return S_OK;
}


// Funkcija koja se poziva kad aplikacija zaprimi novu poruku
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{

PAINTSTRUCT ps;
HDC hdc;

switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;

case WM_DESTROY:
PostQuitMessage( 0 );
break;

default:
return DefWindowProc( hWnd, message, wParam, lParam );
}

return 0;
}

Ovo je osnova koju ćemo koristiti za naše avanture sa Desetkom. Ako vam je ovo nepoznato, a znate C++, preporučam da malo proučite Win32 API, imate moj tutorial o osnovama istog u DirectX 9 tutorialima. Ukoliko vam neke stvari ovdje budu nepoznate u ovom kôdu, slobodno se obratite ovdje, pa ću vam objasniti sve što vas zanima. Da bi ovaj navedeni kôd radio, morate napraviti Win32 projekt.

New project -> Win32 Project... I odaberite jednostavno empty u wizardu.

Ukoliko sve bude išlo okej, ovo bi trebali dobiti:

http://www.imagesforme.com/out.php/i131347_pic.jpg

A sad. Idemo se koncentrirati na Direct3D10, shall we : )

SkunK
19-08-2008, 22:42
:bravo:

RayDX
20-08-2008, 01:22
Welcome to the world of DirectX 10 programming

http://img258.imageshack.us/img258/3812/picture2ug3.png

Tradicionalno počinjemo sa sličkom vezanom uz novogeneracijski hardware i DirectX10. Naravno, radi se o pompoznom Crysisu. I ne, nećete ništa slično raditi. Ako i nakon ovog želite nastaviti, onda ste spremni prihvatiti izazove koji su pred vama. Imamo osnovni framework koji ćemo nadograđivati te smo obdareni svim alatima i potrebnim znanjem da počnemo. Upalite compiler, ubacite projekt kojeg ste napravili i idemo. Prvo što trebate napraviti je dodati biblioteke u postavkama projekta. Specifično, radi se o .lib-ovima: d3d10.lib i d3dx10.lib.

Project -> <Ime solucije> properties -> Linker -> Input ili jednostavno Alt+F7 pa Linker-> Input

http://www.imagesforme.com/out.php/i131393_pic2.jpg

Odlično. Idemo sada disecirati kôd!


// Direct3D10 init - main.cpp

// Includes, ne zaboravite dodati .lib-ove
#include <windows.h>
#include <d3d10.h>
#include <d3dx10.h>

Zanemarujući komentare:

windows.hS- header file koji nam omogućuje razvoj windows aplikacija
d3d10.hS- Direct3D 10 header file koji nam omogućuje stvaranje živopisnih aplikacija
d3dx10.hS- Direct3Dx 10 header file, pruža pomoć kod razvoja


// Globalne varijable
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D10_DRIVER_TYPE g_driverType = D3D10_DRIVER_TYPE_NULL;
ID3D10Device* g_pd3dDevice = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
ID3D10RenderTargetView* g_pRenderTargetView = NULL;

HINSTANCE g_hInstSi HWND g_hWndSsu nam već poznati, handle za instancu i handle za prozor.

D3D10_DRIVER_TYPE g_driverTypeS
Najjednostavnije rečeno, device-driver type. Dakle, određuje hoće li device biti "pogonjen" hardverom ili softverom. Sa tipovima ću vas kasnije upoznati, a zasad evo definicija:


typedef enum D3D10_DRIVER_TYPE
{
D3D10_DRIVER_TYPE_HARDWARE = 0,
D3D10_DRIVER_TYPE_REFERENCE = 1,
D3D10_DRIVER_TYPE_NULL = 2,
D3D10_DRIVER_TYPE_SOFTWARE = 3,
} D3D10_DRIVER_TYPE;

ID3D10Device* g_pd3dDevice
Globalna varijabla kojom definiramo naš D3D uređaj kojeg ćemo koristiti za renderiranje i stvaranje D3D resursa. Navedenog ćemo kasnije proslijediti pri stvaranju uređaja, jer kao što vidite, zasad je postavljen na NULL jer je potrebno pravilno inicijalizirati uređaj.

IDXGISwapChain* g_pSwapChain
Ovim deklariramo interface prema našem swap chainu, jedan zgodušan dodatak DirectXu, ovo nam služi tako da uzima par površinea (surface), backbuffere na koje će spremati podatke te ih kasnije swapati i prezentirati konačnu sliku na zaslonu.

ID3D10RenderTargetView* g_pRenderTargetView
Još jedno sučelje koje ćemo koristiti za identificiranje render-target podresursa, tj. Direct3D treba negdje renderirati, tako da jednostavno moramo mu ponuditi mjesto gdje će to raditi, a pošto DirectX ima toliko načina da reprezentira grafičke podatke (bufferi različiti), specifično traži render-target, nešto što mu programer da kao označeno mjesto za iscrtavanje.


// Prototipi funkcija
HRESULT InitWindow ( HINSTANCE hInstance, int nCmdShow );
HRESULT InitDevice ();
void Cleanup ();
void Render ();
LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM );

Sumnjam da je ovdje potrebno previše objašnjavanja. Prototipi naših funkcija da C++ zna "da su tu". Imamo InitWindow() za inicijalizaciju prozora, InitDevice() za inicijalizaciju D3D10 uređaja i swap chaina, Cleanup() za počistiti kasnije, Render() za prljav posao iscrtavanja te WndProc() koji procesira poruke koje aplikacija dobiva.


// Ulazna točka u aplikaciju
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;

if( FAILED( InitDevice() ) )
{
Cleanup();
return 0;
}

// Glavna petlja, poruke
MSG msg = { 0 };
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
Render();
}
}
Cleanup();

return ( int )msg.wParam;
}

Dakle, u glavnoj funkciji naše aplikacije pozivamo funkcije za inicijalizaciju prozora, uređaja, postavljamo petlju za poruke... Onda imamo poziv za Render(); unutar loopa jer da bi se sve vrtilo u stvarnosti, ta funkcija se stalno mora pozivati i osvježavati prozor sa novim grafičkim podatcima. Cleanup se aktivira u slučaju da propadne inicijalizacija uređaja, kao FAIL_SAFE sustav, te kada se aplikacija gasi.


// Inicijalizacija prozora
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Ispunjavanje WNDCLASSEX strukture
WNDCLASSEX wcex;
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"WindowClass";
wcex.hIconSm = 0;

// Registriranje ispunjene klase
if( !RegisterClassEx( &wcex ) )
return E_FAIL;

// Napravi prozor aplikacije
g_hInst = hInstance;

// RECT setup
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );

// Napravi prozor
g_hWnd = CreateWindow( L"WindowClass",
L"Direct3D10 Tutorial - Inicijalizacija, D3D10",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
rc.right - rc.left,
rc.bottom - rc.top,
NULL,
NULL,
hInstance,
NULL );


if( !g_hWnd )
return E_FAIL;

ShowWindow( g_hWnd, nCmdShow );

return S_OK;
}

Standardna inicijalizacija prozora, pretpostavlja se da ste upoznati s njom. Nadam se :)
Ako vam nije nešto jasno, slobodno pitajte.

InitDevice();

Evo nas do mesa aplikacije, zanimljivih stvari. Nema smisla čekati... Ajmo.


HRESULT hr = S_OK;;

RECT rc;
GetClientRect( g_hWnd, &rc );
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
UINT createDeviceFlags = 0;

HRESULT hr = S_OK;;
Ovo je generični HRESULT za uspjeh.

RECT rc;
Stvaramo RECT strukturu ( Win32 API, GDI )

GetClientRect( g_hWnd, &rc );
Ova funkcija traži dva parametra, handle prema našem prozoru te adresu maloprije stvorene RECT strukture, rc. Služi da vrati podatke o veličini klijent područja aplikacije, a radi to vrlo jednostavno, dohvati gornji lijevi dio i donji desni. Pošto su koordinate relativne gornjem lijevom kutu, gornji lijevi kut je naravno 0, 0.

UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
Stvaramo dvije varijable tipa unsigned integera, width i height te jednostavnim kalkulacijama dolazimo do veličine prozora (naš slučaj je 640x480, kao rezultat)

UINT createDeviceFlags = 0;
Nemamo nikakvih specifičnih flagova, tako da ovo postavljamo na 0.


D3D10_DRIVER_TYPE driverTypes[]S=
{
D3D10_DRIVER_TYPE_HARDWARE,
D3D10_DRIVER_TYPE_REFERENCE,
};

UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0]S);

Ovdje postavljamo driverTypove koje želimo probati napraviti, preferiramo da prvo bude hardverski, a ako to propadne, onda ćemo stvoriti REF uređaj. To je u biti softversko renderiranje. Navedeni posao ćemo obaviti koristeći for petlju kasnije u kôdu.

UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0]S);
Izračunavamo broj driverTypeova koje smo stavili u enumeraciju.


DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );

sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = true;

Swap chain je zadužen da prezentira renderirani i spremni back buffer na surface/površinu koju poznajemo kao front buffer tj. ono što upravo vidimo, klijent prozor naše aplikacije ili možda cijeli zaslon ako smo u fullscreen modeu. Da bi radio kako spada potrebno je popuniti DXGI_SWAP_CHAIN_DESC strukturu da ima dovoljno informacija za obavljanje svog posla. Prvi dio kôda služi da stvorimo samu strukturu te sa ZeroMemory() funkcijom postavljamo je na NULL.

sd.BufferCount = 1;S- broj backbuffera, 1 zasad
sd.BufferDesc.Width = width;S- širina bacbuffera, koju smo izračunali naravno već
sd.BufferDesc.Height = height;S- visina backbuffera, također izračunali
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;S- 8 + 8 + 8 + 8 = 32... 32-bitni D3D10 format u kojem smo rezervirali 8 bita za R, G, B i A
sd.BufferDesc.RefreshRate.Numerator = 60;S- predstavlja gornji dio našeg refresh broja... 60
sd.BufferDesc.RefreshRate.Denominator = 1;S- predstavlja donji dio našeg refresh broja... 1
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;S- kažemo strukturi kako ćemo koristiti backbuffer
sd.OutputWindow = g_hWnd;S- dajemo handle prozora
sd.SampleDesc.Count = 1;S- multi-sampling zasad ne želimo, stavljamo 1...
sd.SampleDesc.Quality = 0;S- ...a ovdje 0.
sd.Windowed = true;S- dajemo na znanje da će biti u prozor-modeu : )


for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];

hr = D3D10CreateDeviceAndSwapChain( NULL,
g_driverType,
NULL,
createDeviceFlags,
D3D10_SDK_VERSION,
&sd,
&g_pSwapChain,
&g_pd3dDevice );
if( SUCCEEDED( hr ) )
break;
}
if( FAILED( hr ) )
return hr;

Vrlo jednostavan dio kôda, kao što smo već spominjali, koristimo for petlju da prošišamo kroz strukturu da prvo pokušamo napraviti hardware pogonjen device, a ukoliko to propadne, ostaje nam REF deviceType. Prvi parametar zasad postavimo na NULL, drugi na trenutni driverType, u ovoj petlji može biti jedino HARDWARE ili REFERENCE u ovom slučaju. Treći ostavite na NULL, a dalje proslijedite createDeviceFlags (kojeg smo stavili na 0 jer nemamo "želja" ). D3D10_SDK_VERSION stavljamo da uređaj zna o kojoj SDK verziji se radi. Dalje ostavljamo adrese swap chain descriptiona kojeg smo popunili maloprije, g_pSwapChaina (interface, globalne varijable, remember?) i interface za uređaj koji ćemo koristiti.


// Napravi render target view
ID3D10Texture2D* pBackBuffer;

hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), ( LPVOID* ) &pBackBuffer );
if( FAILED( hr ) )
return hr;

hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
if( FAILED( hr ) )
return hr;

g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );

ID3D10Texture2D* pBackBuffer;
Prvo napravimo pokazivač za backbuffer kojeg ćemo koristiti da dobijemo samu adresu pointera da prčkamo s njime.

hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), ( LPVOID* ) &pBackBuffer );
Koristeći sada funkcionalni swap chain interface, pozivamo funkciju GetBuffer() koja uzima 3 parametra, kojima vas sada neću zamarati. Bitno je da je prvi 0, drugi postavljeni da vrati uuid od ID3D10Texture2D te treći da daje adresu našeg pointera gdje ćemo dobiti backbuffer za korištenje.

hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
Kroz inteface uređaja dolazimo do funkcije za stvaranje RenderTargetViewa gdje dajemo pointer backbuffera kojeg smo stvorili maloprije, drugi parametar na null te treći, naravno, adresa naše globalne varijable, interfacea za RenderTargetView. Nakon toga pozivamo funkciju Release() da zagasimo kratki život pBackBuffera... Vječno zahvalni, dakako.

g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );
Ova funkcija služi nam da postavimo koliko render targeta ćemo imati... Imat ćemo 1, dajemo adresu našeg RenderTargetViewa, a treći parametar stavljamo NULL. Sad je skoro sve spremno za vatromet.


D3D10_VIEWPORT vp;
vp.Width = width;
vp.Height = height;
vp.MaxDepth = 1.0f;
vp.MinDepth = 0.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pd3dDevice->RSSetViewports( 1, &vp );

Većina ovog kôda samo sebe objašnjava, no ajmo proletjeti. Prva dva postavljaju visinu i širinu, sljedeća dva minimalnu dubinu i maksimalnu ( najjednostavnije 0.0f i 1.0f ), a zadnja dva dijela ove strukture su TopLeftX i TopLeftY, gdje stavljamo 0.

g_pd3dDevice->RSSetViewports( 1, &vp );
Ovom funkcijom postavljamo koliko ćemo viewportsa ima imati, u ovom slučaju 1 te prosljeđujemo adresu popunjene D3D10_VIEWPORT strukture.

Render();


void Render()
{

// oboji backbuffer u crno
float ClearColor[4]S= { 0.0f, 0.0f, 0.0f, 0.0f };
g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
g_pSwapChain->Present( 0, 0 );

}

float ClearColor[4]S= { 0.0f, 0.0f, 0.0f, 0.0f };
Vrlo jednostavna funkcija, prvo stvorimo polje (array) tipa float, 4 članova (0+3) za svaki dio: red, green, blue i alpha. Zbog jednostavnosti, stavljamo sve na 0.0f, a to će nam dati awesome boju... Črnu.

g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
Ovom funkcijom dajemo naredbu da se očisti backbuffer koji je, s poštovanjem, sahranjen duboko u RenderTargetViewu :), te kao drugi parametar prosljeđujemo polje/array sa color informacijom da "ofarbamo" backbuffer.

g_pSwapChain->Present( 0, 0 );
Sada pozivamo Present() funkciju da bi naš swapchain interface prezentirao sadržaj backbuffera kojeg smo upravo "ofarbali" na zaslon korisnika. Samo proslijedite dvije 0 kao parametre, kasnije ćemo objasniti detaljnije.


void Cleanup()
{

// Ukoliko ima što za počistiti, počisti.
if( g_pd3dDevice ) g_pd3dDevice->ClearState();
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();

}




// Funkcija koja se poziva kad aplikacija zaprimi novu poruku
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{

PAINTSTRUCT ps;
HDC hdc;

switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;

case WM_DESTROY:
PostQuitMessage( 0 );
break;

default:
return DefWindowProc( hWnd, message, wParam, lParam );
}

return 0;
}

Ove dvije funkcije su poprilično jednostavne, sa drugom biste trebali biti odavno upoznati, a prva jednostavno provjerava ostatke od programa jesu li aktivni, i ukoliko jesu, daje naredbu funkcijom Release() da tiho odu u ponor gdje svi dobri bitovi idu. Uf, nadam se da je dosta. Evo što bi trebalo biti nakon što uspješno kompilirate:

http://www.imagesforme.com/out.php/i131424_YAY.jpg

Nadam se da ste uživali, a mogu vam reći da je ovo tek početak : )
Živjeli : )

Skoro zaboravih, cjelokupni kôd:


// Direct3D10 init - main.cpp

// Includes, ne zaboravite dodati .lib-ove
#include <windows.h>
#include <d3d10.h>
#include <d3dx10.h>

// Globalne varijable
HINSTANCE g_hInst = NULL;
HWND g_hWnd = NULL;
D3D10_DRIVER_TYPE g_driverType = D3D10_DRIVER_TYPE_NULL;
ID3D10Device* g_pd3dDevice = NULL;
IDXGISwapChain* g_pSwapChain = NULL;
ID3D10RenderTargetView* g_pRenderTargetView = NULL;


// Prototipi funkcija
HRESULT InitWindow ( HINSTANCE hInstance, int nCmdShow );
HRESULT InitDevice ();
void Cleanup ();
void Render ();
LRESULT CALLBACK WndProc ( HWND, UINT, WPARAM, LPARAM );


// Ulazna točka u aplikaciju
int WINAPI wWinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPWSTR lpCmdLine, int nCmdShow )
{
if( FAILED( InitWindow( hInstance, nCmdShow ) ) )
return 0;

if( FAILED( InitDevice() ) )
{
Cleanup();
return 0;
}

// Glavna petlja, poruke
MSG msg = { 0 };
while( WM_QUIT != msg.message )
{
if( PeekMessage( &msg, NULL, 0, 0, PM_REMOVE ) )
{
TranslateMessage( &msg );
DispatchMessage( &msg );
}
else
{
Render();
}
}
Cleanup();

return ( int )msg.wParam;
}


// Inicijalizacija prozora
HRESULT InitWindow( HINSTANCE hInstance, int nCmdShow )
{
// Ispunjavanje WNDCLASSEX strukture
WNDCLASSEX wcex;
wcex.cbSize = sizeof( WNDCLASSEX );
wcex.style = CS_HREDRAW | CS_VREDRAW;
wcex.lpfnWndProc = WndProc;
wcex.cbClsExtra = 0;
wcex.cbWndExtra = 0;
wcex.hInstance = hInstance;
wcex.hIcon = 0;
wcex.hCursor = LoadCursor( NULL, IDC_ARROW );
wcex.hbrBackground = ( HBRUSH )( COLOR_WINDOW + 1 );
wcex.lpszMenuName = NULL;
wcex.lpszClassName = L"WindowClass";
wcex.hIconSm = 0;

// Registriranje ispunjene klase
if( !RegisterClassEx( &wcex ) )
return E_FAIL;

// Napravi prozor aplikacije
g_hInst = hInstance;

// RECT setup
RECT rc = { 0, 0, 640, 480 };
AdjustWindowRect( &rc, WS_OVERLAPPEDWINDOW, FALSE );

// Napravi prozor
g_hWnd = CreateWindow( L"WindowClass",
L"Direct3D10 Tutorial - Inicijalizacija, D3D10",
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT,
CW_USEDEFAULT,
rc.right - rc.left,
rc.bottom - rc.top,
NULL,
NULL,
hInstance,
NULL );


if( !g_hWnd )
return E_FAIL;

ShowWindow( g_hWnd, nCmdShow );

return S_OK;
}

// Inicijalizacija uređaja i swap chaina
HRESULT InitDevice()
{
HRESULT hr = S_OK;;

RECT rc;
GetClientRect( g_hWnd, &rc );
UINT width = rc.right - rc.left;
UINT height = rc.bottom - rc.top;
UINT createDeviceFlags = 0;

D3D10_DRIVER_TYPE driverTypes[]S=
{
D3D10_DRIVER_TYPE_HARDWARE,
D3D10_DRIVER_TYPE_REFERENCE,
};

UINT numDriverTypes = sizeof( driverTypes ) / sizeof( driverTypes[0]S);

DXGI_SWAP_CHAIN_DESC sd;
ZeroMemory( &sd, sizeof( sd ) );

sd.BufferCount = 1;
sd.BufferDesc.Width = width;
sd.BufferDesc.Height = height;
sd.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
sd.BufferDesc.RefreshRate.Numerator = 60;
sd.BufferDesc.RefreshRate.Denominator = 1;
sd.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;
sd.OutputWindow = g_hWnd;
sd.SampleDesc.Count = 1;
sd.SampleDesc.Quality = 0;
sd.Windowed = true;

for( UINT driverTypeIndex = 0; driverTypeIndex < numDriverTypes; driverTypeIndex++ )
{
g_driverType = driverTypes[driverTypeIndex];

hr = D3D10CreateDeviceAndSwapChain( NULL,
g_driverType,
NULL,
createDeviceFlags,
D3D10_SDK_VERSION,
&sd,
&g_pSwapChain,
&g_pd3dDevice );
if( SUCCEEDED( hr ) )
break;
}
if( FAILED( hr ) )
return hr;

// Napravi render target view
ID3D10Texture2D* pBackBuffer;

hr = g_pSwapChain->GetBuffer( 0, __uuidof( ID3D10Texture2D ), ( LPVOID* ) &pBackBuffer );
if( FAILED( hr ) )
return hr;

hr = g_pd3dDevice->CreateRenderTargetView( pBackBuffer, NULL, &g_pRenderTargetView );
pBackBuffer->Release();
if( FAILED( hr ) )
return hr;

g_pd3dDevice->OMSetRenderTargets( 1, &g_pRenderTargetView, NULL );

D3D10_VIEWPORT vp;
vp.Width = width;
vp.Height = height;
vp.MaxDepth = 1.0f;
vp.MinDepth = 0.0f;
vp.TopLeftX = 0;
vp.TopLeftY = 0;
g_pd3dDevice->RSSetViewports( 1, &vp );

return S_OK;

}

void Render()
{

// oboji backbuffer u crno
float ClearColor[4]S= { 0.0f, 0.0f, 0.0f, 0.0f };
g_pd3dDevice->ClearRenderTargetView( g_pRenderTargetView, ClearColor );
g_pSwapChain->Present( 0, 0 );

}

void Cleanup()
{

// Ukoliko ima što za počistiti, počisti.
if( g_pd3dDevice ) g_pd3dDevice->ClearState();
if( g_pRenderTargetView ) g_pRenderTargetView->Release();
if( g_pSwapChain ) g_pSwapChain->Release();
if( g_pd3dDevice ) g_pd3dDevice->Release();

}




// Funkcija koja se poziva kad aplikacija zaprimi novu poruku
LRESULT CALLBACK WndProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam )
{

PAINTSTRUCT ps;
HDC hdc;

switch( message )
{
case WM_PAINT:
hdc = BeginPaint( hWnd, &ps );
EndPaint( hWnd, &ps );
break;

case WM_DESTROY:
PostQuitMessage( 0 );
break;

default:
return DefWindowProc( hWnd, message, wParam, lParam );
}

return 0;
}

RIDER32
20-08-2008, 15:31
Svaka cast :pray: Mozda jednog dana procitam :D
A val cu stic procitat ovih dana :P

Sony Ericsson
21-01-2009, 16:42
Ray, da sam ja ti - bio bih milijunaš :pray:

SkunK
21-01-2009, 18:04
Ray, da sam ja ti - bio bih milijunaš :pray:


Maaa daj, Ray moj Ray nemoze ispraviti bug u svom forumu pa nemogu tamo postat svoj Binary tutorial jer forum ne zeli prihvatiti 1=11 :)

Luka
21-01-2009, 19:42
erm.. probao sam sad, 1=11 normalno radi

Nee Joe
21-01-2009, 19:45
Ray, svaka čast. Toliko truda si nikad ne bi dao koliko ti, a rezultati su savršeni. :bravo:

SkunK
21-01-2009, 20:32
erm.. probao sam sad, 1=11 normalno radi


Imao je problema s 1=11 i 11=1 javljao je error. Ajde ako hoces, iz PCPlay tu podforuma kopiraj sadržaj "Binary po ReiKu" i probaj ga postati na DoubleBuffer vidit ces da ces dobit error.