gpu 단계에서 간략한 렌더링 파이프라인개념
1. 정점 셰이더 (Vertex Shader)
3D 모델의 각 꼭짓점(정점) 위치를 계산해서 화면 좌표로 변환하는 단계
2. 래스터라이저 (Rasterizer)
정점들을 연결해서 삼각형을 만들고, 그 안을 픽셀로 채우는 단계
3. 픽셀 셰이더 (Fragment Shader)
각 픽셀의 최종 색상을 계산하는 단계
Fragment Shader = Pixel Shader (같은 것)
역사적 이유
DirectX (Microsoft)
"Pixel Shader"라는 용어 사용
Windows, Xbox 등 Microsoft 생태계
OpenGL (크로노스 그룹)
"Fragment Shader"라는 용어 사용
크로스 플랫폼, 웹, 모바일 등
┌─────────────────────┐
│ 애플리케이션 단계 │ ← CPU
├─────────────────────┤
│ 지오메트리 단계 │ ← GPU (정점)
├─────────────────────┤
│ 래스터라이저 단계 │ ← GPU (픽셀)
└─────────────────────┘
각 단계는 병렬로 파이프라인처럼 흐름 — 앞 단계가 끝나야 다음 단계 시작이 아니라, 동시에 진행됨.
완전히 CPU에서 실행. 개발자가 가장 많이 제어하는 영역.
① 씬 관리
어떤 오브젝트가 존재하는지, 각 오브젝트의 Transform 정보를 알아낸다
② 가시성 판단 (Culling)
Frustum Culling → 카메라 시야 밖 오브젝트 제거
Occlusion Culling → 다른 오브젝트에 완전히 가려진 것 제거
Backface Culling → 카메라를 등진 삼각형 면 제거 (이건 GPU에서도 가능)
③ 렌더 상태 설정
이 오브젝트는 어떤 셰이더를 쓸 것인가?
어떤 텍스처를 바인딩할 것인가?
블렌딩 모드는 무엇인가?
④ Draw Call 전송
CPU → GPU로 "이 메시를, 이 셰이더로, 그려라" 명령 전달
이 명령 하나 = Draw Call 1개
사실 Draw Call 안에 렌더 스테이트(렌더상태),DP Call 이 들어가 있다.
애플리케이션 단계의 출력이 다음 단계의 입력 품질을 결정함.
Draw Call이 많을수록 CPU-GPU 통신 비용 증가 → 병목 발생.
"무엇을 어디에 그릴 것인가"를 결정하는 단계.
정점(Vertex) 단위로 처리. 여러 서브 단계로 구성됨.
오브젝트의 정점 좌표를 여러 공간으로 옮기는 과정.
오브젝트 공간 (Object Space)
↓ Model Matrix (월드 배치)
월드 공간 (World Space)
↓ View Matrix (카메라 시점)
뷰 공간 (View/Camera Space)
오브젝트 공간: 메시 자체의 로컬 좌표. 원점이 오브젝트 중심.
월드 공간: 씬 전체 기준 좌표. 모든 오브젝트가 같은 기준점 사용.
뷰 공간: 카메라가 원점이고 카메라가 -Z를 바라보는 좌표계.
조명 계산의 상당수가 여기서 이루어짐.
정점 셰이딩 단계는 각 정점의 위치를 변환하고, 노말·텍스처 좌표 등
픽셀 셰이더에 전달할 속성을 계산하는 단계다. 필요하다면 여기에서 정점 단위 조명/색을 계산할 수도 있다
정점셰이딩은 모서리만 계산하고 나머지를 보간한다.빠르지만 부정확 하기에 픽셸세이딩에서
모든 픽셀을 직접계산한다
per‑vertex(정점) 조명 설명
정점 조명(per‑vertex lighting, Gouraud Shading)에서는 정점 셰이더에서 각 정점의 조명·색을 계산하고, 래스터라이저가 삼각형 내부 픽셀에 대해 이 값을 보간한다. 이 방식은 빠르지만, 하이라이트나 실루엣 주변에서 정확도가 떨어진다.”
per‑pixel(픽셀) 조명 설명
“픽셀 조명(per‑pixel lighting)에서는 정점 셰이더가 조명 계산에 필요한 노말·위치 등을 넘겨 주고, 픽셀 셰이더가 각 픽셀마다 조명식을 직접 평가한다. 계산량은 늘어나지만 보다 정확한 조명과 하이라이트를 얻을 수 있다
뷰 공간 → 클립 공간으로 변환. 3D → 2D의 핵심.
원근 투영 (Perspective Projection)
멀리 있는 것은 작게, 가까운 것은 크게
FOV(시야각), Near/Far Plane 정의
직교 투영 (Orthographic Projection)
거리에 관계없이 동일한 크기
UI, 2D 게임, 기술 도면에 사용
투영 후 좌표계 = 클립 공간 (Clip Space)
모든 좌표가 -1 ~ +1 범위의 정규화된 값(NDC)으로 변환됨.
카메라 절두체(Frustum) 경계를 벗어난 삼각형 처리.
완전히 안쪽 → 그대로 통과
완전히 바깥 → 완전 제거
걸쳐 있음 → 경계면에서 잘라내고 새 정점 생성
/|
/ | ← 절두체 경계
----/--+-----
/ |새 정점 생성됨
/ |
클립 공간의 NDC 좌표 → 실제 픽셀 좌표로 변환.
NDC (-1 ~ +1) → 화면 픽셀 좌표 (0 ~ 1920 등)
"픽셀 단위로 어떻게 채울 것인가"를 결정하는 단계.
정점 3개로 정의된 삼각형의 엣지 방정식 계산.
"어떤 픽셀이 이 삼각형 안에 있는가?"를 판별하기 위한 수학적 준비.
삼각형 내부에 있는 모든 픽셀 후보(Fragment)를 찾아내는 과정.
┌───┬───┬───┬───┐
│ │ ▓ │ ▓ │ │
├───┼───┼───┼───┤
│ ▓ │ ▓ │ ▓ │ ▓ │ ← 삼각형이 덮는 픽셀들 = Fragment 생성
├───┼───┼───┼───┤
│ │ ▓ │ ▓ │ │
└───┴───┴───┴───┘
각 Fragment에는 보간된 값이 포함됨:
각 Fragment마다 최종 색상 계산.
개발자가 HLSL로 직접 작성하는 Fragment Shader가 여기서 실행.
텍스처 샘플링 + 조명 계산 + 그림자 + 반사 + ...
→ 최종 RGBA 색상 결정
픽셀 셰이더 결과를 실제 프레임버퍼에 합치는 과정.
여러 테스트가 순서대로 수행됨.
① Z-Buffer 테스트 (깊이 테스트)
이 픽셀의 깊이 vs 현재 버퍼에 저장된 깊이
더 앞에 있으면 → 덮어쓰기
더 뒤에 있으면 → 버리기
② 스텐실 테스트 (Stencil Test)
스텐실 버퍼의 값 조건을 만족하는 픽셀만 통과
마스킹, 포탈, 실루엣 효과 등에 활용
③ 알파 블렌딩 (Alpha Blending)
반투명 오브젝트: 기존 픽셀 색상과 새 픽셀 색상을 알파값으로 혼합
Source SrcAlpha + Destination (1 - SrcAlpha)
Early-Z: 원래 Z테스트는 픽셀 셰이더 이후지만,
최적화를 위해 픽셀 셰이더 이전에 미리 깊이 테스트를 하는 것.
Alpha Test(discard)를 쓰면 "픽셀이 살아남을지 모르니" Early-Z 불가.
[애플리케이션]
씬 데이터 → Culling → 렌더 상태 설정 → Draw Call
↓
[지오메트리] 정점 스트림 입력
오브젝트공간 → 월드공간 → 뷰공간 → 투영 → 클리핑 → 화면공간
↓
[래스터라이저] 삼각형 + 정점 데이터
삼각형 설정 → 순회 → Fragment 생성 → 픽셀 셰이딩 → 합성 → 프레임버퍼
*추가설명
오해하기 쉬운 부분인데, 렌더 패스와 렌더링 파이프라인은 다른 레벨의 개념
렌더 패스 (Render Pass)
└── 각 패스마다 파이프라인 전체를 한 번씩 돌림
즉, 파이프라인이 렌더 패스 "안에" 들어가는 구조
공장 생산라인(파이프라인)
제품을 여러 번 라인에 올리는 것 = 렌더 패스
- 1회차: 밑칠만 하기 (Shadow Pass)
- 2회차: 색 입히기 (Opaque Pass)
- 3회차: 마감 처리 (Post Processing)
라인 자체의 구조(애플리케이션→지오메트리→래스터라이저)는 매번 동일
Shadow Pass
└── [파이프라인 전체 실행] → Shadow Map 텍스처 생성
Depth Prepass
└── [파이프라인 전체 실행] → Depth Buffer 생성
Opaque Pass
└── [파이프라인 전체 실행] → 불투명 오브젝트들 렌더링
Transparent Pass
└── [파이프라인 전체 실행] → 반투명 오브젝트 렌더링
Post Processing Pass
└── [파이프라인 전체 실행] → 화면 전체에 효과 적용
각 패스의 출력(텍스처, 버퍼)이 다음 패스의 입력으로 들어가는 구조예요.
파이프라인 = 정점 하나를 픽셀로 만드는 과정
렌더 패스 = 그 과정을 "무슨 목적으로, 몇 번" 돌릴지 결정하는 단위
최종적으로 화면에 출력될 색상(RGBA)을 저장
각 픽셀의 R, G, B, A 값 저장
프레임버퍼라고도 불림
렌더링 결과가 여기에 쌓임
더블 버퍼링으로 운영됨:
Front Buffer → 현재 화면에 표시 중
Back Buffer → 다음 프레임 렌더링 중
각 픽셀의 깊이값(카메라로부터의 거리)을 저장
0.0 = Near Plane (카메라 바로 앞)
1.0 = Far Plane (가장 멀리)
용도:
앞 오브젝트가 뒤 오브젝트를 가리는 판정
Early-Z 최적화의 기반
SSAO, DOF 등 Post Processing의 입력값
픽셀마다 정수값(0~255)을 저장하는 마스크
Depth Buffer와 같은 메모리에 저장됨 (D24S8 포맷 — 깊이 24bit + 스텐실 8bit)
용도:
특정 영역만 렌더링 → 포탈, 거울 반사
실루엣 외곽선 → 값이 1인 픽셀 테두리만 그리기
마스킹 효과
Deferred Rendering에서 사용. 조명 계산에 필요한 정보를 여러 텍스처에 나눠 저장
G-Buffer 0 → Albedo (기본 색상)
G-Buffer 1 → Normal (노말 벡터)
G-Buffer 2 → Specular / Metallic / Roughness
G-Buffer 3 → Emission
Geometry Pass → G-Buffer 채우기
↓
Lighting Pass → G-Buffer 읽어서 조명 계산
Forward Rendering은 G-Buffer 없이 한 번에 처리.
광원 시점에서 렌더링한 깊이값 저장
Shadow Pass에서 생성
Opaque Pass에서 읽어서
"이 픽셀이 그림자 안에 있나?" 판정에 사용
위 버퍼들을 묶어서 부르는 개념:
Frame Buffer
├── Color Buffer
├── Depth Buffer
└── Stencil Buffer
Unity에서 RenderTexture = 커스텀 Frame Buffer라고 볼 수 있어요.
Shadow Pass → Shadow Map Buffer 생성
Depth Prepass → Depth Buffer 채우기
Geometry Pass → G-Buffer 채우기 (Deferred만)
Opaque Pass → Color Buffer에 쓰기
Depth/Stencil Buffer로 판정
Post Processing → Color/Depth Buffer 읽어서 효과 적용
↓
Front Buffer로 스왑 → 화면 출력