Direct3DX 복습하기 2 - Day - 렌더링 파이프 라인(Rendering Pipeline)
1. 렌더링 파이프 라인
렌더링 파이프라인은 3D모델과 데이터를 GPU가 처리하여 2D 화면에 그리는 과정을 의미하며 크게 세가지로 나눌 수 있다.
(1) 애플리케이션 단계 : CPU에서 실행되는 논리적 계산과 렌더링 명령을 생성하는 단계
(2) 기하 처리 단계 : GPU에서 정점 데이터를 처리하고, 객체의 위치나 형태를 변형하는 단계
(3) 래스터화 단계 : 처리된 정점을 픽셀로 변환하고 화면에 출력하는 단계
2. 렌더링 파이프 라인의 주요 단계
(1) 입력 어셈블리(Input Assembly)
입력 어셈블리 단계는 렌더링 파이프라인의 시작점으로, 애플리케이션에서 제공하는 정점(Vertex) 데이터와 인덱스(Index) 데이터를 GPU로 전달하는 역할을 한다.
(1) - 1 - 주요 기능
- 정점 데이터 수집 : 정점 버퍼(Vertex Buffer)에서 위치(Position), 색상(Color), 텍스쳐 좌표등의 데이터를 가져옴
- 프리미티브 형성 : 정점들을 삼각형(Triangle), 선(Line), 점(Point) 등의 기본 도형으로 조립
- 인덱스 사용 : 인덱스 버퍼(Index Buffer)를 사용하여 정점의 재사용을 통해 메모리 효율성 높임
(1) - 2 - 예제
- 정점 버퍼와 인덱스버퍼를 생성하고 설정
- IASetVertexBuffers와 IASetIndexBuffer 함수를 사용해서 입력 어셈블리 단계에 버퍼를 바인딩한다.
- IASetPrimitiveTopology를 설정하여 GPU가 정점 데이터를 어떻게 해석할지 지정
(1) - 3 - 최적화 고려 사항
- 버텍스 캐싱(Vertex Caching) : 정점 데이터를 효율적으로 캐싱하여 중복 계산을 줄임
- 인스턴싱(Instancing) : 동일한 메쉬를 여러 번 렌더링 할 경우, 인스턴싱을 사용해 효율성을 높인다.
(2) 버텍스 셰이더(Vertex Shader)
버텍스 셰이더는 정점 데이터를 처리하는 프로그램으로, 각 정점의 위치를 변환하고, 조명 계산 등을 수행한다.
(2) - 1 - 주요 기능
- 좌표 변환 : 모델 공간(Model Space) -> 월드 공간(World Space) -> 뷰 공간(View Space) -> 클립 공간(Clip Space)로 정점 위치를 변환
- 조명 계산 : 정점 단위에서 간단한 조명 효과를 적용
- 정점 속성 전달 : 텍스쳐 좌표, 색상 등 정점 속성을 픽셀 셰이더로 전달
(2) - 2 - 예제
VertexShader
- Constant Buffer를 통해 월드-뷰-프로젝션 행렬을 받아와 정점 위치를 변환
- 변환된 위치를 SV_POSITION으로 변환하여 다음 단계로 전달
- 색상과 텍스쳐 좌표를 VS_OUPUT 구조체를 통해 픽셀 셰이더로 전달한다.
(2) - 3 - 최적화 고려 사항
- 효율적인 셰이더 코드 작성 : 불필요한 연산을 피하고, 병렬 처리에 최적화된 코드를 작성
- LOD(Level Of Detail) : 멀리 있는 객체는 덜 복잡한 버텍스 셰이더를 사용해 성능 최적화
(3) 테셀레이션(Tessellation) (선택)
테셀레이션 단계는 저해상도 메쉬를 고해상도로 분할하여 디테일을 높이는 과정으로 이 단계는 특히 지형, 곡면 등의 세밀한 표현에 유용하다.
(3) - 1 - 주요 구성 요소 :
상수 덮개 셰이더 (Constant Hull Shader) : 패치 단위로 입력 데이터를 받아 제어 포인트를 처리
테셀레이션 (Tessellator) : 하드웨어에서 제어 포인트를 기반으로 패치를 세부적으로 분할
영역 셰이더 (Domain Shader) : Tessellator에서 생성된 새로운 정점을 처리해 최종 위치를 계산
(3) - 2 - 예제
- Hull Shader는 입력 패치를 받아 테셀레이션 팩터(Tessellation Factor)를 설정
- Tessellator는 Hull Shader에서 지정한 팩터에 따라 패치를 세부적으로 분할한다.
- Domain Shader는 Tessellator에서 생성된 새로운 정점을 처리하여 최종 위치를 계산
(3) - 3 - 활용 예 :
- 카메라에 가까운 삼각형들은 테셀레이션을 적용해서 세부도를 높이고, 먼 삼각형들은 기존 메시를 그대로 적용하는 방식으로 Level of detail(LOD) 구현 가능
- 메모리에선 로우폴리 메시를 적용하고 필요하면 즉석으로 폴리곤을 만들어 표현해 메모리 절약이 가능하다
- 애니메이션이나 물리처리 같은 계산이 많은 작업은 로우폴리 메시로 수행하고 렌더링 할 땐 테셀레이션을 적용시켜 계산량을 줄일 수 있다.
(3) - 4 - 최적화 고려 사항
- 테셀레이션 팩터 조정 : 디테일 수준을 동적으로 조정하여 성능과 품질의 균형을 맞춤
- 테셀레이션 셰이더 최적화 : Hull Shader와 Domain Shader를 효율적으로 작성하여 테셀레이션 성을 높임
(4) 기하 셰이더(Geometry Shader) (선택)
기하 셰이더는 버텍스 셰이더와 픽셀 셰이더 사이에 위치하며, 기본적인 프리미티브(점, 선, 삼각형)을 추가로 생성하거나 수정할 수 있다. 예를 들면, 입자 효과, 실루엣 강조, 또는 복잡한 기하학 생성에 사용된다.
(4) - 1 - 주요 기능 :
- 프리미티브 생성 및 수정 : 기존 프리미티브를 기반으로 새로운 프리미티브를 생성하거나 수정
- 동적 객체 생성 : 입자 시스템에서 입자를 동적으로 생성하거나, 실루엣 강조를 위해 추가적인 선을 생성
(4) - 2 - 예제
- TriangleStream을 사용해 새로운 삼각형 생성
- 예제에서는 기존 삼각형을 그대로 출력하지만, 기하 셰이더를 사용하면 새로운 삼각형을 생성하거나 변형 가능
(4) - 3 - 활용 예 :
- 입자 시스템 : 입자를 동적으로 생성하거나 변경
- 실루엣 강조 : 객체의 외곽선을 강조하기 위해 추가적인 선을 생성
- 파티클 효과 : 폭발, 불꽃 등의 시각 효과 생성
(4) - 4 - 최적화 고려 사항
- 프리미티브의 최소화 : 기하 셰이더는 추가적인 프리미티브를 생성할 수 있으므로, 필요한 경우에만 사용
- 병렬 처리 최적화 : 기하 셰이더의 연산을 병렬 처리할 수 있도록 최적화
(5) 래스터화(Rasterization)
래스터화 단계는 3D 공간의 정점과 프리미티브를 2D 화면의 픽셀로 변환하는 과정으로, 이 단계에서 프리미티브는 픽셀 단위로 분할되고, 각 픽셀에 대해 색상 및 깊이 정보가 계산된다.
(5) - 1 - 주요 기능 :
- 클리핑 : 프러스텀 안에 있는 부분만 화면에 표시되도록 삼각형을 잘라내어 불필요한 렌더링을 방지
- 화면 공간 변환 : 클립 공간의 정점을 화면 좌표로 변환하여 칙셀 단위로 매핑
- 픽셀 생성 : 삼각형 내부의 픽셀들을 생성하고, 각 픽셀의 위치와 관련된 데이터(깊이, 텍스쳐 좌표)를 계산
- 선형보간법(Interpolation) : 정점 속성을 픽셀 단위로 보간하여 픽셀 셰이더에 전달
(5) - 2 - 예제
래스터화는 주로 GPU 하드웨어에서 처리되기 때문에, 직접적인 코드 예제보다는 래스터화의 동작을 설명하는 것이 적합하다.
- FillMode : 삼각형을 채워서 렌더링할지, 선으로만 렌더링할지 결정.
- CullMode : 백 페이스 컬링(Back Face Culling)을 설정하여 보이지 않는 면을 제거
- DepthClipEnable : 깊이 클리핑을 활성화 하여 프러스텀 밖의 픽셀을 잘라낸다.
(5) - 3 - 최적화 고려 사항
- 클리핑 최적화 : 프러스텀 클리핑을 효율적으로 수행하여 불필요한 픽셀 계산을 줄임
- 멀티샘플링(MultiSampling) : 안티앨리어싱(Anti-Aliasing)을 위해 멀티 샘플링을 적용하여 픽셀 품질을 향상
(6) 픽셀 셰이더(Pixel Shader)
픽셀 셰이더는 래스터화된 픽셀의 최종 색상을 계산하는 단계로, 각 픽셀의 색상, 텍스쳐, 조명, 그림자 등을 계산하여 최종 화면에 출력할 색상을 결정.
(6) - 1 - 주요 기능 :
- 텍스처 매핑 : 텍스처 이미지를 픽셀에 적용
- 조명 계산 : 픽셀 단위의 조명 효과를 계산
- 페인팅 및 효과 : 색상 보정, 블렌딩, 포스트 프로세싱 효과 적용
(6) - 2 - 예제
- Texture2D와 SamplerState를 사용하여 텍스쳐를 샘플링
- 텍스쳐 색상과 정점 색상을 곱하여 최종 픽셀 색상을 계산
- SV_TARGET을 사용하여 최종 색상을 출력
(6) - 3 - 고급 기능 :
- 멀티 텍스쳐링 : 여러 텍스쳐를 결합하여 복잡한 재질 표현
- 라이팅 모델 : 디퓨즈(Diffuse), 스펙큘러(Specular), 앰비언트(Ambient) 라이팅 계산
- 포스트 프로세싱 : 블룸, 블러, 색상 보정 등 다양한 후처리 효과 적용
(6) - 4 - 최적화 고려 사항
- 텍스처 최적화 : 텍스쳐 메모리 접근을 최소화하고, MIP 맵(MIP MAP)을 사용해 효율적으로 텍스쳐를 샘플링
- 조명 계산 최적화 : 조명 계산을 효율적으로 수행하여 픽셀 셰이더의 연산 부하를 줄임
MIPMAP 이란 렌더링 속도를 향상시키기 위한 목적으로 기본 텍스처와 이를 연속적으로 미리 축소시킨 텍스쳐 들로 이루어진 비트맵 이미지의 집합이다.
(7) 출력 병합(Output Merger)
출력 병합 단계는 픽셀 셰이더에서 계산된 픽셀 색상을 최종적으로 화면에 출력하기 전에 여러 테스트와 블렌딩을 수행하는 단계로, 깊이 테스트, 스텐실 테스트, 블렌딩 등을 통해 최종 픽셀 값을 결정
(7) - 1 - 주요 기능 :
- 깊이 테스트 ( Depth Testing) : 픽셀의 깊이 값을 비교하여 앞에 있는 픽셀만 표시
- 스텐실 테스트 ( Stencil Testing) : 스텐실 버퍼를 사용하여 픽셀의 표시 여부를 제어
- 블렌딩 ( Blending) : 여러 픽셀 색상을 혼합하여 투명도 효과 등을 구현
- 래스터라이제이션(Rasterization) : 최종적으로 픽셀을 화면에 출력
(7) - 2 - 예제
- 블렌딩 상태(Blend State) 를 생성하여 투명도 효과를 구현, 예제에서는 소스 알파(SRC_ALPHA)와 역 소스 알파(INV_SRC_ALPHA)를 사용한 블렌딩
- OMSetBlendState 함수를 사용하여 출력 병합 단계에 블렌딩 상태를 설정
- 깊이 스텐실 상태(Depth Stencil State)를 설정하여 깊이 테스트와 스텐실 테스트를 활성화
- OMSetRenderTargets를 사용해 최종 렌더 타겟과 깊이 스텐실 뷰를 설정
(7) - 3 - 최적화 고려 사항
- 깊이 버퍼 최적화 : 깊이 버퍼를 효율적으로 관리하여 깊이 테스트 성능을 높임
- 블렌딩 최적화 : 필요한 경우에만 블렌딩을 활성화하여 GPU 부하를 줄임
3. 렌더링 파이프 라인의 흐름
입력 어셈블리(Input Assembly) : 정점 및 인덱스 데이터를 수집해 프리미티브를 형성
버텍스 셰이더(Vertex Shader) : 정점 데이터를 변환하고 필요한 속성을 계산
테셀레이션(Tessellation) : 저해상도 메쉬를 고해상도로 세분화
기하 셰이더(Geometry Shader) : 프리미티브를 추가 생성하거나 수정
래스터화( Rasterization) : 3D 프리미티브를 2D 픽셀로 변환
픽셀 셰이더(Pixel Shader) : 각 픽셀의 최종 색상을 계산
출력 병합(Output Merger) : 깊이 테스트, 스텐실 테스트, 블렌딩 등을 수행하여 최종 픽셀을 화면에 출력
4. 렌더링 파이프 라인에서의 최적화 요소
절두체 컬링(Frustum Culling) , 클리핑(Clipping) :
(1) 카메라 프러스텀(Frustum)이란
카메라가 3D 공간을 볼 수 있는 영역을 프러스텀이라고 부르며, 절두체라는 이름을 가진 기하학적 도형으로, 피라미드 모양의 윗부분이 잘린 형태이다. 이 프러스텀 안에 있는 객체들만 카메라에 보이기 때문에, 이 영역 밖에 있는 객체는 렌더링할 필요가 없다.
(2) Frustum Culling 과정 :
(1) 카메라의 프러스텀 계산 : 카메라의 위치, 방향, 시야삭(FOV), 근평면(Near Plane), 원평면(Far Plane)을기준으로 프러스텀을 정의한다.
(2) 객체의 바운딩 박스 확인 : 3D 객체는 보통 바운딩 박스(Bounding Box)라 는 간단한 박스로 감싸져 있어. 이 박스가 프로스텀 안에 있는지를 확인한다.
(3) 안에 있지 않은 객체 제외 : 바운딩 박스가 프러스텀 밖에 있으면 그 객체를 렌더링하지 않음으로 불필요한 연산을 줄인다.
Frustem Culling : 카메라의 시야 범위(Frustum) 밖에 있는 객체들을 렌더링에서 제외하는 과정으로, 이는 GPU가 굳이 시야에 보이지 않는 객체들을 렌더링하지 않도록 막아 성능을 높이는데 도움을 준다.
(3) Clipping의 과정 :
(1) 프러스텀 경계를 넘어가는 정점 확인 : 삼각형 같은 3D 프리미티브의 일부 정점이 카메라 시야를 넘어가면, 이 부분을 잘라내기 위해 클리핑을 수행한다.
(2) 잘린 프리미티브 재구성 : 넘어가는 부분을 잘라낸 후, 나머지 부분으로 새로운 삼각형을 만들어 화면에 그릴 수 있게 재구성 한다.
Clipping : 프러스텀 안에 있는 객체라도, 일부가 카메라 시야 밖으로 나가면 그 부분을 잘라내는 과정으로, 이는 객체가 프러스텀 안에 있더라도 모니터에 보이지 않는 부분을 계산하지 않도록 최적화하는 기법이다.
(4) 절두체 컬링(Frustum Culling) 과 클리핑(Clipping) 의 차이점
Frustum Culling은 아예 프러스텀 바깥에 있는 객체는 렌더링하지 않고, Clipping은 프러스텀 경계를 넘는 객체는 보이는 부분만 렌더링을 해준다.
레벨 오브 디테일(LOD) :
멀리 있는 객체일 수록 간단한 메쉬로 렌더링 하여 불필요한 자원 낭비를 막아줌 정점 및 픽셀 셰이더의 병렬 처리 : GPU는 수많은 정점과 픽셀을 동시에 처리하므로, 셰이더 코드가 병렬 처리를 잘 활용하도록 최적화진행
5. 렌더링 파이프라인 최적화 팁
1. 정점 데이터 최적화 :
- 필요 없는 정점 속성을 제거
- 인덱스 버퍼를 사용하여 정점 재사용 극대화
2. 셰이더 최적화 :
- 불필요한 연산 제거
- 정점과 픽셀 셰이더의 복잡성을 최소화
- 셰이더 코드의 병렬 처리 효율성을 높임
3. 렌더링 순서 최적화 :
- 투명 객체는 렌더링 후반에 처리해 블렌딩 성능 향상
- 상태 변경 최소화 : 렌더링 상태를 변경하는 횟수를 줄여 드라이버 오버헤드 감소
4. 메모리 관리 :
- 정점 버퍼와 인덱스 버퍼를 효율적으로 관리하여 메모리 대역폭 사용 최적화
- 텍스쳐 압축과 MIP맵을 사용해 텍스쳐 메모리 사용 최적화
5. 멀티패스 렌더링 최소화 :
- 가능하면 단일 패스로 모든 필요한 렌더링을 완료
- 필요한 경우 멀티패스를 효율적으로 관리하여 오버헤드 최소화