Notice
Recent Posts
Recent Comments
Link
«   2026/02   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
Tags
more
Archives
Today
Total
관리 메뉴

MOONSUN

[D3D11] 01. RenderingTriangle 삼각형 그리기 본문

D3D

[D3D11] 01. RenderingTriangle 삼각형 그리기

MoonSun_v 2025. 9. 29. 15:27

 

앞에서는 D3D11 인터페이스 객체들에 대해 간단하게 알아보았는데,

이제부터는 진짜로 그 객체들을 활용해서 결과물을 만들어 보도록 하겠다. 

 

Direct3D 11의 기본 렌더링 파이프라인 동작을 이해하기 위해, 화면에 삼각형을 그려보도록 하겠다! 

실행 화면

 

 

D3DProgramming/D3DProgramming at main · MoonSun-v/D3DProgramming

Direct3D 공부하는 레포지토리 입니다. Contribute to MoonSun-v/D3DProgramming development by creating an account on GitHub.

github.com

 

 

 

 

 

0. D3D11 변수 선언

// 렌더링 파이프라인을 구성하는 필수 객체의 인터페이스 ( 뎊스 스텐실 뷰도 있지만 아직 사용X )
ID3D11Device* m_pDevice = nullptr;						
ID3D11DeviceContext* m_pDeviceContext = nullptr;		
IDXGISwapChain* m_pSwapChain = nullptr;				
ID3D11RenderTargetView* m_pRenderTargetView = nullptr;	

// [ 렌더링 파이프라인에 적용하는 객체와 정보 ]
ID3D11VertexShader* m_pVertexShader = nullptr;	// 정점 셰이더
ID3D11PixelShader* m_pPixelShader = nullptr;	// 픽셀 셰이더
ID3D11InputLayout* m_pInputLayout = nullptr;	// 입력 레이아웃
ID3D11Buffer* m_pVertexBuffer = nullptr;		// 버텍스 버퍼
UINT m_VertextBufferStride = 0;					// 버텍스 하나의 크기
UINT m_VertextBufferOffset = 0;					// 버텍스 버퍼의 오프셋
UINT m_VertexCount = 0;							// 버텍스 개수

 

 

 

1. D3D11 장치 초기화 

Direct3D의 기본 렌더링 환경 설정

 

  • 스왑체인 → 백버퍼/프론트버퍼 관리, 화면 출력 구조 준비
  • 디바이스/컨텍스트 → GPU 리소스 생성과 명령 전송 준비
  • 렌더타겟뷰 → 픽셀 셰이더 출력 위치 지정
  • 뷰포트 → 화면 출력 영역 지정
  • 결과 → DX11에서 기본 렌더링 파이프라인을 사용할 준비 완료

 

 

 

1-0. HRESULT 선언

HRESULT hr = 0;

 

  • DirectX 함수들은 대부분 HRESULT 타입 반환
  • 성공 여부를 확인할 때 사용 (예: SUCCEEDED(hr))

 

 

1-1. 스왑 체인(SwapChain) 설정 

DXGI_SWAP_CHAIN_DESC swapDesc = {};
swapDesc.BufferCount = 1;                                    // 백버퍼 개수 (더블 버퍼링: 1)
swapDesc.BufferUsage = DXGI_USAGE_RENDER_TARGET_OUTPUT;      // 백버퍼의 용도: 렌더타겟 출력
swapDesc.OutputWindow = m_hWnd;                              // 출력할 윈도우 핸들
swapDesc.Windowed = true;                                    // 창 모드(true) / 전체화면(false)
swapDesc.BufferDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;     // 백버퍼 포맷: 32비트 RGBA

// 백버퍼(텍스처) 해상도 지정
swapDesc.BufferDesc.Width = m_ClientWidth;
swapDesc.BufferDesc.Height = m_ClientHeight;

// 화면 주사율(Refresh Rate) 지정 (60Hz)
swapDesc.BufferDesc.RefreshRate.Numerator = 60;
swapDesc.BufferDesc.RefreshRate.Denominator = 1;

// 멀티샘플링(안티에일리어싱) 설정 (기본: 1, 안 씀)
swapDesc.SampleDesc.Count = 1;
swapDesc.SampleDesc.Quality = 0;

 

 

1-2. 디바이스 / 디바이스 컨텍스트 / 스왑체인 생성

  • 디바이스(Device): GPU 리소스 생성, 관리
  • 디바이스 컨텍스트(Device Context): GPU 명령 전송
  • 스왑체인(SwapChain): 화면 출력

HR_T 매크로로 오류 처리 (실패 시 메시지 출력/중단)

UINT creationFlags = 0;

#ifdef _DEBUG
	creationFlags |= D3D11_CREATE_DEVICE_DEBUG; // 디버그 레이어 활성화 (DirectX API 호출 시 검증 메시지 출력)
#endif

HR_T(D3D11CreateDeviceAndSwapChain(
	NULL,                           // 기본 어댑터 사용
	D3D_DRIVER_TYPE_HARDWARE,       // 하드웨어 렌더링 사용
	NULL,                           // 소프트웨어 드라이버 없음
	creationFlags,                  // 디버그 플래그
	NULL, NULL,                     // 기본 Feature Level 사용
	D3D11_SDK_VERSION,              // SDK 버전
	&swapDesc,                      // 스왑체인 설명 구조체
	&m_pSwapChain,                  // 스왑체인 반환
	&m_pDevice,                     // 디바이스 반환
	NULL,                           // Feature Level 반환 (사용 안 함)
	&m_pDeviceContext));            // 디바이스 컨텍스트 반환

 

 

 

 

1-3. 렌더타겟뷰(Render Target View) 생성

 

  • 백버퍼 텍스처 가져오기 → 렌더타겟 뷰 생성
  • OM(Output Merger) 단계에 바인딩 → 픽셀 셰이더 출력 결과가 이 버퍼로 출력

 

// 스왑체인의 0번 버퍼(백버퍼)를 가져옴
ID3D11Texture2D* pBackBufferTexture = nullptr;
HR_T(m_pSwapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), (void**)&pBackBufferTexture));

// 백버퍼 텍스처를 기반으로 렌더타겟뷰 생성
HR_T(m_pDevice->CreateRenderTargetView(pBackBufferTexture, NULL, &m_pRenderTargetView));  // 텍스처는 내부 참조 증가

// 텍스처 참조 해제 (렌더타겟뷰가 참조 잡고 있으므로 여기선 해제 가능) // 외부 참조 카운트를 감소시킨다.
SAFE_RELEASE(pBackBufferTexture);	

// 출력 병합(OM: Output Merger) 단계에 렌더타겟 바인딩
m_pDeviceContext->OMSetRenderTargets(1, &m_pRenderTargetView, NULL);

 

 

1-4. 뷰포트(Viewport) 설정 : 화면에서 실제로 렌더링될 영역 정의

 

  • TopLeftX/Y: 시작 좌표
  • Width/Height: 뷰포트 크기
  • MinDepth/MaxDepth: 깊이 버퍼 범위 (0~1)
  • RSSetViewports: 래스터라이저 단계에 적용

 

D3D11_VIEWPORT viewport = {};
viewport.TopLeftX = 0;                        // 뷰포트 시작 X
viewport.TopLeftY = 0;                        // 뷰포트 시작 Y
viewport.Width = (float)m_ClientWidth;        // 뷰포트 너비
viewport.Height = (float)m_ClientHeight;      // 뷰포트 높이
viewport.MinDepth = 0.0f;                     // 깊이 버퍼 최소값
viewport.MaxDepth = 1.0f;                     // 깊이 버퍼 최대값

// 래스터라이저(RS: Rasterizer) 단계에 뷰포트 설정
m_pDeviceContext->RSSetViewports(1, &viewport);

 

 

 

2. 렌더링 리소스 초기화

실제로 그릴 오브젝트와 셰이더 준비

 

  • 정점(Vertex) 정의 → GPU 버퍼 생성 : 렌더링할 도형 준비
  • 버텍스 셰이더 → 입력 레이아웃 : 정점 데이터를 처리하고 셰이더로 전달
  • 픽셀 셰이더 : 화면에 그릴 픽셀 색상 계산
  • 결과적으로 삼각형 1개를 GPU에서 렌더링할 준비 완료
  • 월드/뷰/프로젝션 변환 없이 NDC 좌표로 직접 삼각형 정의 → 단순화

 

 

 

2-0. 변수 선언

HRESULT hr = 0;
ID3D10Blob* errorMessage = nullptr;

 

  • HRESULT : DirectX 함수의 성공/실패 결과 저장
  • ID3D10Blob* errorMessage : 셰이더 컴파일 에러 메시지 버퍼

 

 

 

2-1. 정점 데이터(Vertex) 준비

 

  • NDC(Normalized Device Coordinates, -1~1) 좌표계 사용
  • 삼각형 1개 정의 (v0, v1, v2)
  • z=0.5 : 깊이값 (화면 앞으로 약간 이동)

 

해당 프로젝트 에서는 월드/뷰/프로젝션 변환을 쓰지 않고,
직접 NDC(Normalized Device Coordinate, -1 ~ +1 좌표계)에 맞게 작성 했다. 
Vertex vertices[] =
{
	Vector3(-0.5, -0.5, 0.5), // v0: 왼쪽 아래
	Vector3(0.0,  0.5, 0.5), // v1: 위쪽 중앙
	Vector3(0.5, -0.5, 0.5), // v2: 오른쪽 아래	
};

 

 

 

2-2. 정점 버퍼(Vertex Buffer) 생성

  • 초기 데이터: CPU 메모리에 있는 정점 배열을 GPU 버퍼로 복사
  • CreateBuffer() : GPU에 정점 버퍼 생성, 이후 렌더링 시 해당 버퍼를 바인딩
// 정점 버퍼 속성 구조체
D3D11_BUFFER_DESC vbDesc = {};
m_VertexCount = ARRAYSIZE(vertices);                  // 정점 개수
vbDesc.ByteWidth = sizeof(Vertex) * m_VertexCount;    // 버퍼 크기 (정점 크기 × 정점 개수)
vbDesc.CPUAccessFlags = 0;                            // CPU 접근X
vbDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;          // 정점 버퍼 용도
vbDesc.MiscFlags = 0;
vbDesc.Usage = D3D11_USAGE_DEFAULT;                   // GPU가 읽고 쓰는 기본 버퍼

// 버퍼에 초기 데이터 복사할 구조체
D3D11_SUBRESOURCE_DATA vbData = {};
vbData.pSysMem = vertices; // 버텍스 배열 주소

// 정점 버퍼 생성
HR_T(hr = m_pDevice->CreateBuffer(&vbDesc, &vbData, &m_pVertexBuffer));

// 버텍스 버퍼 정보
m_VertextBufferStride = sizeof(Vertex); // 정점 하나의 크기
m_VertextBufferOffset = 0;              // 시작 오프셋 (0)

 

 

 

2-3. 버텍스 셰이더(Vertex Shader)

 

  • HLSL 파일 BasicVertexShader.hlsl에서 vs_4_0 규격으로 컴파일
  • GPU에서 사용할 버텍스 셰이더 객체 생성

 

ID3DBlob* vertexShaderBuffer = nullptr; // 컴파일된 버텍스 셰이더 코드(hlsl) 저장 버퍼

// ' HLSL 파일에서 main 함수를 vs_4_0 규격으로 컴파일 '
HR_T(CompileShaderFromFile(L"BasicVertexShader.hlsl", "main", "vs_4_0", &vertexShaderBuffer));

// 버텍스 셰이더 객체 생성
HR_T(m_pDevice->CreateVertexShader(
	vertexShaderBuffer->GetBufferPointer(), // 필요한 데이터를 복사하며 객체 생성 
	vertexShaderBuffer->GetBufferSize(), NULL, &m_pVertexShader));

 

 

 

2-4. 입력 레이아웃(Input Layout) : 버텍스 데이터 형식과 셰이더 입력 매핑

  • "POSITION" : HLSL 버텍스 셰이더에서 사용할 의미적 이름
  • DXGI_FORMAT_R32G32B32_FLOAT : 3차원 좌표(float3)
  • 슬롯 0번, 정점마다 데이터 적용

이 과정을 통해 GPU가 정점 데이터를 어떻게 읽을지 알 수 있음

D3D11_INPUT_ELEMENT_DESC layout[] =  
{	
	{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0 } // ' POSITION: float3 (R32G32B32_FLOAT), 슬롯 0번, 정점 당 데이터 '
};

// 버텍스 셰이더의 Input시그니처와 비교해 유효성 검사 후 -> InputLayout 생성
HR_T(hr = m_pDevice->CreateInputLayout(layout, ARRAYSIZE(layout),
	vertexShaderBuffer->GetBufferPointer(), vertexShaderBuffer->GetBufferSize(), &m_pInputLayout));

// 복사했으니 버퍼는 해제 가능
SAFE_RELEASE(vertexShaderBuffer);

 

 

 

2-5. 픽셀 셰이더(Pixel Shader)

 

  • HLSL 파일 BasicPixelShader.hlsl에서 ps_4_0 규격으로 컴파일
  • GPU에서 픽셀 셰이더 객체 생성
  • 이후 프래그먼트 단계에서 색상을 결정하는 셰이더

 

ID3DBlob* pixelShaderBuffer = nullptr; // 컴파일된 버텍스 픽셀 코드(hlsl) 저장 버퍼

// ' HLSL 파일에서 main 함수를 ps_4_0 규격으로 컴파일 '
HR_T(CompileShaderFromFile(L"BasicPixelShader.hlsl", "main", "ps_4_0", &pixelShaderBuffer));

// 픽셀 셰이더 객체 생성
HR_T(m_pDevice->CreatePixelShader(	  // 필요한 데이터를 복사하며 객체 생성 
	pixelShaderBuffer->GetBufferPointer(),
	pixelShaderBuffer->GetBufferSize(), NULL, &m_pPixelShader));

// 복사했으니 버퍼는 해제 가능
SAFE_RELEASE(pixelShaderBuffer);

 

 

 

3. 렌더링 ( Direct3D 렌더링 파이프라인 실행 )

 

  • 화면 초기화 → 이전 프레임 제거, 배경색 칠함
  • IA 단계 설정 → 정점 버퍼, 입력 레이아웃, 삼각형 리스트 지정
  • 셰이더 바인딩 → 버텍스/픽셀 셰이더 준비
  • Draw 호출 → GPU에서 삼각형 1개 렌더링
  • Present 호출 → 백버퍼 내용을 화면에 표시

 

즉, 위에서 준비한 리소스를 이용해,
매 프레임마다 GPU에 렌더링 명령을 전달하고 결과를 화면에 출력하는 과정

 

 

3-1. 렌더타겟 초기화 (Clear)

  • 화면 전체를 지정한 색상(color)으로 칠함 → 이전 프레임 흔적 제거
float color[4] = { 0.80f, 0.92f, 1.0f, 1.0f }; //  Light Sky Blue 
m_pDeviceContext->ClearRenderTargetView(m_pRenderTargetView, color);

 

 

3-2. 파이프라인 설정

Draw 호출 전에 반드시 렌더링 파이프라인 필수 스테이지 설정 해야함

 

 

   

   3-2-1. IA(Input Assembler) 단계에서 정점 연결 방식 설정

  • TRIANGLELIST → 3개의 정점마다 1개의 삼각형 생성

 

m_pDeviceContext->IASetPrimitiveTopology(D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST); // 정점을 이어서 그릴 방식 (삼각형 리스트 방식)

 

   3-2-2. 정점 버퍼 바인딩

 

  • 슬롯 0번에 m_pVertexBuffer 연결
  • Stride: 정점 하나 크기
  • Offset: 버퍼 시작 오프셋
m_pDeviceContext->IASetVertexBuffers(0, 1, &m_pVertexBuffer, &m_VertextBufferStride, &m_VertextBufferOffset);

 

 

   3-2-3. 정점 데이터 형식과 셰이더 입력 매핑

  • GPU가 정점 데이터를 올바르게 해석하도록 설정
m_pDeviceContext->IASetInputLayout(m_pInputLayout);

 

   3-2-4. 버텍스/픽셀 셰이더 바인딩

  • GPU에서 정점 처리 → 픽셀 처리 순서로 수행
m_pDeviceContext->VSSetShader(m_pVertexShader, nullptr, 0);		
m_pDeviceContext->PSSetShader(m_pPixelShader, nullptr, 0);

 

 

3-3. 그리기 호출

 

  • 실제 GPU에 렌더링 명령 전달
  • m_VertexCount 만큼 정점 읽어서 삼각형 그리기
  • 첫 번째 정점은 0번부터 시작

 

m_pDeviceContext->Draw(m_VertexCount, 0);

 

 

3-4. 스왑체인 교체 ( Present )

 

  • 백버퍼 → 프론트버퍼로 화면 출력
  • 실제로 윈도우에 렌더링 결과 표시
  • 0, 0 : 즉시 전송, 동기화 옵션 없음

 

m_pSwapChain->Present(0, 0);

 

 

 

4. 리소스 해제 

사용했던 객체들을 해제 해준다. 

 

현재 코드에서는 안전하게 해제하는 매크로를 따로 만들어서 사용하지만,

이후 부터는 Comptr을 활용하는 방식을 사용할 것이다. 

 

( Comptr 활용 하는 방법은 이전 글에 정리했ㅇ..)

...

void TestApp::UninitD3D()
{
	SAFE_RELEASE(m_pRenderTargetView);
	SAFE_RELEASE(m_pDeviceContext);
	SAFE_RELEASE(m_pSwapChain);
	SAFE_RELEASE(m_pDevice);
}

...

void TestApp::UninitScene()
{
	SAFE_RELEASE(m_pVertexBuffer);
	SAFE_RELEASE(m_pInputLayout);
	SAFE_RELEASE(m_pVertexShader);
	SAFE_RELEASE(m_pPixelShader);
}

 

 

간단하게 화면에 삼각형을 출력하는 이 프로젝트는 Direct3D 11 기반의 최소 단위 렌더링이라고 할 수 있다.

코드를 천천히 훑으며 렌더링 파이프라인의 흐름을 되새겨 보면, 렌더링 과정의 이해에 도움이 될 것 같다.