
이 문서의 내용을 이해하려면 그래픽 렌더링 파이프라인에 대한 기본적인 이해가 필요합니다.
아래는 Vulkan 그래픽스 파이프라인의 예입니다. 그래픽스 파이프라인에 대해 이미 알고 있다면 이 섹션을 건너뛰어도 됩니다.

여러 플랫폼에서 사용되는 그래픽 파이프라인에는 여러 유형이 있으며 그 예로 Vulkan, DirectX, OpenGL 등이 있습니다. 모든 파이프라인은 Input Assembler, Vertex Shader, Rasterization and Fragment Shader (혹은 pixel shader) 와 같은 파이프라인의 공통 단계를 공유합니다.
일부 API는 Rasterizer 이전 또는 이후에 다른 단계가 있어 Rasterizer 전 후에 파이프라인 특정 작업을 해당 플랫폼에 최적화할 수 있습니다.

보간에 대한 개념이 헷갈린다면 버텍스 셰이더와 픽셀 셰이더 사이의 이 예시 시나리오를 상상해 보세요.
두 개의 정점 값 A와 B가 있다고 가정합니다. 두 정점 모두 고유한 2D 화면 위치와 3D 픽셀 색상을 포함합니다. 이제 정점 A가 화면에서 색 값이 빨간색인 점이고 정점 B가 색 값이 파란색인 다른 점이고 A와 B를 연결하는 선을 그렸다면 다음과 같은 그림이 나타납니다.
빨간색(VA) - 보라색 - 파란색(B)
버텍스 셰이더는 A와 B를 연결하는 선을 그린 다음 픽셀 셰이더로 전달하여 A와 B 사이의 색상을 보간합니다. 포인트 A는 순수하게 빨간색이고 포인트 B는 순수하게 파란색으로 중간 색인 보라색(빨간색과 파란색이 혼합된)을 얻습니다.
이해해야 할 핵심 사항은 버퍼는 GPU의 메모리에 저장된 리소스일 뿐이라는 점입니다. 이러한 리소스는 CPU에서 선언된 다음 다양한 렌더링 프로세스 중에 사용하기 위해 GPU로 복사됩니다. 데이터를 GPU로 전달하기 전에 CPU에서 데이터를 정의할 때 정렬이 중요하므로 그래픽 API와 인터페이스할 때 패딩과 정렬에 대한 내용을 확인할 준비를 하세요. 버퍼의 종류는 매우 다양하지만 가장 일반적인 버퍼 몇 가지는 다음과 같습니다.
input assembler가 읽을 모든 버텍스를 정의하는 데이터를 보유하고 GPU 파이프라인에서 지정한 버텍스 셰이더 코드에 바인딩합니다.
Index Buffer는 버텍스 버퍼의 버텍스에 대한 포인터 배열이며, 인덱스 버퍼는 일반적으로 한 번에 세 개의 버텍스를 읽습니다. 하지만 오프셋을 지정할 수 있으므로 버텍스 버퍼에서 버텍스 셰이더로 공급할 여러 조합을 만들 수 있습니다. 화면 공간 전용 쿼드, 삼각형 또는 기타 프리미티브 등 다양한 드로 콜을 정의하여 사용할 수 있습니다.
Command Buffer는 프레임을 렌더링하는 데 필요한 명령을 기록하는 데 사용됩니다. 이러한 명령에는 뷰포트 설정, 셰이더, 텍스처 바인딩, 지오메트리 렌더링을 위한 드로 콜 실행과 같은 작업이 포함됩니다. 명령 버퍼가 기록되면 실행을 위해 GPU에 제출할 수 있습니다.
Depth Buffer(Z Buffer라고도 함)는 3D 그래픽에서 화면의 각 픽셀에 대한 깊이 정보를 저장하는 데 사용되는 메모리 버퍼입니다. 장면에서 오브젝트의 상대적 깊이를 결정하고 뷰어에 더 가까운 오브젝트가 멀리 있는 오브젝트 위에 그려지도록 하는 데 사용됩니다.
“Geometry Buffer"라고도 하는 GBuffer는 3D 씬의 지오메트리에 대한 다양한 정보를 저장하는 데 사용되는 여러 개의 오프스크린 렌더 타깃 세트입니다. 이 정보에는 깊이, 노멀, 알베도(색상), 스페큘러 및 기타 데이터 등이 포함될 수 있습니다. GBuffer는 일반적으로 장면의 지오메트리를 캡처하는 프로세스와 해당 지오메트리에 조명 및 셰이딩을 적용하는 프로세스를 분리하는 기술인 deferred shading에 사용됩니다.
Texture Buffer는 텍스처 데이터를 저장하는 데 사용되는 GPU 버퍼입니다. 텍스처는 장면에 시각적 디테일과 사실감을 더하기 위해 3D 모델의 표면에 적용되는 2D 또는 3D 이미지입니다. 텍스처 버퍼는 텍스처의 픽셀 데이터를 저장하는 데 사용되며, 여기에는 색상, 알파, 노멀 정보 등이 포함될 수 있습니다.
언리얼 엔진에서 셰이더 코드는 파일 확장자 형식이 .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;
}