Direct3D
고친 사람 github-actions[bot]
Direct3D 는 Windows 와 Xbox 에서 프로그램이 그래픽 카드에 그림을 그리라고 시킬 때 쓰는 약속입니다. 어떤 함수를 부르면 무엇이 그려져야 하는지를 Microsoft 가 문서로 정합니다. 그래픽 카드 회사들은 그 문서에 맞춰 각자 드라이버를 만듭니다. 그래서 프로그램은 어느 회사 카드가 꽂혀 있든 같은 함수를 부릅니다.
쉽고 빠른 이해
Direct3D 는 그래픽 카드에 그림을 시키는 함수 목록을 정한 약속입니다. Windows 게임이 칼을 휘두르는 장면 한 장을 화면에 낼 때 이 목록의 함수를 부릅니다.
이게 없으면 그래픽 카드 회사마다 명령 방식이 다릅니다. 같은 장면을 그리려고 회사별로 코드를 따로 짜야 합니다.
어떻게 도나:
- 점 좌표와 그림 파일 같은 데이터를 그래픽 카드 메모리로 올립니다
- 점을 어디에 둘지와 픽셀을 무슨 색으로 칠할지 정하는 작은 프로그램을 넘깁니다
- 그리라는 명령을 보내면 그래픽 카드가 결과를 화면용 메모리에 칠합니다
대가도 있습니다. Windows 와 Xbox 밖에서는 돌지 않아서, 같은 게임을 다른 운영체제에 내려면 그리기 코드를 다시 짭니다. 세대가 바뀌면 부르는 방식이 크게 갈려서 옛 코드를 그대로 못 옮기기도 합니다.
그래서 Direct3D 는 Windows 와 Xbox 를 겨냥한 게임처럼 한 플랫폼에서 성능을 끝까지 짜내는 프로그램에 알맞습니다. 여러 운영체제에 같은 코드를 올려야 하면 다른 그래픽 규격을 고릅니다.
상세
Direct3D 는 3차원 그래픽을 그리는 API(Application Programming Interface, 응용 프로그램 인터페이스)의 규격입니다. 함수 이름과 인자, 부른 뒤에 무엇이 일어나야 하는지까지가 그 규격입니다. 게임이 화면 한 장을 만들 때 부르는 것이 이 함수들입니다.
DirectX 안의 3D 담당
DirectX 는 Windows 에서 소리·입력 장치·그래픽처럼 게임에 필요한 장치를 다루는 API 묶음입니다. Direct3D 는 그중 3차원 그림을 맡는 부분입니다.
두 이름은 섞여 쓰입니다. DirectX 12 라고 하면 대개 그 묶음의 3D 부분인 Direct3D 12 를 가리킵니다. 게임 설정 화면에 DirectX 11 과 DirectX 12 중 고르라는 항목이 나오면, 그것도 그리기를 맡는 이 부분을 고르는 것입니다.
함수 호출이 그래픽 카드까지 내려가는 길
Direct3D 는 따로 설치하는 프로그램이 아니라 규격입니다. 다만 그 규격을 받아 주는 코드의 절반이 운영체제 안에 이미 들어 있습니다. Windows 에 들어 있는 Direct3D 런타임이 프로그램의 호출을 먼저 받습니다.
나머지 절반은 그래픽 카드 회사가 만든 드라이버입니다. 드라이버는 운영체제와 하드웨어 사이에서 장치를 다루는 소프트웨어입니다. 런타임이 넘긴 일을 그 회사 GPU(Graphics Processing Unit, 그래픽 처리 장치)가 알아듣는 명령으로 바꿉니다. 그래픽 카드에 박힌 칩이 GPU 입니다.
flowchart TD
A["게임·3D 프로그램"] --> B["Direct3D 런타임 · Windows 에 들어 있다"]
B --> C["회사 A 드라이버"]
B --> D["회사 B 드라이버"]
C --> E["회사 A GPU"]
D --> F["회사 B GPU"]
그림에서 프로그램은 런타임 하나만 봅니다. 아래에 어느 회사 카드가 꽂혀 있든 부르는 함수는 같습니다. 대신 그 런타임이 없는 운영체제에서는 Direct3D 로 짠 그리기 코드가 아예 돌지 않습니다.
이 층이 있는 까닭이 여기서 드러납니다. 카드마다 알아듣는 명령이 다릅니다. 규격이 없으면 게임은 회사별로 다른 명령을 알아야 합니다.
새 카드가 나올 때마다 그리기 코드도 고쳐야 합니다. 규격 하나를 사이에 두면 그 일이 드라이버 쪽으로 넘어갑니다.
그림이 그려지는 순서
Direct3D 의 그리기는 정해진 단계를 차례로 지납니다. 이렇게 이어진 단계 전체가 그래픽스 파이프라인입니다. 프로그램이 넘긴 점이 여러 단계를 거쳐 화면의 픽셀 색이 됩니다. 단계 이름에 셰이더가 붙은 것은 프로그래머가 써 넣는 작은 프로그램이 도는 단계입니다.
flowchart TD
A["입력 어셈블러 · 점을 읽어 도형으로 묶는다"] --> B["정점 셰이더 · 점의 위치를 옮긴다"]
B --> C["테셀레이션 · 도형을 잘게 쪼갠다"]
C --> D["지오메트리 셰이더 · 도형을 늘리거나 버린다"]
D --> E["래스터라이저 · 도형이 덮는 픽셀을 찾는다"]
E --> F["픽셀 셰이더 · 픽셀 색을 정한다"]
F --> G["출력 병합기 · 가림을 판정하고 색을 섞는다"]
첫 단계인 입력 어셈블러는 메모리에 올려 둔 점들을 읽어 프리미티브로 묶습니다. 프리미티브는 점·선·삼각형처럼 GPU 가 한 번에 그리는 기본 도형입니다. 뒤 단계들이 다루는 것은 낱낱의 점이 아니라 이렇게 묶인 도형입니다.
정점 셰이더는 정점 하나하나를 화면 기준 위치로 옮깁니다. 정점은 좌표와 색 같은 값을 가진 점 하나입니다.
테셀레이션은 도형 하나를 더 작은 삼각형 여럿으로 쪼개는 단계입니다. 멀리 있는 지형은 성기게, 가까운 지형은 촘촘하게 쪼개 쓰는 데 씁니다. 지오메트리 셰이더는 들어온 도형을 보고 도형을 더 만들거나 버립니다.
래스터라이저는 좌표로만 된 도형이 화면의 어느 픽셀을 덮는지 찾습니다. 이 일이 래스터화입니다. 뒤를 보이고 선 삼각형을 그리기 전에 버리는 컬링도 이 단계의 설정입니다.
픽셀 셰이더는 래스터라이저가 찾아낸 픽셀마다 색을 정합니다. 마지막 출력 병합기는 깊이 테스트로 다른 물체에 가려진 픽셀을 버립니다. 반투명한 색은 이미 칠해진 뒤쪽 색과 섞습니다(블렌딩).
셰이더가 붙지 않은 나머지는 설정만 바꾸는 고정 단계입니다. 테셀레이션과 지오메트리 셰이더는 안 쓰면 건너뜁니다.
셰이더는 HLSL 로 쓴다
셰이더 단계에 넣는 그 작은 프로그램을 Direct3D 에서는 HLSL(High Level Shader Language, 고수준 셰이더 언어)로 씁니다. C 와 닮은 문법입니다.
아래는 어떤 픽셀이든 빨강으로 칠하는 픽셀 셰이더입니다.
float4 main() : SV_TARGET
{
return float4(1, 0, 0, 1); // 빨강·불투명
}
돌려주는 값 넷은 빨강·초록·파랑과 불투명도입니다. 앞의 셋이 1, 0, 0 이라 빨강입니다. 마지막 1 은 완전히 불투명하다는 뜻입니다.
함수 이름 뒤에 붙은 SV_TARGET 은 이 색을 렌더 타깃에 쓰라는 표시입니다. 렌더 타깃은 그린 결과를 담는 GPU 쪽 메모리입니다.
세대가 바뀌면 부르는 방식이 갈린다
Direct3D 는 세대마다 함수 목록을 크게 갈아엎습니다. 지금 쓰이는 것은 11 세대와 12 세대 둘입니다.
11 세대에서는 드라이버가 뒤에서 챙겨 주는 일이 많습니다. 메모리를 언제 잡고 풀지, 쌓인 명령을 언제 보낼지를 드라이버가 정합니다. 프로그램은 무엇을 그릴지만 지시합니다.
12 세대는 그 일을 프로그램에 넘겼습니다. 메모리를 잡고 푸는 것이 프로그램 몫입니다. 그리기 설정도 하나씩 켜는 대신 파이프라인 상태 객체 한 덩이로 묶어 미리 만들어 둡니다.
그릴 명령은 명령 목록에 모아 둡니다. 모아 둔 목록은 큐로 보냅니다. 큐는 GPU 가 차례로 꺼내 가는 명령 대기줄입니다.
얻는 것은 CPU(Central Processing Unit, 중앙 처리 장치) 쪽 부담이 줄어든다는 것입니다. 여러 코어가 각자 명령 목록을 쌓을 수 있어 한 코어가 병목이 되는 일도 줄어듭니다.
대가는 둘입니다. 코드가 길어집니다. 동기화도 프로그램이 직접 맞춰야 합니다.
GPU 가 아직 읽고 있는 메모리를 CPU 가 덮어쓰면 화면이 깨집니다. 어느 시점에 안전한지를 판정하는 일이 프로그램으로 내려온 셈입니다.
두 세대는 나란히 쓰입니다. 12 세대로 갈아타는 비용이 커서, 11 세대로 짠 코드를 두고 병목이 되는 대목만 12 세대로 바꿔 끼우는 길도 열려 있습니다.
같은 단계를 다르게 부르는 이름
Direct3D 계열이 쓰는 이름은 OpenGL 계열과 다릅니다. 단계는 같습니다. 이름만 갈립니다.
표의 셋째 줄에 나오는 상수 버퍼는 셰이더가 함께 읽는 값을 담아 두는 GPU 쪽 메모리입니다. 이름은 계열마다 갈려도 담는 것은 같습니다.
이 갈림은 문서와 예제를 옮겨 읽을 때 걸립니다. 그래픽 책과 예제가 두 계열에 흩어져 있어서, 이름만 다른 같은 것을 다른 기능으로 읽기 쉽습니다.
나란히 서는 다른 그래픽스 API
같은 역할을 하는 그래픽스 API 가 셋 더 있습니다. OpenGL 은 여러 운영체제에서 두루 도는 규격입니다. Vulkan 은 그 뒤를 잇는 규격입니다. Metal 은 Apple 이 자기 기기용으로 낸 API 입니다.
Direct3D 12 와 Vulkan 은 성격이 닮았습니다. 둘 다 드라이버가 챙겨 주던 일을 프로그램으로 내렸습니다. 다른 점은 도는 곳입니다. Direct3D 는 Windows 와 Xbox 에만 있고 Vulkan 은 여러 운영체제에 있습니다.
그래서 여러 기기에 같은 게임을 내는 팀은 그리기 부분을 한 겹 감싸 둡니다. Unity·언리얼 엔진 같은 게임 엔진이 그 한 겹입니다. 기기마다 그 안에서 다른 그래픽스 API 를 골라 부릅니다.
관련 항목
Direct3D 가 속하는 상위 분류
DirectX · 그래픽스 API · 그래픽스 · 렌더링 · API
Direct3D 를 대신할 수 있는 그래픽스 API
OpenGL · Vulkan · Metal · WebGPU · WebGL
Direct3D 가 그리기를 거치는 처리 단계
그래픽스 파이프라인 · 입력 어셈블러 · 정점 셰이더 · 테셀레이션 · 지오메트리 셰이더 · 래스터화 · 픽셀 셰이더 · 출력 병합기
Direct3D 로 켜고 끄는 그리기 설정
컬링 · 깊이 테스트 · 스텐실 테스트 · 블렌딩 · 뷰포트
Direct3D 가 다루는 GPU 쪽 객체
텍스처 · 정점 버퍼 · 상수 버퍼 · 렌더 타깃 · 프레임버퍼 · 스왑 체인
Direct3D 12 에서 프로그램이 직접 다루는 장치
커맨드 리스트 · 커맨드 큐 · 파이프라인 상태 객체 · 디스크립터 힙 · 펜스
Direct3D 와 이웃 API 가 쓰는 셰이더 언어
Direct3D 를 만들고 실행하는 주체
Microsoft · Windows · Xbox · 디바이스 드라이버 · GPU
Direct3D 를 감싸 쓰는 제품
Unity · 언리얼 엔진 · 게임 엔진 · ANGLE
Direct3D 로 그릴 때 부딪히는 개념
다른 이름: D3D · 다이렉트3D