Graphics Pipeline
Shader Stage 들이 직접 코딩할 수 있는 부분이고, 나머지는 API 내부에서 처리해준다.
우리가 다룰 부분은 Vertex Shader와 Pixel Shader이다.
- Vertex shader: 각 vertex에 적용할 수 있는 transformation이나 lighting 등을 수행
- Pixel(fragment) shader : 각 pixel마다 색상을 계산하여 처리

Input-Assembler(IA) Stage
◇ CPU가 제공한 정보들을 primitive 형태로 모아준다.
- 일반적으로 primitive는 삼각형을 의미한다.
- 관련해서 우리가 해줘야 할 일들은 다음과 같다.
- 1) Create input buffers
- 2) Create the input-layer object
- 3) Bind objects to the input-assembler stage
- 4) Specify the primitive type
- 5) Call draw methods
IA Stage - 1) Create input buffers
IA Stage에 전달해야 하는 버퍼 : Vertex buffer, Index buffer
◇ Create a Vertex Buffer
1. Vertex 구조체를 만든다.
2. Vertex에 기하 정보(position)를 담은 메모리를 할당한다.
// header
struct SimpleVertex
{
XMFLOAT3 Pos;
};
// global variables
ID3D11Buffer* g_pVertexBuffer = nullptr;
///////////////////////////////
SimpleVertex sVertices[] =
{
{XMFLOAT3 (0.0f, 0.5f, 0.5f)},
{XMFLOAT3 (0.5f, -0.5f, 0.5f)},
{XMFLOAT3 (-0.5f, -0.5f, 0.5f)},
};
3. Buffer Description(버퍼 정보들)을 만들어준다.
- ByteWidth는 buffer의 크기, BindFlags는 어떤 buffer인지를 나타냄.
4. SUBRESOURCE_DATA를 만든다.
- 실제 데이터 정보에 대한 포인터를 전달해줌.
D3D11_BUFFER_DESC bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(SimpleVertex) * 3;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;
D3D11_SUBRESOURCE_DATA initData = {};
initData.pSysMem = sVertices;
5. CreateBuffer() 함수를 호출한다.
- D3DDevice에 buffer를 만드는 것을 요청한다.
- 함수 인자 값으로 앞서 만든 buffer description, subresource data, 그리고 buffer의 주소를 넣어준다.
hr = g_pd3dDevice->CreateBuffer(
&bd,
&initData,
&g_pVertexBuffer
);
if (FAILED(hr))
return hr;
◇ Create a Index Buffer
같은 방식으로 Index Buffer도 만들어준다.
1. Buffer를 만들고 index 정보를 저장한다.
2. Buffer description을 생성한다. 이때 flag를 Index buffer로 해야한다.
ID3D11Buffer* g_pIndexBuffer = nullptr;
///////////////////////////////
WORD sIndices[] = { 0, 1, 2 };
bd = {};
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(WORD) * 3;
bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
bd.CPUAccessFlags = 0;
3. SUBRESOURCE_DATA를 만든다.
4. CreateBuffer() 함수를 호출한다.
initData = {};
initData.pSysMem = sIndices;
hr = g_pd3dDevice->CreateBuffer(
&bd,
&initData,
&g_pIndexBuffer
);
if (FAILED(hr))
return hr;
IA Stage - 2) Create the Input-Layout Object
◇ Input-Layout Object는 IA Stage의 input에 대한 정보(앞에서 만든 buffer 들)를 전달해주는 역할을 한다.
- Input data에 대한 설명을 해주는 input-element description을 만들어 준다.
- 하나의 vertex는 position, normal, uv 등의 element 정보를 가질 수 있음. 하지만 지금은 position만 사용할 예정이기 때문에 이를 첫번째 파라미터로 명시해줬다.
- 2번째 파라미터 : semantic index
- 3번째 파라미터 : element 데이터의 format = 입력 데이터 타입에 따라 명시해주면 된다.
ID3D11InputLayout* g_pVertexLayout = nullptr;
///////////////////////////////
ID3DBlob* pVertexShaderBlob = nullptr;
D3D11_INPUT_ELEMENT_DESC layouts[] =
{
{"POSITION",
0,
DXGI_FORMAT_R32G32B32_FLOAT,
0,
0,
D3D11_INPUT_PER_VERTEX_DATA,
0
},
};
UINT uNumElements = ARRAYSIZE(layouts);
- 4번째 파라미터 : input slot
IA Stage에는 여러 개의 vertex buffer가 들어갈 수 있다.(= 오브젝트가 여러 개인 경우)
각 n개의 vertex buffer를 하나의 슬롯으로 할당하여 IA의 input으로 들어간다. 따라서 Input-layout object에서는 몇 번째 slot을 다룰지에 대한 정보가 필요하다. 이게 4번째 파라미터 값이다.
- 5번째 파라미터 : offset = 데이터를 어디서부터 읽을지
이후 D3DDevice에세 InputLayout을 만들어달라고 요청한다.
- CreateInputLayout() 파라미터 : layout / element 수 / vertex shader의 포인터 / vertex shader의 크기 / out 파라미터
- Blob : shader 코드 데이터를 가리킨다고 생각하면 된다.
hr = g_pd3dDevice->CreateInputLayout(
layouts,
uNumElements,
pVertexShaderBlob->GetBufferPointer(),
pVertexShaderBlob->GetBufferSize(),
&g_pVertexLayout
);
pVertexShaderBlob->Release();
if (FAILED(hr))
return hr;
IA Stage - 3) Bind Objects to the IA Stage
◇ 지금까지 만들어준 input buffer들과 input layout object를 IA stage에 binding 해준다.
-
Stride : vertex buffer에서 하나의 vertex 정보 크기 = SimpleVertex 구조체의 크기
- Offset : vertex buffer에서 시작 지점
UINT uStride = sizeof(SimpleVertex);
UINT uOffset = 0;
g_pImmediateContext->IASetVertexBuffers(
0, // slot
1, // buffer의 개수
&g_pVertexBuffer, // vertex buffer
&uStride, // stride
&uOffset // offset
);
g_pImmediateContext->IASetIndexBuffer(
g_pIndexBuffer, // index buffer
DXGI_FORMAT_R16_UINT, // format of the index data
0 // offset
);
///////////////////////////
g_pImmediateContext->IASetInputLayout(g_pVertexLayout);
IA Stage - 4) Specify the Primitive Type
g_pImmediateContext->IASetPrimitiveTopology(
D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST
);
IA Stage - 5) Call Draw Method
- Binding이 된 후 draw 함수를 호출해서 그려 달라고 요청한다.
- Draw 요청을 하게 되면, DirectX가 그래픽스 파이프라인을 쭉 실행하여 화면에 출력해 준다.
- Draw 함수 종류
-
Draw(): Index buffer 없이 vertex에 따라 draw
- DrawIndexed(): index buffer를 이용하여 index 순서대로 vertex를 그려준다.
-
Vertex Shader(VS) Stage
◇ 모든 vertex에 transformation, skinning, per-vertex lighting 과 같은 처리들을 해준다.
- vertex 개수만큼 반복 실행된다. = VS의 input도 vertex 하나이고, output도 vertex 하나이다.
- VS는 하나의 vertex를 입력으로 하고, 하나의 vertex를 출력하는 함수라고 생각하면 된다.
- 그리고 앞으로 이 함수를 우리가 정의해 줄 예정이다.
Vertex Shader
- Shader는 High-Level Shading Language(HLSL)이라는 언어로 작성된다. C++이 아닌 HLSL로 따로 작성하는 이유는 GPU에서 Matrix와 Vector 연산을 훨씬 빠르게 처리할 수 있기 때문이다.
- VS에서 가장 중요한 일은 좌표계 사이를 transform 해주는 것이다.

- input과 output은 float4 형태의 벡터(x,y,z,w)
- POSITION : input parameter의 sementic
- SV_POSITION : 함수의 반환값. 이때 SV는 System Value를 의미한다. = Clip-space의 position
Rasterizer Stage
◇ 3차원 좌표계에 있는 vertex들을 화면에 그리기 위한 2D pixel(raster image)로 변환해준다.
3D graphic을 2D pixel에 mapping 시키는 단계
◇ Rasterization
- 화면은 2차원 grid로 pixel로 되어있고 각 pixel들은 색깔을 가지고 있다. 이 pixel들을 칠해서 모니터에 화면이 만들어진다.
- 아래 그림에서 왼쪽은 3개의 vertex로 구성된 삼각형이고 이를 rasterization 하면 오른쪽 그림처럼 된다.
- GPU는 어떤 pixel이 삼각형이 포함되고, 칠해져야 하는지 여부를 계산하게 된다. 이후 칠해져야 하는 pixel들만 pixel shader 단계를 진행하게 된다.

Pixel Shader(PS) Stage
◇ 최종 pixel의 색깔을 결정하는 단계
- Per-pixel 단위로 Lighting이나 post-processing 같은 처리를 해준다.
- Input : pixel의 색상을 결정할 정보들
- Output : 계산 후 결정된 최종 pixel 색상 정보
float4 PS(float4 Pos : SV_POSITION) : SV_Target
{
return float4(1.0f, 1.0f, 0.0f, 1.0f);
}
- 마찬가지로 input과 output은 float 4 타입이다.
- Input의 semantic : SV_POSITION = vertex shader의 output
- Output으로 color(RGBA)를 내보낸다. = SV_Target.
Creating the Shaders
DWORD dwShaderFlags = D3DCOMPILE_ENABLE_STRICTNESS;
#if defined (DEBUG) || defined(_DEBUG)
dwShaderFlags |= D3DCOMPILE_DEBUG;
dwShaderFlags |= D3DCOMPILE_SKIP_OPTIMIZATION;
#endif
ID3DBlob* pErrorBlob = nullptr;
hr = D3DCompileFromFile(
“VS.hlsl”, // FileName
nullptr, // shader macros
nullptr, // include files
“VS”, // Entry point
“vs_5_0”, // shader target
dwShaderFlags, // flag1
0, // flag2
ppBlobOut, // ID3DBlob out
&pErrorBlob // error blob out
);
컴파일 한 뒤에는 Vertex Shader Object를 만들어준다. 이 과정 역시 Device가 요청하게 된다.
- 파라미터 : 컴파일된 shader의 포인터 / shader의 size / Class link(nullptr) / shader를 가리키는 포인터( ID3D11VertexShaer*)
ID3D11VertexShader* g_pVertexShader = nullptr;
///////////////////////////////
hr = g_pd3dDevice->CreateVertexShader(
pVertexShaderBlob->GetBufferPointer(),
pVertexShaderBlob->GetBufferSize(),
nullptr,
&g_pVertexShader);
if (FAILED(hr))
{
pVertexShaderBlob->Release();
return hr;
}
Pixel Shader도 마찬가지로 만들어준다.
ID3D11PixelShader* g_pPixelShader = nullptr;
///////////////////////////////
hr = g_pd3dDevice->CreatePixelShader(
pPixelShaderBlob->GetBufferPointer(),
pPixelShaderBlob->GetBufferSize(),
nullptr,
&g_pPixelShader);
if (FAILED(hr))
{
pPixelShaderBlob->Release();
return hr;
}
Setting the Shaders
이제 만들어둔 shader를 pipeline에 binding 해준다.
g_pImmediateContext->VSSetShader(g_pVertexShader, nullptr, 0);
g_pImmediateContext->PSSetShader(g_pPixelShader, nullptr, 0);
다음 포스팅에서는 3D Space와 Transformation 과정에 대해 자세히 다룰 예정이다.
Reference KHU 강형엽 교수님 강의의 실습 수업을 수강하며 정리한 내용입니다.
'DX11' 카테고리의 다른 글
| [DX11] 3D 렌더링 엔진 만들기 (2) - Window/D3D Initializaiton (0) | 2026.04.13 |
|---|---|
| [DX11] 3D 렌더링 엔진 만들기 (1) - Introduction (0) | 2026.04.12 |