본 게시물은 Unity Korea 채널의 URP Shader Training 시리즈와 강의에 사용된 도큐먼트를 공부하며 정리한 게시물입니다.
Unity SRP는 주로 HLSL로 작성되며 HLSLPROGRAM으로 시작하며 ENDHLSL로 끝나는 구조를 가지고 있다.
C언어에서 pragma는 컴파일러가 입력을 처리하는 방법을 지정하는 언어 문법 -> Shader가 어떻게 컴파일 될지를 지시하는 전처리 문법
//예시
#pragma prefer_hlslcc gles
![]()
- Input Assembly : GPU는 메모리에서 버텍스 및 인덱스 버퍼를읽고, Vertex가 Tri를 형성하는 방식을 결정 -> 나머지는 Pipeline에 전달
- Vertex Shading : 한 번에 하나의 버텍스에서 실행되는 Mesh의 모든 Vertex에 대해 한 번 실행됨.
여기에서 Vertex를 변환하고 위치를 받은 다음 카메라 및 Viewport 설정을 사용해 화면에서 최종 위치를 계산- Rasterization : Vertex Shader가 삼각형의 각 Vertex에 실행되고 GPU가 화면에 표시될 위치를 알고 있으면 삼각형이 Rasterization되어 개별 픽셀의 모음으로 변환됨
Vertex별 정보(UV좌표, Vertex Color, Normal등)은 삼각형 Pixel을 통해 보간됨
(Rasterization된 픽셀은 각 Vertex의 보간된 값을 가지게 된다)
- Pixel Shading : 레스터화 된 각 픽셀은 Pixel Shader를 통해 실행(기술적으로는 아직 픽셀이 아닌 Pragment Shader라고 함)
Material 속성, Texture, 광원등 기타 Parameter를 프로그래밍 된 방식으로 결합해 특정 색상을 얻어 픽셀에 색상 부여- Render Target Output : 마지막으로 픽셀은 Rendering 대상에 쓰여지지만 일부 테스트를 거쳐야 유효하게 된다.
EX_Depth Test는 이미 렌더링 대상보다 멀리 떨어진 픽셀을 생략할 수 있다.
이러한 모든 Test(Depth, Alpha, Stencil등)를 통과하면 메모리의 렌더링 타겟에 쓰여지게 됨.- Z Sort 방식 : 렌더링 하는 물체를 카메라 시점으로부터 거리를 Sorting(정렬)하고, 시점으로부터 먼 것부터 순서대로 렌더링 하는 방식
(Mesh가 겹칠 경우 Sorting Issue 발생)- Z Buffer 방식 : 물체를 Rendering하면서 그 물체까지의 거리를 픽셀(텍셀) 단위로 Depth Buffer에 저장해두고 다음에 렌더링 할때 이미 렌더링 되어 있는 물체의 거리와 렌더링 하려는 물체의 거리를 비교하면서 앞에 있으면 렌더링(반투명의 경우 렌더링 어려움)
Input Assembly 단계에서 VertexBuffer에서 필요한 정보를 가져오는 역할을 한다.
//예시
struct VertexInput
{
float4 vertex : POSITION;
};
| Type(s) | Name(변경 가능) | Semantic | Notes |
|---|---|---|---|
| float4 | vertex | POSITION | Local 공간(Model 공간)의 정점 위치 |
| float3 | normal | NORMAL | Vertex의 Normal |
| float4 | texcoord[n] | TEXCOORD[n] | Vertex의 UV좌표 |
| float4 | tangent | TANGENT | Mesh에서 계산된 또는 Import된 Tangent값 |
| float4 | color | COLOR | Vertex의 Color값 |
//예시
LitForwardPAss.hlsl
struct Attributes
{
float4 positionOS :POSITION;
float3 normalOS :NORMAL;
float4 tangentOS :TANGENT;
float2 texcoord :TEXCOORD0;
float2 lightmapUV :TEXCOORD1;
UNITY_VERTEX_INPUT_INSTANCE_ID
};
LitMetaPass.hlsl
struct Attributes
{
float4 positionOS :POSITION;
float3 normalOS :NORMAL;
float2 uv0 :TEXCOORD0;
float2 uv1 :TEXCOORD1;
float2 uv2 :TEXCOORD2;
#ifdef _TANGENT_TO_WORLD
float4 tangentOS :TANGENT;
#endif
};
URP Lit Shader에서는 LitForwardPass.hlsl이나 GbufferPass.hlsl, LitMetaPass.hlsl등, 각 패스별 hlsl파일에 선언되어 있음.
| Type(s) | Name(변경 가능) | Semantic | Notes |
|---|---|---|---|
| float4 | pos | SV_POSITION | 투영 공간으로 변화된 후의 Vertex Position |
| float3 | normal | NORMAL | 뷰 공간으로 변형된 후 Vertex의 Normal값 |
| float4 | uv | TEXCOORD[n] | 첫 번째 UV 채널 좌표값 |
| float4 | tangent | TANGENT | Tangent값 |
| float4 | diff, spec | COLOR | Vertex Color 혹은 그 외의 임의의 컬러값 |
| Any | Any | 사용자 정의 | Vertex stage에서 계산한 값을 Pixel Shader로 전달할 때 임의로 정의할 수 있음 |
interpolate(보간기)의 숫자는 Shader Model에 영향을 받는다. Advanced의 #pragma target설정 참고
//Varying Struct는 사용자 정의이므로 임의로 선언할 수 있다.
struct Varying
{
float4 positionCS :SV_POSITION;
float2 uc :TEXCOORD0;
};
//Vertex Function(Vertex Shader Stage)
VertexOutput vert(VertexInput v)
{
VertexOutput o;
o.vertex = TransformObjectToHClip(v.vertex.xyz);
return o;
}
Vertex Shader의 주 목적은 3D의 정보를 2D로 변환하는 것.
이를 위해 필요한 정보를 Vertex buffer에서 가져온 값을 사용(VertexInput v), 계산된 결과를 (VertexOutput o)통해 보간기로 Pixel Shader에 전달하게 됨.
위 예제 코드는 Vertex의 포지션 정보만을 계산해 화면에 그리는 간단한 코드
3D Mesh의 각 Vertex를 사용하는 툴의 3D 공간으로 변환하는 과정을 거치게 됨.
이 변환 과정은 간략하게 [Object Space]->[World Space]->[Camera Space]->[Clip Space]
Local Space, Objet Space, 물체 공간 모두 다 같은 의미로 오브젝트 별로 가지고 있는 자기만의 공간을 월드 공간내의 좌표로 변환하는 과정
![]()
//Core/ShaderLibrary/SpaceTransform.hlsl에서 확인할 수 있음
float4x4 GetObjectToWorldMatrix()
{
return UNITY_MATRIX_M;
}
...
float3 TransformObjectToWorld(float3 positionOS)
{
return mul(GetObjectToWorldMatrix(), float(positionOS, 1.0)).xyz;
}
![]()