43.정점과 입력 배치
Direct3D의 정점은 공간적 위치 이외에 추가적인 자료(특성)를 가질 수 있다. 원하는 자료를 가진 커스텀 정점 형식을 만들려면 형식에 맞는 구조체를 정의하고, 정점 구조체의 각 성분이 어떤 용도인지 Direct3D에게 알려 주어야 한다. 이런 정보를 Direct3D에게 알려주는 수단이 ID3D11InputLayout 형식의 입력 배치(input layout) 객체이다. 입력 배치는 D3D11_INPUT_ELEMENT_DESC 구조체들로 이루어진 배열로 구축한다. 이 배열의 각 원소는 정점 구조체의 각 성분을 서술하는 역할을 하기 때문에 정점 구조체의 성분이 두 개라고 하면 구조체의 배열의 원소도 두 개여야 한다. D3D11_INPUT_ELEMENT_DESC 구조체는 d3d11.h에서 찾을 수 있다.
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;
1.SemanticName : 성분에 부여된 문자열 이름. 정점 셰이더(VS)에서 의미소(semantic) 이름으로 쓰이므로 반드시 유효한 변수 이름이여야 한다. 나는 의미소보다 시맨틱이 편하므로 시맨틱으로 부른다. 정점 구조체의 성분을 정점 셰이더 입력 서명과 대응시키는 역할을 한다. 대소문자를 구분하지 않는다.
2.SemanticIndex : 시맨틱에 부여된 인덱스이다. 개별적인 시맨틱 이름을 부여하는 대신 인덱스을 통해서 각각을 구별할 수 있다. 예를 들어 POSTION 시맨틱을 두 개를 사용한다면 POSITION0, POSTION1처럼 선언하는게 아니라 동일하게 POSITION을 사용하고, 0, 1을 가진다. 셰이더 코드에서 인덱스가 지정되지 않은 시맨틱은 인덱스가 0인 시맨틱으로 간주된다.
3.Format : 정점 성분의 자료 형식을 나타낸다.
4.InputSlot : 성분의 자료가 공급될 정점 버퍼 슬롯의 인덱스다. Direct3D는 16개의 정점 버퍼 슬롯를 GPU로 넘길 수 있다. 즉, 정점 버퍼중에 몇 번째 정점 버퍼를 사용할 것인지 결정한다.
5.AlignedByteOffset : 이전 정점 성분과 바이트 단위 거리. 일일히 계산할 수 없기에 보통 D3D11_APPEND_ALIGNED_ELEMENT으로 한다.
6.InputSlotClass : 입력 원소를 정점의 자료로서 공급할 것인지 인스턴스의 자료로서 공급할 것인지를 결정한다.
- D3D11_INPUT_PER_VERTEX_DATA : 입력 원소는 정점에 대한 자료이다.
- D3D11_INPUT_PER_INSTANCE_DATA : 입력 원소는 인스턴스에 대한 자료이다.
7.InstanceDataStepRate : 인스턴스별 자료 원소 하나 당 그릴 인스턴스 개수이다. 정점 자료의 경우에는 0을 설정한다. 즉, InputSlotClass에 D3D11_INPUT_PER_VERTEX_DATA을 넣는 경우 0을 설정한다.
두 정점 구조체 Vertex1과 Vertex2가 있을 때 아래처럼 입력 배치 배열을 각각 사용하면 된다.
struct Vertex1
{
XMFLOAT3 Pos;
XMFLOAT4 Color;
};
struct Vertex2
{
XMFLOAT3 Pos;
XMFLOAT3 Normal;
XMFLOAT2 Tex0;
XMFLOAT2 Tex1;
};
//Vertex1용
D3D11_INPUT_ELEMENT_DESC desc1[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0 }
}
//Vertex2용
D3D11_INPUT_ELEMENT_DESC desc2[] =
{
{"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{"NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{"TEXCOORD", 0, DXGI_FORMAT_R32G32_FLOAT, 0, 24, D3D11_INPUT_PER_VERTEX_DATA, 0 },
{"TEXCOORD", 1, DXGI_FORMAT_R32G32_FLOAT, 0, 32, D3D11_INPUT_PER_VERTEX_DATA, 0 }
}
입력 배치 배열을 완성했으면 입력 배치를 위한 ID3D11InputLayout 인터페이스를 얻었다면, ID3D11Device::CreateInputLayout 메서드를 호출해서 입력 배치를 생성한다. d3d11.h에서 찾을 수 있다.
HRESULT CreateInputLayout(
const D3D11_INPUT_ELEMENT_DESC* pInputElementDescs,
UINT NumElements,
const void* pShaderBytecodeWithInputSignature,
SIZE_T BytecodeLength,
ID3D11InputLayout** ppInputLayout);
1.pInputElementDescs : 정점 구조체를 서술하는 D3D11_INPUT_ELEMENT_DESC들의 배열
2.NumElements : D3D11_INPUT_ELEMENT_DESC 배열의 원소 개수
3.pShaderBytecodeWithInputSignature : 컴파일된 셰이더에 대한 포인터. 요소 배열에 대한 유효성 검사되는 입력 서명이 포함되어 있다.
4.BytecodeLength : 컴파일된 셰이더의 크기
5.ppInputLayout : 생성된 입력 배치를 반환하는 포인터