Now we have shaders and the different buffers that we need to render. There's just one more crucial component before we can draw anything. We give our vertices to the API, and in the shader we request input data based on semantics, but how does the API know which parts of each vertex to match to each semantic?
This is done through an input layout, which is just a description of the layout of your vertex element, along with annotations to indicate which part matches which semantic. Just like most items in Direct3D11, you need to fill out a description structure before you can create the input layout with the device. A typical layout may look like the following code snippet:
const D3D11_INPUT_ELEMENT_DESC vertexDesc[] = { { "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 }, { "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }, };
Each of the D3D11_INPUT_ELEMENT_DESC
structures consists of the following pieces of data:
typedef struct D3D11_INPUT_ELEMENT_DESC { LPCSTR SemanticName; UINT SemanticIndex; DXGI_FORMAT Format; UINT InputSlot; UINT AlignedByteOffset; D3D11_INPUT_CLASSIFICATION InputSlotClass; UINT InstanceDataStepRate; } D3D11_INPUT_ELEMENT_DESC;
It begins with the semantic name, of which there are many different options. At the very least you require a
POSITION
attribute and everything else is optional. The most commonly used semantics are as follows:
POSITION
COLOR
NORMAL
TEXCOORD
TEXCOORD
refers to the texture coordinate on the model, which is a float2
/vector2
attribute describing the
UV coordinates at that vertex.
Note
UV coordinates refer to the part of the texture displayed at that point, and exist as a value between zero and one.
Semantics do not require that they only exist once within a vertex structure. Some, like the texture coordinate, can have multiple versions, referred to in HLSL using a number at the end of the semantic (TEXCOORD0
, TEXCOORD1
, and so on).
The second parameter in this structure lets you define which one this particular attribute refers to. In most cases this can just remain at zero; however, if you need multiple normal or texture coordinates, this would be the place to specify which index it has.
The format defines the data type stored in that attribute. You need to ensure you match the data type, and number of channels or parts to what it actually stores to ensure it is read properly. Some commonly used formats are the following:
DXGI_FORMAT_R32G32B32_FLOAT
DXGI_FORMAT_R32G32_FLOAT
DXGI_FORMAT_R32_FLOAT
There are also variations of these for signed and unsigned integers (SINT and UINT, respectively).
After that you need to define which Input Slot this attribute exists in. You can get away with setting this to zero in most cases. If you are working with multiple vertex buffers, you would use this parameter to specify which buffer this attribute applies to.
AlignedByteOffset
refers to the offset from the start of the element to the start of the attribute. If you are defining these attributes sequentially you can use the helper D3D11_APPEND_ALIGNED_ELEMENT
, which will automatically determine the correct offset based on the earlier description.
InputSlotClass
refers to the D3D11_INPUT_CLASSIFICATION
descriptor for the attribute. Unless you are working with instancing, this will always be D3D11_INPUT_PER_VERTEX_DATA
.
Finally InstanceDataStepRate
can just be set to zero if you aren't working with instancing. This value specifies the number of instances that should be drawn before advancing to the next element in the instance data buffer.
With all of this information we just need a quick call to the CreateInputLayout()
method on the device and we will have an input layout ready for use. The following is what that call looks like:
M_d3dDevice->CreateInputLayout( vertexDesc, 2, // Size of the vertex description array vsFile->Data, vsFile->Length, &m_inputLayout );
Here we started by passing the array of vertex descriptors, and telling the device how many descriptors exist within the array. Then we need to pass in the vertex shader file that this input layout will be mapped to. We need to do this because vertex shaders may expect different layouts and we need to ensure that everything matches up; otherwise, there will be a miscommunication and error.
Finally we receive an ID3D11InputLayout*
object that we can use when drawing to specify the layout to be used with the set shader.