2023. 6. 8. 17:22ㆍUnity
ShaderLab
ShaderLab은 범용적인 HLSL만으로는 유니티 3D 엔진만의 여러 기능들을 제어할 수 없어서 만든 규칙이다. OpenGL이나 HLSL 같은 셰이더 언어는 유니티 엔진이 존재하기 전부터 사용된 언어이기 때문.
특징은 간단한 문법 구조를 가지고 있고 엔진 기능들에 관련된 것들이다. 직접적으로 셰이딩 연산에 관한 것들은 없다.
// 이것은 주석입니다. (Comment)
Shader "Custom/ShaderLab HLSL/CustomUnlit"
{
Properties
{ }
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
};
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
return OUT;
}
half4 frag(Varyings IN) : SV_Target
{
half4 color = half4(0, 1, 0, 1);
return half4(0, 1, 0, 1);
}
ENDHLSL
}
}
}
위의 예시 코드에서 HLSLPROGRAM부터 ENDHLSL까지가 HLSL이고 나머지가 ShaderLab이다. ShaderLab 영역은 세미콜론 없이 끝난다.
HLSL 연산자 표기
간략한 표기들
color = color * _Value; -> color *= _Value;
a = a + b; -> a += b;
a = a - b; -> a -= b;
0.5 -> .5
HLSL 블록 구조
위의 예시 코드를 뜯어보면,
#pragma vertex vert
#pragma라는 컴파일 명령어가 사용되었다. 셰이더 컴파일러에게 Vertex Shader는 vert 함수라는 것을 알려준다. vert 이름은 관례로 사용된다.
pragma 지시문에 대한 매뉴얼 (https://docs.unity3d.com/kr/2021.2/Manual/SL-PragmaDirectives.html)
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
Core.hlsl 파일을 포함하라는 전 처리 지시어(Preprocessiong directive)이다. 셰이더가 컴파일되기 전에 미리 해당 경로의 파일을 포함시키라는 뜻이다. 전체 경로를 지정하지 않으면 현재 셰이더와 같은 폴더를 사용한다. 밑에 "TransformObjectToHClip" 함수가 Core.hlsl에 정의되어 있어서 포함시키지 않으면 에러가 발생한다.
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
};
구조체(struct) 사용.
Attributes라는 새로운 자료형을 정의하고 멤버 변수는 float4 positionOS 하나이다. 이 변수가 GPU의 POSITION 정보와 연결된다.
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
return OUT;
}
IN 변수를 Attributes 자료형으로 초기화 한다. Attributes 자료형의 멤버 변수로 positionOS가 있으므로 IN 변수도 positionOS 구성 성분을 가지게 된다.
vert는 함수 이름이지만 함수의 자료형이 Varyings라는 뜻이다. 이 때문에 함수의 최종 리턴 값이 Varyings의 자료형인 OUT 변수가 반환된다.
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
버텍스 셰이더에서 유일하게 수행하는 연산이다. TransformObjectToHClip 함수는 셰이더에 입력되는 버텍스의 로컬 좌표를 카메라 기준에서의 좌표계로 바꿔준다. 이때 원근감도 적용된다.
half4 frag(Varyings IN) : SV_Target
{
half4 color = half4(0, 1, 0, 1);
return half4(0, 1, 0, 1);
}
최종 프래그먼트 셰이더 부분이다. 픽셀의 색상을 연산하는 곳이다.
_BaseColor 프로퍼티 추가
// 이것은 주석입니다. (Comment)
Shader "Custom/ShaderLab HLSL/CustomUnlitColor"
{
Properties
{
[MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
}
SubShader
{
Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }
Pass
{
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
struct Attributes
{
float4 positionOS : POSITION;
};
struct Varyings
{
float4 positionHCS : SV_POSITION;
};
CBUFFER_START(UnityPerMaterial)
half4 _BaseColor;
CBUFFER_END
Varyings vert(Attributes IN)
{
Varyings OUT;
OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
return OUT;
}
half4 frag(Varyings IN) : SV_Target
{
return _BaseColor;
}
ENDHLSL
}
}
}
이전 코드와 비교해서 달라진 점들이 있다.
Shader "Custom/ShaderLab HLSL/CustomUnlitColor"
셰이더 이름이 변경되었다. 슬래시로 카테고리를 구분하며 전체 경로는 큰 따옴표 안에 있어야 한다.
Properties
{
[MainColor] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
}
Properties는 셰이더 그래프의 블랙보드와 같은 역할을 한다. [MainColor]는 프로퍼티의 속성(Attribute)이다.
[MainColor] 속성이 없으면 유니티 엔진은 이름 규칙으로 "_Color"만 메인 컬러로 인식하지만 [MainColor] 속성에 의해 "_Color" 이름이 아니어도 메인 컬러로 지정된다.
메인 컬러 속성 여부는 셰이더에 아무런 영향이 없고 프로그래머가 재질과 셰이더를 다룰 때 굳이 셰이더 코드를 몰라도 메인 컬러 프로퍼티를 바로 사용할 때 필요하다. [MainColor] 속성이 없어도 아무 문제없다.
[MainColor][HDR] _BaseColor("Base Color", Color) = (1, 1, 1, 1)
[HDR]을 넣으면 HDR 속성을 사용할 수 있다.
CBUFFER_START(UnityPerMaterial)
half4 _BaseColor;
CBUFFER_END
HLSL 코드 내에서 _BaseColor 변수를 선언하였다. 바로 위에서 이미 선언했었는데 또 선언한 이유는 앞선 선언은 ShaderLab의 문법으로 재질 에디터에 프로퍼티를 보여주기만 할 뿐이었기 때문이다. 셰이더 그래프에서 블랙보드에 프로퍼티 만들고 쓰지는 않은 모양이랑 같다.
- 프로퍼티 변수는 HLSL에서 한 번 더 선언해야 한다.
- HLSL에서 변수를 선언하는 위치는 변수를 사용하는 위치보다 앞선 위치에 있어야 한다.
half 자료형은 float에 비해 낮은 정밀도이기 때문에 메모리를 작게 차지하므로 셰이더 최적화에 도움을 준다. 월드 위치나 UV 좌표처럼 높은 정밀도를 요구하지 않는다면 half 정밀도도 충분하다.
텍스쳐와 샘플러를 제외한 모든 변수들은 CBUFFER의 START와 END로 묶어준다. 이것은 SRP Batcher를 위한 문법이다. CBUFFER라는 GPU 메모리 영역에 프로퍼티 값을 저장함으로써 서로 다른 재질이라도 Set Pass Call을 최소화하는 Batching 최적화를 한다.
half4 frag(Varyings IN) : SV_Target
{
return _BaseColor;
}
최종 컬러 값이 반환된다.
참고자료
'Unity' 카테고리의 다른 글
[Unity] URP 셰이더 HLSL Lambert 셰이더 (0) | 2023.06.10 |
---|---|
[Unity] URP 셰이더 HLSL 텍스쳐 적용 (0) | 2023.06.09 |
[Unity] URP 셰이더 Gamma와 sRGB (0) | 2023.06.07 |
[Unity] URP 셰이더 Z Buffer와 반투명(Translucent) (0) | 2023.06.07 |
[Unity] URP 셰이더 Sub Graph, Custom Lighting, Keyword (0) | 2023.06.05 |