Input Layout에 대해 알아보자. Vertex Buffer
와 Vertex Shader
를 사용하기 위해 설정을 해주어야 하는게 하나 더 있다. 바로 Input Layout
의 설정이다. Input Layout
은 정점 쉐이더에서 사용할 정점에 대해 특성을 부여해준다. 정점 구조체로 정점 버퍼를 만들었던 것을 보자. 정점은 다양한 형태로 구성될 수 있고 정점 버퍼 역시 다양한 형태를 따를 수 있다. 쉐이더에서는 이 정점의 구조에 대해 서로 독립되어 있는 구조이기 때문에 정점 데이터의 형태에 대해 알 수 없다. 이 정점 데이터의 형태를 쉐이더에게 알려주는 것이 바로 Input Layout
이다.
struct SimpleVertexCombined
{
XMFLOAT3 Pos;
XMFLOAT3 Col;
};
위 정점 구조체는 위치와 색을 포함한다. 그렇지만 데이터형으로 봤을 때는 그저 2개의 XMFLOAT3
데이터일 뿐이다.
D3D11_INPUT_ELEMENT_DESC 준비
D3D11_INPUT_ELEMENT_DESC
은 정점 데이터의 각 요소들의 특성을 설명해주는 구조체이다. 레퍼런스는 마이크로 소프트에 있다.
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
에서 기억해두어야 할 것이 바로 SemanticName
이다. 쉐이더 코드를 보자. PixelIn VS( float4 position : SV_POSITION, float4 color : COLOR )
를 보면 정점 쉐이더에서 파라메터를 받는데 SemanticName
을 사용하는 것을 알 수 있다. 즉, 정점 쉐이더에서 정점 버퍼의 내용을 전달 받을 때 하나의 정점 데이터에 있는 여러 요소들을 이 SemanticName
을 통해서 구분하는 것이다.
D3D11_INPUT_ELEMENT_DESC elements[] =
{
{ "SV_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
}
정점 구조체에서 정의하는 데이터가 위치와 색이므로 D3D11_INPUT_ELEMENT_DESC
은 그에 해당하는 2개를 배열로 준비해둔다.
Input Layout 만들기
Input Layout
객체를 만들기 위한 코드는 다음과 같다. 레퍼런스는 마이크로 소프트에 있다.
ID3D11InputLayout* InputLayout = nullptr;
D3D11Device->CreateInputLayout( &elements[ 0 ], ARRAYSIZE( elements ), compiledShader->GetBufferPointer(), compiledShader->GetBufferSize(), InputLayout );
여기에서 주의할 점은 파라메터로 컴파일된 쉐이더 객체가 필요하다는 것이다. 인풋 레이아웃의 정보와 실제 쉐이더 코드의 정보와 일치하는지 확인하는 용도이다.
Input Layout 연결 하기
생성된 Input Layout
객체도 렌더링 파이프라인에 연결해주어야 한다.
D3D11DeviceContext->IASetInputLayout( InputLayout );
주의점 – 인풋 레이아웃은 정점 데이터와 정확히 일치해야 할 것
정점 데이터와 인풋 레이아웃의 내용이 서로 다를 경우 대부분 의도와 다르게 동작한다. 어떻게 동작하는지 한 번 알아보자.
struct CRVertex
{
CRVector Position;
};
D3D11_INPUT_ELEMENT_DESC elements[] =
{
{ "SV_POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
};
위 케이스의 경우 일단 삼각형의 모양은 제대로 출력이 된다. 색상값은 제대로 출력되지 않는다.
struct CRVertex
{
CRVector Position;
};
D3D11_INPUT_ELEMENT_DESC elements[] =
{
{ "SV_POSITION", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{ "COLOR", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D11_APPEND_ALIGNED_ELEMENT, D3D11_INPUT_PER_VERTEX_DATA, 0 },
이 케이스부터 삼각형이 출력되지 않는다.

정점 쉐이더가 정점 버퍼에서 데이터를 참조하는 방식은 위와 같다.
첫번째 Input Layout Element
는 정점 버퍼의 시작 위치에서부터 데이터를 스트리밍 하고, 포맷의 크기만큼 오프셋을 건너뛰며 다음 데이터를 참조한다. 즉 포멧이 DXGI_FORMAT_R32G32B32_FLOAT
라면 12 Byte씩 건너뛰며 값이 쉐이더로 전달된다.
두번째 Input Layout Element
는 정점 버퍼의 시작 위치에서 이전 엘리먼트 포맷의 크기만큼 떨어진 위치에서부터 데이터를 스트리밍 하고, 포맷의 크기만큼 오프셋을 건너뛰며 다음 데이터를 참조한다. 즉 포멧이 DXGI_FORMAT_R32G32B32_FLOAT
라면 12 Byte씩 건너뛰며 값이 쉐이더로 전달된다.
물론 이런 부분들은 하드웨어 특성에 따라 혹은 기타 설정에 따라 달라질 수 있으므로 정확한 보장이 되지 않는 부분이므로 발생시켜서는 안된다. 하지만 내부에서 어떤 방식으로 동작하는지 오동작은 어떻게 이루어지는 한 번 톺아볼 필요가 있다.
이전글 : [DirectX11] 6. 삼각형 – Vertex Shader에 대해 알아보자.
다음글 : [DirectX11] 8. 삼각형 – Pixel Shader에 대해 알아보자.