[Unreal Engine] Fundamentals of Graphics Programming

Imeamangryang·2025년 6월 27일
post-thumbnail

출처 : staticJPL - Render-Dependency-Graph-Documentation


Fundamentals of Graphics Programming

Graphics Pipeline

이 문서의 내용을 이해하려면 그래픽 렌더링 파이프라인에 대한 기본적인 이해가 필요합니다.

아래는 Vulkan 그래픽스 파이프라인의 예입니다. 그래픽스 파이프라인에 대해 이미 알고 있다면 이 섹션을 건너뛰어도 됩니다.

여러 플랫폼에서 사용되는 그래픽 파이프라인에는 여러 유형이 있으며 그 예로 Vulkan, DirectX, OpenGL 등이 있습니다. 모든 파이프라인은 Input Assembler, Vertex Shader, Rasterization and Fragment Shader (혹은 pixel shader) 와 같은 파이프라인의 공통 단계를 공유합니다.

일부 API는 Rasterizer 이전 또는 이후에 다른 단계가 있어 Rasterizer 전 후에 파이프라인 특정 작업을 해당 플랫폼에 최적화할 수 있습니다.

  • Step 1. input assembler는 사용자가 지정한 버퍼에서 raw vertex data를 수집하며, 제가 아는 한 HLSL과 같은 셰이더 코드와 결합하면 나중에 살펴보게 될 입력 시맨틱에 바인딩할 수 있습니다.
  • Step 2. Vertex shader는 모든 버텍스에 대해 실행되며 일반적으로 변환을 적용하여 버텍스 위치를 모델 공간에서 화면 공간으로 전환합니다. 또한 버텍스별 데이터를 파이프라인으로 전달합니다. 일반적으로 버텍스에서 월드 스페이스, 로컬 스페이스 또는 스크린 스페이스로 매트릭스 변환을 수행하기 전에 수행합니다. 버텍스 데이터는 표준 클립 스페이스에서 해석됩니다.

  • Step 3. Geometry shader는 모든 프리미티브(삼각형, 선, 점)에서 실행되며, 이를 버리거나 더 많은 프리미티브를 출력할 수 있습니다. 테셀레이션 셰이더와 비슷하지만 훨씬 더 유연합니다. 그러나 오늘날의 애플리케이션에서는 많이 사용되지 않으며 일부 API가 다르다는 점을 다시 한 번 강조합니다.
  • Step 4. rasterization stage에서는 프리미티브를 조각으로 분리합니다. 이것이 프레임버퍼에 채워지는 픽셀 요소입니다. 화면 외부에 있는 조각은 모두 폐기되고 그림과 같이 버텍스 셰이더가 출력한 속성이 조각 전체에 보간됩니다. 일반적으로 다른 프리미티브 조각 뒤에 있는 조각도 뎁스 테스트로 인해 여기서 버려집니다. 뎁스 테스트는 컬링 프로세스에 대한 정보를 저장하는 뎁스 버퍼와 함께 사용됩니다. 제가 알기로는 래스터라이저가 파이프라인에서 프로그래밍할 수 없는 유일한 곳입니다.
  • Step 5. Fragment Shader(또는 언리얼에서는 픽셀 셰이더)입니다. 픽셀 셰이더는 래스터화 단계에서 살아남은 모든 조각에 대해 호출되며, 조각이 어떤 프레임버퍼에 어떤 색상 및 뎁스 값과 함께 쓰여질지 결정합니다. 버텍스 셰이더의 보간된 데이터를 사용하여 이 작업을 수행할 수 있으며, 여기에는 텍스처 좌표 및 조명용 노멀 등이 포함될 수 있습니다.

보간에 대한 개념이 헷갈린다면 버텍스 셰이더와 픽셀 셰이더 사이의 이 예시 시나리오를 상상해 보세요.

두 개의 정점 값 A와 B가 있다고 가정합니다. 두 정점 모두 고유한 2D 화면 위치와 3D 픽셀 색상을 포함합니다. 이제 정점 A가 화면에서 색 값이 빨간색인 점이고 정점 B가 색 값이 파란색인 다른 점이고 A와 B를 연결하는 선을 그렸다면 다음과 같은 그림이 나타납니다.

빨간색(VA) - 보라색 - 파란색(B)

버텍스 셰이더는 A와 B를 연결하는 선을 그린 다음 픽셀 셰이더로 전달하여 A와 B 사이의 색상을 보간합니다. 포인트 A는 순수하게 빨간색이고 포인트 B는 순수하게 파란색으로 중간 색인 보라색(빨간색과 파란색이 혼합된)을 얻습니다.

  • Step 6. 픽셀 셰이더가 최종 이미지를 프레임버퍼에 넘기기 전에 API 특정 작업을 볼 수 있습니다.

GPU Buffers

이해해야 할 핵심 사항은 버퍼는 GPU의 메모리에 저장된 리소스일 뿐이라는 점입니다. 이러한 리소스는 CPU에서 선언된 다음 다양한 렌더링 프로세스 중에 사용하기 위해 GPU로 복사됩니다. 데이터를 GPU로 전달하기 전에 CPU에서 데이터를 정의할 때 정렬이 중요하므로 그래픽 API와 인터페이스할 때 패딩과 정렬에 대한 내용을 확인할 준비를 하세요. 버퍼의 종류는 매우 다양하지만 가장 일반적인 버퍼 몇 가지는 다음과 같습니다.

  • Vertex Buffer

input assembler가 읽을 모든 버텍스를 정의하는 데이터를 보유하고 GPU 파이프라인에서 지정한 버텍스 셰이더 코드에 바인딩합니다.

  • Index Buffer

Index Buffer는 버텍스 버퍼의 버텍스에 대한 포인터 배열이며, 인덱스 버퍼는 일반적으로 한 번에 세 개의 버텍스를 읽습니다. 하지만 오프셋을 지정할 수 있으므로 버텍스 버퍼에서 버텍스 셰이더로 공급할 여러 조합을 만들 수 있습니다. 화면 공간 전용 쿼드, 삼각형 또는 기타 프리미티브 등 다양한 드로 콜을 정의하여 사용할 수 있습니다.

  • Command Buffer

Command Buffer는 프레임을 렌더링하는 데 필요한 명령을 기록하는 데 사용됩니다. 이러한 명령에는 뷰포트 설정, 셰이더, 텍스처 바인딩, 지오메트리 렌더링을 위한 드로 콜 실행과 같은 작업이 포함됩니다. 명령 버퍼가 기록되면 실행을 위해 GPU에 제출할 수 있습니다.

  • Depth Buffer

Depth Buffer(Z Buffer라고도 함)는 3D 그래픽에서 화면의 각 픽셀에 대한 깊이 정보를 저장하는 데 사용되는 메모리 버퍼입니다. 장면에서 오브젝트의 상대적 깊이를 결정하고 뷰어에 더 가까운 오브젝트가 멀리 있는 오브젝트 위에 그려지도록 하는 데 사용됩니다.

  • GBuffer

“Geometry Buffer"라고도 하는 GBuffer는 3D 씬의 지오메트리에 대한 다양한 정보를 저장하는 데 사용되는 여러 개의 오프스크린 렌더 타깃 세트입니다. 이 정보에는 깊이, 노멀, 알베도(색상), 스페큘러 및 기타 데이터 등이 포함될 수 있습니다. GBuffer는 일반적으로 장면의 지오메트리를 캡처하는 프로세스와 해당 지오메트리에 조명 및 셰이딩을 적용하는 프로세스를 분리하는 기술인 deferred shading에 사용됩니다.

  • Texture Buffer

Texture Buffer는 텍스처 데이터를 저장하는 데 사용되는 GPU 버퍼입니다. 텍스처는 장면에 시각적 디테일과 사실감을 더하기 위해 3D 모델의 표면에 적용되는 2D 또는 3D 이미지입니다. 텍스처 버퍼는 텍스처의 픽셀 데이터를 저장하는 데 사용되며, 여기에는 색상, 알파, 노멀 정보 등이 포함될 수 있습니다.

HLSL

언리얼 엔진에서 셰이더 코드는 파일 확장자 형식이 .usf 및 .ush인 HLSL을 사용하여 작성됩니다.

저는 HLSL을 어셈블리 코드와 비슷하다고 상상하고 싶습니다. 입력 레지스터와 출력 레지스터가 있습니다. 버텍스 셰이더에서 입력을 정의하면 셰이더는 이를 픽셀 셰이더에 입력으로 출력합니다.

Regular semantics은 파이프라인의 특정 단계에 대한 입력 및 출력 데이터의 의미를 정의하는 데 사용됩니다. 여기에는 위치, 색상, 노멀, 텍스처 좌표 등이 포함될 수 있습니다. 이를 “Regular Semantics”이라고 부르고 원하는 이름을 지정할 수 있지만, 앞서 설명한 대로 입력 어셈블러를 통해 파이프라인에서 이러한 리소스(버퍼)를 바인딩해야 합니다.

반면에 'System semantics'은 특정 플랫폼이나 API에 특정한 입력 및 출력 데이터의 의미를 정의하는 데 사용됩니다. 이러한 시맨틱은 DirectX 또는 OpenGL API와 같이 GPU와 API 간에 데이터가 전달되는 방식을 정의하는 데 사용됩니다.

예를 들어, 특정 입력 변수에 DirectX 전용 시스템 시맨틱인 버텍스 ID가 포함되어 있음을 나타내기 위해 “SV_VERTEXID” 시맨틱을 사용할 수 있습니다. 이 시맨틱은 버텍스 셰이더가 현재 사용 중인 버텍스 버퍼가 처리 중인 현재 버텍스에 따라 SV_VERTEXID 시맨틱이 증가하는 DirectX 전용 드로 콜에 연결됩니다.

아래는 나중에 트라이앵글을 그리기 위해 바인딩할 트라이앵글 HLSL 코드입니다.

// 커스텀 버텍스 셰이더의 진입점을 위한 TriangleVS Binable Name  
void TriangleVS( 
	in float2 InPosition : ATTRIBUTE0,    // First Input Bindable Regular Symantic
	in float4 InColor : ATTRIBUTE1,       // Second Input Bindable Regular Symantic 
	out float4 OutPosition : SV_POSITION, // System Symantic Ouput position to Pixel Shader 
	out float4 OutColor : COLOR0          // System Symantic Output Color to Pixel Shader 
	) 
{
	OutPosition = float4(InPosition, 0, 1); 
	OutColor = InColor; 
} 

// 커스텀 픽셀 셰이더의 TrianglePS 바인딩 가능 진입점
void TrianglePS( 
	in float4 InPosition : SV_POSITION, // System Symantic Input position to Pixel Shader 
	in float4 InColor : COLOR0,         // System Symantic Input Color to Pixel Shader  
	out float4 OutColor : SV_Target0)   // System Symantic Render Target, IE. The 2D texture resource Render to. 
{
	OutColor = InColor; 
}
profile
언리얼 엔진 주니어 개발자 입니다.

0개의 댓글