Ara que ja tenim una
finestra on treballar, procedirem a pintar el nostre primer triangle de
Directx11. Per a poder dibuixar geometria necessitem crear i inicialitzar un
buffer de vèrtexs (vertex buffer). En altres llibreries gràfiques, com les
primeres versions d’OpenGL o de DirectX, si que era possible crear primitives
amb funcions específiques (ex: glBegin(GL_TRIANGLES) en OpenGL o DrawPrimitive
en DirectX). El fet de treballar amb buffers millora el rendiment considerablement,
és per això que Directx aposta per ells.
Així que dissenyarem la nostra pròpia
estructura per a guardar els vèrtexs dels polígons, en el nostre cas, del
triangle. L’anomenarem MyVertex:
struct MyVertex
{
float Position[2];
unsigned Color;
};
Alhora creem una matriu de vèrtexs a la qual
anomenem myVertices. Això ho fem per
poder crear el buffer de vèrtexs que ve a continuació.
MyVertex myVertices[] = {
{ 0.0f,
0.5f, 0xFF0000FF },
{ 0.3f, -0.5f, 0xFF00FF00 },
{ -0.3f, -0.5f,
0xFFFF0000 },
};
Per crear un buffer de vèrtex, s'omple
l'estructura D3D11_BUFFER_DESC, proporcionant diversos paràmetres com la mida
del buffer en bytes, i alguns flags, entre altres.
D3D11_BUFFER_DESC
bufDesc; //Creem la instància de buffer;
ZeroMemory&bufDesc,
sizeof(bufDesc)); //Inicialitzem el buffer amb zeros.
bufDesc.ByteWidth
= sizeof(MyVertex) * _countof(myVertices); //Definim la mida.
bufDesc.Usage =
D3D11_USAGE_IMMUTABLE;
bufDesc.BindFlags =
D3D11_BIND_VERTEX_BUFFER;
Els paràmetres que poden rebre tant Usage com BindFlags són els següents:
typedef enum D3D11_USAGE {
D3D11_USAGE_DEFAULT = 0,
D3D11_USAGE_IMMUTABLE = 1,
D3D11_USAGE_DYNAMIC = 2,
D3D11_USAGE_STAGING = 3,
} D3D11_USAGE;
D3D11_USAGE_DEFAULT = 0,
D3D11_USAGE_IMMUTABLE = 1,
D3D11_USAGE_DYNAMIC = 2,
D3D11_USAGE_STAGING = 3,
} D3D11_USAGE;
D3D11_USAGE_DEFAULT
Un recurs que requereix escriptura i lectura
per la GPU. Aquest acostuma a ser el més usat.
D3D11_USAGE_IMMUTABLE
Un recurs que només pot ser llegit per la GPU.
No pot ser escrit i no és completament accessible per tota la CPU. És
immutable, no pot ser canviat un cop inicialitzat.
D3D11_USAGE_DYNAMIC
Un recurs que és accessible tant per GPU
(només lectura) com per la CPU (només escriptura). És idoni si és tracta d’un
recurs que serà actualitzat per la CPU almenys un cop per frame.
D3D11_USAGE_STAGING
Un recurs que suporta còpies de la GPU a la
CPU.
typedef enum D3D10_BIND_FLAG {
D3D10_BIND_VERTEX_BUFFER = 0x1L,
D3D10_BIND_INDEX_BUFFER = 0x2L,
D3D10_BIND_CONSTANT_BUFFER = 0x4L,
D3D10_BIND_SHADER_RESOURCE = 0x8L,
D3D10_BIND_STREAM_OUTPUT = 0x10L,
D3D10_BIND_RENDER_TARGET = 0x20L,
D3D10_BIND_DEPTH_STENCIL = 0x40L
D3D10_BIND_VERTEX_BUFFER = 0x1L,
D3D10_BIND_INDEX_BUFFER = 0x2L,
D3D10_BIND_CONSTANT_BUFFER = 0x4L,
D3D10_BIND_SHADER_RESOURCE = 0x8L,
D3D10_BIND_STREAM_OUTPUT = 0x10L,
D3D10_BIND_RENDER_TARGET = 0x20L,
D3D10_BIND_DEPTH_STENCIL = 0x40L
} D3D10_BIND_FLAG;
D3D11_BIND_VERTEX_BUFFER
D3D11_BIND_INDEX_BUFFER
D3D11_BIND_CONSTANT_BUFFER
Aquest
flag no es pot combinar amb cap altre bind flag.
D3D11_BIND_SHADER_RESOURCE
D3D11_BIND_STREAM_OUTPUT
Uneix un buffer de sortida per l’stream-output
stage.
D3D10_BIND_RENDER_TARGET
Uneix una textura com a render-target per l’output-merger
stage.
D3D11_BIND_DEPTH_STENCIL
Uneix una textura com a depth-stencil per l’output-merger
stage.
Cal també omplir l'estructura
D3D11_SUBRESOURCE_DATA. És un mecanisme nou i molt útil introduït a DX10
que permet passar les dades inicials per al recurs creat. Un recurs amb
D3D11_USAGE_IMMUTABLE ha de ser inicialitzat d'aquesta manera ja que les seves
dades no es poden canviar després de la creació.
D3D11_SUBRESOURCE_DATA
bufData; // En creem una instància.
ZeroMemory(&bufData,
sizeof(bufData)); // Inicialitzem amb
zeros.
bufData.pSysMem
= myVertices; // Dades inicials.
Després de completar les estructures
D3D11_SUBRESOURCE_DATA i D3D11_BUFFER_DESC opcionals, cridem a la funció CreateBuffer:
HRESULT CreateBuffer(
[in] const
D3D11_BUFFER_DESC *pDesc,
[in,
optional] const D3D11_SUBRESOURCE_DATA *pInitialData,
[out,
optional] ID3D11Buffer **ppBuffer
);
On:
pDesc [in]
Tipus const D3D11_BUFFER_DESC*. Punter a l’estructura de
dades D3D11_BUFFER_DESC que descriu el buffer. (Creada anteriorment).
pInitialData [in,
optional]
Tipus const D3D11_SUBRESOURCE_DATA*. Punter a
l’estructura de dades D3D11_SUBRESOURCE_DATA que descriu la inicialització de
les dades. Usa NULL per assignar l’espai només (no és possible usar NULL si el
flag d’Usage, comentat anteriorment, és D3D11_USAGE_IMMUTABLE).
ppBuffer [out,
optional]
Tipus ID3D11Buffer**. Adreça d’un punter a la interfície
ID311Buffer per l’objecte buffer crear. Estableix aquest paràmetre a NULL per
validar els altres paràmetres d’entrada.
Un cop cridada la funció hem creat el buffer
de vèrtex i al mateix temps l’hem omplert amb les dades transmeses a partir del
punter a D3D311_SUBRESOURCE_DATA.
El següent pas és crear shaders. DX11 ens obliga a escriure shaders, fins i tot si volem un simple triangle sense
textura. Una vegada més, es fa molt difícil el començament per a principiants,
però al mateix temps reforça els bons hàbits en la programació.
Afortunadament, no és necessari proporcionar
tots els tipus de shaders suportats per DX11. N’hi ha molts : Vertex Shader (VS), Shader Hull i Domain
Shader (nous a DX11, relacionats amb la tessel·lació), Geometry Shader (existent des de DX10, pot generar una nova geometria),
Pixel Shader (PS) i Compute Shader (per
al còmput de propòsit general). Per al nostre triangle n’hi ha prou amb un VS
i un PS.
Un cop tenim el codi dels shaders, aquests s’han de compilar. Per
fer-ho cridem a:
On:
pSrcFile [in]
Tipus LPCTSTR. Nom de l’arxiu que conté el codi del shader.
pDefines [in]
Tipus const
D3D10_SHADER_MACRO*. Opcional. Punter a un array
de definicions macro. Si no s’usa, cal definir pDefines a NULL.
pInclude [in]
Tipus LPD3D10INCLUDE. Opcional. Punter a una interfície
per al maneig d’arxius include. No
s’ha de col·locar a NULL si el fitxer de
shaders conté un #include.
pFunctionName [in]
Tipus LPCSTR. Nom de la funció de dins de l’arxiu on
s’escriu el shader que comença
l’execució del shader. En un mateix
arxiu de shader hi pot haver una
funció diferent pel píxel shader i
una altra pel vèrtex shader. Amb
aquest paràmetre es descriu quin és el nom de la funció que correspon al shader en qüestió. En el cas que només
hi hagués funcions per un tipus de shader,
aquest paràmetre perd el sentit i s’ha de col·locar a NULL.
pProfile [in]
Tipus LPCSTR. Cadena que específica el model de shader, pot ser shader model 2, 3, 4 o 5.
Flags1 [in]
Tipus UINT. Tipus de compilació de shader. Si s’està compilant un efecte i no un shader, cal col·locar aquest flag
a 0.
Flags2 [in]
Tipus UINT. Tipus de compilació d’ efecte. Si s’està
compilant un shader i no un efecte,
cal col·locar aquest flag a 0.
pPump [in]
Tipus ID3DX11ThreadPump*. Un punter a una interfície thread pump.
ppShader [out]
Tipus ID3D10Bloob**. Punter a la memòria que conté el shader compilat.
ppErrorMsgs [out]
Tipus ID3D10Bloob**. Punter a la memòria que conté una
llista d’errors i advertències que poden haver ocorregut durant la compilació.
pHResult [out]
Tipus HRESULT*. Punter del valor de retorn. Pot ser NULL.
Si el paràmetre pPump no és NULL, pHResult ha de ser una localització de
memòria vàlida fins que acabi l’execució.
Aquesta funció carrega l’arxiu on està escrit el shader i el compila. Si la compilació és
satisfactòria, s’obté un objecte ID3D10Blob amb el codi del shader compilat. És
només un buffer amb algunes dades binaries existents a la memòria RAM. A
continuació cal cridar, per separat, a la següent funció:
HRESULT CreateVertexShader(
[in] const void *pShaderBytecode,
[in] SIZE_T BytecodeLength,
[in] ID3D11ClassLinkage *pClassLinkage,
[out] ID3D11VertexShader **ppVertexShader
);
On:
pShaderBytecode [in]
Tipus const
void*. Punter al shader compilat.
BytecodeLength [in]
Tipus SIZE_T. Mida del vèrtex shader compilat.
pClassLinkage [in]
Tipus ID3D11ClassLinkage*. Punter a la classe linkage interface. El valor pot ser
NULL.
ppVertexShader [out]
Tipus ID3D11VertexShader**. Adreça d’un punter a la
interfície ID3D11VertexShader. Si és
NULL els altres paràmetres seran validats i comprovats si passen la validació
amb S_FALSE enlloc d’S_OK.
per crear un objecte ID3D11VertexShader real
d'aquest codi.
Pel cas del Pixel shader cal utilitzar altre cop les funcions esmentades pel vèrtex shader, però aquest cop amb petites
diferències a l’hora de passar els paràmetres especificant les raons del píxel shader. Aquestes diferències poden ser
apreciades al codi font del programa, descrit al final d’aquest capítol.
Ara només falta un objecte abans de poder
renderitzar el tan esperat triangle. Es diu disseny d'entrada (InputLayout) i representa la descripció
de la nostra estructura de vèrtex.
Hem de definir un array de D3D11_INPUT_ELEMENT_DESC
dient que els nostres vèrtexs tenen posició del tipus float [2] i color de tipus unsigned,
d’aquesta manera:
D3D11_INPUT_ELEMENT_DESC inputDesc[] = {
{
"POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA, 0 },
{
"COLOR", 0,
DXGI_FORMAT_R8G8B8A8_UNORM, 0,
D3D11_APPEND_ALIGNED_ELEMENT,
D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
Un cop omplerta la descripció, cal cridar a la
següent funció:
HRESULT CreateInputLayout(
[in] const D3D11_INPUT_ELEMENT_DESC
*pInputElementDescs,
[in] UINT
NumElements,
[in] const void *pShaderBytecodeWithInputSignature,
[in] SIZE_T BytecodeLength,
[out] ID3D11InputLayout **ppInputLayout
);
On:
pInputElementDescs [in]
Tipus const D3D11_INPUT_ELEMENT_DESC*. Array de
l’input-assembler amb els diferents tipus de dades del programa. Cada tipus
està descrit dins de la constant. Creat anteriorment.
NumElements [in]
Tipus UINT. El nombre d’elements dins l’array anterior.
pShaderBytecodeWithInputSignature [in]
Tipus const void*. Un punter al vèrtex shader compilat.
BytecodeLength [in]
Tipus SIZE_T. Mida del shader compilat.
ppInputLayout [out]
Tipus ID3D11InputLayout**. Un punter a l’objecte
input-layout a crear.
per a crear un objecte ID3D11InputLayout.
Finalment configurem els següents estats de
render a la variable tipus ID3D11DeviceContext. S’hi poden configurar més, però
pel nostre triangle amb aquests n’hi ha prou. Més endavant en veurem més,
com el ID3D11DepthStencilState.
- IASetVertexBuffers – per obligar a usar el vertex buffer.
- IASetInputLayout – per informar de l’estructura dels
nostres vèrtexs.
- IASetPrimitiveTopology – per dir que volem dibuixar una D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
- VSSetShader, PSSetShader – per fixar els shaders.
Val a dir que hi ha altres tipus de primitives
com ara:
D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED
L'estadi IA no s'ha inicialitzat amb una
topologia primitiva. L'estadi IA no funcionarà correctament si no una
topologia primitiva es defineix.
D3D11_PRIMITIVE_TOPOLOGY_POINTLIST
Interpretar les dades dels vèrtexs d'una
llista de punts.
D3D11_PRIMITIVE_TOPOLOGY_LINELIST
Interpretar les dades dels vèrtexs d'una
llista de línies.
D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP
Interpretar les dades dels vèrtexs d'una tira
de línies.
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
Interpretar les dades dels vèrtexs d'una
llista de triangles.
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP
Interpretar les dades dels vèrtexs com una
tira de triangles.
D3D11_PRIMITIVE_TOPOLOGY_LINELIST_ADJ
Interpretar les dades dels vèrtexs de la
llista de les línies amb les dades d'adjacència.
D3D11_PRIMITIVE_TOPOLOGY_LINESTRIP_ADJ
Interpretar les dades dels vèrtexs de la tira
d'acord amb les dades d'adjacència.
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST_ADJ
Interpretar les dades dels vèrtexs dels
triangles de la llista amb les dades d'adjacència.
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLESTRIP_ADJ
Interpretar les dades dels vèrtexs que tira de
triangles amb les dades d'adjacència.
D3D11_PRIMITIVE_TOPOLOGY_1_CONTROL_POINT_PATCHLIST
Interpretar les dades dels vèrtexs de la
llista d'actualitzacions.
I finalment fem la representació real, per la
qual cosa per cada frame que cridem:
• ID3D11DeviceContext::
ClearRenderTargetView - per esborrar la finestra amb una mica de color.
• ID3D11DeviceContext:: Draw - per dibuixar el triangle.
• IDXGISwapChain:: Present - per visualitzar el fotograma.
Això s’escriu a la funció OnFrame,
predefinida per l’API de Windows.
A continuació hi ha escrit el codi font
sencer del programa que crea un triangle:
El resultat final és el següent triangle:
Per a descarregar-se un exemple fes click en aquest enllaç.

No hay comentarios:
Publicar un comentario