티스토리 뷰
실제 셰이더 코드는 각 ShaderLab Pass 내부에 HLSL (High level shading language)로 작성됩니다.
때로는 "cg"라고도하는 것을 볼 수 있습니다. Cg와 HLSL (적어도 DX9 스타일)은 기본적으로 동일한 언어로 취급되지만 cg는 더 이상 사용되지 않으며 몇 년 동안만 사용되었습니다. 그럼에도 불구하고 내장 파이프 라인 셰이더에서는 일반적으로 CGPROGRAM과 ENDCG (및 CGINCLUDE) 간의 코드를 볼 수 있습니다. 이러한 태그에는 HLSLSupport.cginc 및 UnityShaderVariables.cginc와 같은 일부 Builtin-Include 파일이 자동으로 포함됩니다.
그러나 URP에서 이러한 CG 태그를 사용하면 많은 변수와 함수가 두 번 정의되므로 URP ShaderLibrary와 충돌이 발생합니다. URP는 이러한 파일을 포함하지 않으므로 HLSLPROGRAM/HLSLINCLUDE 및 ENDHLSL을 대신 사용해야합니다.
SCALAR VARIABLES
HLSL의 변수는 일반적으로 다음과 같은 스칼라 데이터 유형으로 구성됩니다.
- bool – 참 또는 거짓.
- float – 32 비트 부동 소수점 숫자. 일반적으로 world space position, 텍스처 좌표 또는 삼각법 또는 거듭 제곱/지수와 같은 복잡한 함수와 관련된 스칼라 계산에 사용됩니다.
- half – 16 비트 부동 소수점 숫자. 일반적으로 짧은 벡터, 방향, 물체 공간 위치, 색상에 사용됩니다.
- double – 64 비트 부동 소수점 숫자. 입력/출력으로 사용할 수 없습니다. (참고)
- fixed – 내장 셰이더에서만 사용되며 URP에서는 지원되지 않습니다. 대신 절반을 사용합니다.
- real – 셰이더가 "#define PREFER_HALF 0"을 지정하지 않는 한 (플랫폼에서 지원된다고 가정 할 때) 이것이 기본적으로 절반으로 설정되어 있다고 생각합니다. 그러면 float 정밀도가 사용됩니다.
- int – 32 비트 부호있는 정수
- uint – 32 비트 부호없는 정수 (지원되지 않고 대신 int로 정의되는 GLES2 제외).
VECTOR
벡터는 이러한 스칼라 데이터 유형 중 하나에 한개에서 네개까지 정수를 추가하여 생성할 수 있습니다.
- float4 – (4 개의 부동 소수점을 포함하는 벡터)
- half3
- int2 등
벡터의 구성 요소 중 하나를 얻으려면 .x, .y, .z 또는 .w를 사용할 수 있습니다. (대신 .r, .g, .b, .a도 사용할 수 있습니다.) 또한 이러한 벡터를 여러번 사용해서 재배열하여 다른 벡터를 얻을 수도 있습니다. 이것을 swizzling이라고합니다.
float3 vector = float3(1, 2, 3);
float3 a = vector.xyz; // (1, 2, 3), same as vector.rgb
float3 b = vector3.zyx; // (3, 2, 1), vector.bgr
float3 c = vector.xxx; // (1, 1, 1), vector.rrr
float2 d = vector.zy; // (3, 2), vector.bg
float4 e = vector.xxzz; // (1, 1, 3, 3), vector.rrbb
float f = vector.y; // 2, vector.g
// Note that something like "vector.rx" is not allowed.
// Use vector.rr or vector.xx instead.
MATRIX
스칼라에 1과 4 사이의 두 정수를 "x"로 구분하여 추가하여 행렬을 생성 할 수 있습니다. 첫번째 정수는 행수이고 두번째 정수는 행렬의 열수입니다. (float4x4 : 4행4열 int4x3 : 4행3열, half2x1 : 2행1열, float1x4 : 1행4열)
float3x3 matrix = {0,1,2,
3,4,5,
6,7,8};
float3 row0 = matrix[0]; // (0, 1, 2)
float3 row1 = matrix[1]; // (3, 4, 5)
float3 row2 = matrix[2]; // (6, 7, 8)
float row1column2 = matrix[1][2]; // 5
// Note we could also do
float row1column2 = matrix[1].z;
행렬은 일반적으로 서로 다른 좌표 공간 간의 변환에 사용됩니다. 이렇게하려면 행렬곱셈을 수행해야합니다. 이는 mul 함수를 사용하여 수행 할 수 있습니다 (* 연산자는 행렬 * 벡터 유형에 대해 작동하지 않음). 예를 들어, 오브젝트 공간에서 월드 공간으로의 변환은 다음을 통해 수행됩니다.
mul(GetObjectToWorldMatrix(), float4(positionOS, 1.0)).xyz;
// Where GetObjectToWorldMatrix() returns "UNITY_MATRIX_M"
// Which is the model matrix that Unity passes in for the object
이것이 TransformObjectToWorld 함수가하는 일입니다. 행렬과 벡터의 순서는 mul (x, y) 함수에서 중요합니다.
(행렬은 공간에대한 사상이라고 보면, 행렬곱은 "mul(A,B)일때 B공간을 A공간으로 변환시킨다"고 표현할 수 있습니다.)
ARRAYS
Shaderlab 프로퍼티 또는 마테리얼 인스펙터에서 지원되지 않고 C# 스크립트에서 설정해야하지만 셰이더에서 배열을 지정할 수 있습니다. 배열의 크기는 셰이더에서 지정해야하며 문제를 방지하기 위해 일정하게 유지되어야합니다. 배열의 크기를 모르는 경우 최대 값을 설정하고 0으로 배열 패딩을 전달해야합니다. 여기 예제와 같이 배열을 반복해야 할 때 다른 부동 소수점을 길이로 지정할 수 있습니다.
float _Array[10]; // Float array
float4 _Array[10]; // Vector array
float4x4 _Array[10]; // Matrix array
float 배열을 설정할 때 material.SetFloatArray 또는 Shader.SetGlobalFloatArray를 사용하십시오. SetVectorArray, SetMatrixArray도 글로벌 버전도 있습니다.
OTHER TYPES
HLSL에는 텍스처 및 샘플러와 같은 다른 유형도 포함되어 있으며 URP에서 다음 매크로를 사용하여 정의 할 수 있습니다.
TEXTURE2D(textureName);
SAMPLER(sampler_textureName);
버퍼도 있지만 실제로 사용한 적이 없어서 사용 방법에 익숙하지 않습니다. material.SetBuffer 또는 Shader.SetGlobalBuffer를 사용하여 C#에서 설정됩니다.
#ifdef SHADER_API_D3D11
StructuredBuffer<float3> buffer;
#endif
// I think this is only supported in Direct3D 11?
// and also require #pragma target 4.5 or higher?
// see https://docs.unity3d.com/Manual/SL-ShaderCompileTargets.html
흐름 제어 (if, for, while 등)와 같은 HLSL의 다른 부분을 살펴볼 수도 있지만, 익숙한 경우 구문은 기본적으로 C #과 동일합니다. 여기에서 HLSL에서 지원하는 모든 연산자 목록을 찾을 수도 있습니다.
FUNCTIONS
HLSL에서 함수를 선언하는 것은 C #과 매우 유사합니다. 예를 들면 다음과 같습니다.
float3 example(float3 a, float3 b){
return a * b;
}
float3이 반환 유형 인 경우 example은 함수 이름이고 대괄호 안에는 함수에 전달 된 매개 변수가 있습니다. 반환 유형이없는 경우 void가 사용됩니다. 매개 변수 유형 앞에 "out"을 사용하여 출력 매개 변수를 지정하거나 편집하고 다시 전달할 수있는 입력이되도록하려면 "inout"을 사용할 수도 있습니다.
함수 반환 유형 앞에 "inline"이 표시 될 수도 있습니다. 이것은 default modifier이며 함수가 실제로 가질 수 있는 유일한 수정 자이므로 지정하는 것이 중요하지 않습니다. 이는 컴파일러가 각 호출에 대해 함수 사본을 생성 함을 의미합니다. 이것은 함수 호출의 오버 헤드를 줄이기 위해 수행됩니다.
다음과 같은 기능도 볼 수 있습니다.
#define EXAMPLE(x, y) ((x) * (y))
이를 매크로라고합니다. 매크로는 셰이더를 컴파일하기 전에 처리되며 사용시 매개 변수가 대체 된 정의로 대체됩니다.
float f = EXAMPLE(3, 5);
float3 a = float3(1,1,1);
float3 f2 = EXAMPLE(a, float3(0,1,0));
// becomes :
float f = ((3) * (5));
float a = float(1,1,1);
float3 f2 = ((a) * (float3(0,1,0)));
// then the shader is compiled.
// Note that the macro has () around x and y.
// This is because we could do :
float b = EXAMPLE(1+2, 3+4);
// becomes :
float b = ((1+2) * (3+4)); // 3 * 7, so 21
// If those () wasn't included, it would instead be :
float b = (1+2*3+4)
// which equals 11 due to * taking precedence over +
function으로는 할수 없는 일도 할수 있습니다.
#define TRANSFORM_TEX(tex,name) (tex.xy * name##_ST.xy + name##_ST.zw)
// Usage :
OUT.uv = TRANSFORM_TEX(IN.uv, _MainTex)
// becomes :
OUT.uv = (IN.uv.xy * _MainTex_ST.xy + _MainTex_ST.zw);
"##"연산자는 매크로가 유용 할 수있는 특별한 경우입니다. 이름과 _ST 부분을 연결하여이 사용 입력에 대해 _MainTex_ST를 생성 할 수 있습니다. ## 부분이 생략 된 경우 "name_ST"만 생성되어 정의되지 않았으므로 오류가 발생합니다. (물론 _MainTex_ST도 정의해야하지만, 텍스처 이름에 _ST를 추가하는 것이 Unity가 텍스처의 타일링 및 오프셋 값을 처리하는 방식이므로 의도 된 동작입니다.)
BEGINNING THE SHADER
셰이더는 일반적으로 정점 셰이더와 조각 셰이더의 두 단계로 구성됩니다. 간단히 말해, 정점 셰이더는 메시의 각 정점에 대해 실행되는 반면 프래그먼트는 화면에 표시 될 모든 픽셀에 대해 실행됩니다. 일부 fragment는 버릴 수 있으므로 실제 픽셀과 동일하지는 않습니다. (알파클립/컷아웃 셰이더 및 스텐실 셰이더) 헐/도메인(테셀레이션 용) 및 지오메트리 셰이더와 같은 추가 셰이더도 있지만 여기에서는 설명하지 않겠습니다. 기본 제공 파이프 라인에서와 동일한 방식으로 작동한다고 말할 수 있습니다.
Shaderlab 예제에서는 서브 셰이더 내부의 모든 패스에 코드를 자동으로 포함하는 HLSLINCLUDE가 있습니다. 이것을 사용하는 것은 필수는 아니지만 UnityPerMaterial CBUFFER가 모든 패스에서 동일하도록 보장 할 수 있으므로 도움이됩니다. 이는 셰이더가 SRP Batcher와 호환되도록하는 데 필요합니다. 이 CBUFFER는 노출 된 모든 속성을 포함해야합니다 (Shaderlab 속성 블록에서와 동일). 노출되지 않은 다른 변수를 포함 할 수 없으며 텍스처를 포함 할 필요는 없습니다.
참고 : 변수는 C# material.SetColor/SetFloat/SetVector 등을 통해 설정하기 위해 공개할 필요는 없습니다. 여러 머티리얼 인스턴스에 다른 값이있는 경우 SRP Batcher가 화면에서 계속 함께 배치하므로 오동작이 발생할 수 있습니다. 노출되지 않은 변수가 있는 경우 항상 Shader.SetGlobalColor/Float/Vector 등을 사용하여 설정하여 모든 재질 인스턴스에 대해 일정하게 유지되도록합니다. 재질마다 달라야하는 경우 Shaderlab 프로퍼티 블록을 통해 노출하고 대신 CBUFFER에 추가합니다.
HLSLINCLUDE
#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
CBUFFER_START(UnityPerMaterial)
float4 _BaseMap_ST;
float4 _BaseColor;
//float4 _ExampleDir;
//float _ExampleFloat;
CBUFFER_END
ENDHLSL
위와 같이 #include를 사용하여 URP ShaderLibrary의 Core.hlsl도 포함합니다. 이것은 빌트인 파이프 라인 UnityCG.cginc와 동등한 URP입니다. Core.hlsl (및 자동으로 포함되는 다른 ShaderLibrary 파일)에는 유용한 함수와 매크로 (함수와 유사하지만 셰이더가 컴파일되기 전에 처리됨)가 포함되어 있습니다.
HLSLPROGRAM에서 가장 먼저해야 할 일 중 하나는 정점 및 조각 셰이더를 지정하는 것입니다. 여기에 각 함수의 이름을 입력합니다. 일반적으로 "vert"및 "frag"가 사용되지만 어떤것이든 될 수 있습니다.
HLSLPROGRAM
#pragma vertex vert
#pragma fragment frag
...
ENDHLSL
이러한 함수를 정의하기 전에 몇 가지 구조체가 필요합니다. 이에 대해서는 다음 섹션에서 설명하겠습니다.
포함되지 않은 일부 ShaderLibrary 파일 (예 : Lighting.hlsl)도 있습니다. 지금은이 예제가 Unlit 셰이더이므로 필요하지 않지만 이후 섹션에서 Lit 예제도 살펴 보겠습니다.
참고
cyangamedev.wordpress.com/2020/06/05/urp-shader-code/3/
en.wikipedia.org/wiki/Cg_(programming_language)
docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-scalar
www.alanzucconi.com/2016/10/24/arrays-shaders-unity-5-4/
docs.unity3d.com/ScriptReference/Material.SetFloatArray.html
docs.unity3d.com/ScriptReference/Shader.SetGlobalFloatArray.html
docs.unity3d.com/ScriptReference/Material.SetBuffer.html
docs.unity3d.com/ScriptReference/Shader.SetGlobalBuffer.html
docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-flow-control
docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl-operators
'Unity > Shader' 카테고리의 다른 글
Fragment Shader (0) | 2021.01.21 |
---|---|
Vertex Shader (0) | 2021.01.21 |
Post Processing in the Universal RP (0) | 2021.01.20 |
Structs (0) | 2021.01.20 |
Shaderlab (0) | 2021.01.19 |
- Total
- Today
- Yesterday