Ora parliamo dei buffer. Un buffer predisposto per i vertici si chiama vertex buffer.
Innanzitutto definiamo una nostra struttura per il vertice. Come definitito nel precedente tutorial useremo un formato vertice contenente posizione e colore.
struct VertexType
{
D3DXVECTOR3 position;
D3DXVECTOR4 color;
};
D3DXVECTOR sono dei tipi definiti in DirectX10; D3DXVECTOR3 contiene 3 float (xyz) mentre D3DXVECTOR4 invece ne contiene 4 (xyzw). Se ricordate nello shader avevamo definito la posizione come un float4 mentre qui ci sono 3 valori. Se ricordate il primo tutorial avevo spiegato che i vertici erano usati come omogenei (ultimo valore uguale ad 1). Se usiamo un vector3 passandolo ad un float4 nello shader ci penserà directX a passargli 1.
Un vertice richiede una descrizione tramite una struttura VertexLayout che permetterà di specificare a quale semantica associare ogni valore.
ID3D10InputLayout* vertexLayout;
D3D10_INPUT_ELEMENT_DESC layout[] =
{
{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D10_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D10_INPUT_PER_VERTEX_DATA, 0 },
};
Una descrizione è contenuta nella classe ID3D10InputLayout. Per prima cosa descriviamo i dati contenuti nel vertice. Nel nostro caso abbiamo 2 informazioni, la posizione che è un Vector3 ed il colore che è un vector4. Tale descrizione verrà dichiarata tramite un array di D3D10_INPUT_ELEMENT_DESC
La struttura Element_Desc è formato da questi campi
- SemanticName: nome da associare nello shader. Quel dato nel codice shader avrà questo nome
- SemanticIndex: ad ogni nome è possibile associare un indice (Position1,Position2 etc), utile per avere n elementi con lo stesso nome
- Format: formato, ossia che struttura ha l'elemento
- InputSlot: è possibile utilizzare molti slot contemporaneamente. Questo valore indica quale usare
- AlignedByteOffset: il punto nella zona di memoria da cui leggere i dati, espresso in byte
- InputSlotClass: il tipo di dato contenuto
- InstanceDataStepRate: questa informazione serve per fare l'instancing. Questo sarà un argomento avanzato da affrontare in futuro
Nel primo caso associamo i primi 12byte della struttura alla variabile "POSITION", specificando che si trova all'inizio della struttura (quindi offset 0) e che è formato da 3 float (R32G32B32_FLOAT). Il secondo caso invece sarà la variabile "COLOR", con formato R32G32B32A32_FLOAT perchè formato da float 4 ed avrà offset uguale a 12 perchè segue i primi 3 float della posizione. Dallo shader dovremo ora prendere la sua descrizione in modo da associare definitivamente vertice allo shader.
D3D10_PASS_DESC PassDesc;
effect->GetTechniqueByIndex(0)->GetPassByIndex( 0 )->GetDesc( &PassDesc );
Ed infine creiamo il nostro InputLayour.
HRESULT hr= device->CreateInputLayout( layout, 2, PassDesc.pIAInputSignature, PassDesc.IAInputSignatureSize, &vertexLayout );
La variabile vertexLayout contiene tutto il necessario per l'associazione. Ora possiamo creare il vertexbuffer che conterrà i nostri vertici. Dato che il nostro shader non fa altro che restituire la posizione dovremo usare valori compresi tra -1 ed 1 per poterli vedere.
ID3D10Buffer* vertexBuffer;
VertexType vertices[3];
vertices[0].position=D3DXVECTOR3(0.5F,-0.5F,0);
vertices[0].color=D3DXVECTOR4(1,0,0,0);
vertices[1].position=D3DXVECTOR3(-0.5F,-0.5F,0);
vertices[1].color=D3DXVECTOR4(0,1,0,0);
vertices[2].position=D3DXVECTOR3(0,0.5F,0);
vertices[2].color=D3DXVECTOR4(1,1,0,0);
D3D10_BUFFER_DESC bd;
bd.Usage = D3D10_USAGE_DEFAULT;
bd.ByteWidth = stride * vertexCount;
bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
bd.MiscFlags = 0;
D3D10_SUBRESOURCE_DATA InitData;
InitData.pSysMem = data;
HRESULT hr=device->CreateBuffer( &bd, &vertices, &vertexBuffer );
con queste istruzioni creiamo un buffer di tipo vertex buffer. Molto importante è il ByteWidth in cui si deve specificare la dimensione in byte del buffer (nel nostro caso la struttura ha un vector3 + un vector4 ossia 7 x 4 = 28byte che moltiplicati i 3 vertici fanno 84byte, valore appunto da assegnare. Un altro parametro fondamentale è pSysMem a cui passeremo i dati da inserire nel buffer (nel nostro caso l'array di vertici).
Ora che il buffer è pronto è possibile utilizzarlo.
device->IASetInputLayout(vertexLayout);
device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
device->IASetVertexBuffers( 0, 1, &vertexBuffer, &stride, &offset );
Queste 4 istruzioni impostano il buffer nel device. La prima imposta il tipo di layout, la seconda il tipo di figura da renderizzare (nel nostro caso un elenco di triangoli nonostante sia soltanto 1). Il terzo imposta il buffer (la posizione dello slot, il numero di buffer che contiene questo buffer, nel nostro caso 1, il buffer ed i valori stride del vertice ed offset, il punto dello buffer da cui leggere, 0 nel nostro caso).
Applicando lo shader ed impostando questi buffer tutto sarà pronto.
device->Draw(vertexCount,0);
Questa istruzione manderà a video il risultato mostrando il nostro triangolo. L'istruzione draw prende come parametri il numero dei vertici ed il vertice da dove iniziare il rendering.
Vi lascio il demo. Nel prossimo tutorial vi darò maggiori approfondimenti sugli effect ed i buffer per poi iniziare i tutorial sugli shader
Demo