4. 함수
1. 사용자 정의 함수
HLSL 함수의 특징이다.
1. 익숙한 C++의 함수 구문을 사용한다.
2. 매개변수는 항상 값으로 전달된다.
3. 재귀를 지원하지 않는다.
4. 항상 인라인화된다.
// in: 입력용 변수 out: 출력용 변수 inout: 입출력용 변수
bool foo(in const bool b, out int r1, inout float r2)
{
if(b)
{
r1 = 5; // r1을 통해 하나의 값을 출력한다.
}
else
{
r1 = 1; // r1을 통해 하나의 값을 출력한다.
}
// r2는 inout이므로 입력 값이 될 수도 출력 값이 될 수도 있다.
r2 = r2 * r2 * r2;
return true;
}
2. 내장 함수
https://docs.microsoft.com/ko-kr/windows/win32/direct3dhlsl/dx-graphics-hlsl-intrinsic-functions
내장 함수 - Win32 apps
다음 표에서는 HLSL에서 사용할 수 있는 내재 함수를 나열합니다. 각 함수에는 간단한 설명과 입력 인수 및 반환 형식에 대한 자세한 내용이 있는 참조 페이지에 대한 링크가 있습니다.
docs.microsoft.com
3. 상수 버퍼의 압축 및 채움
HLSL에서 상수 버퍼의 요소들을 4차원 벡터 단위로 저장하는데, 이 때 HLSL은 하나의 요소가 두 4차원 벡터에 걸쳐서 저장되어서는 안된다는 규칙을 강제한다.
// HLSL
cbuffer cb : register(b0)
{
float3 Pos;
float3 Dir;
};
이 자료를 순서대로 4차원 벡터 단위로 저장한다면 다음과 같은 형태가 될 것이다.
벡터 1: (Pos.x, Pos.y, Pos.z, Dir.x)
벡터 2: (Dir.y, Dir.z, 빈칸, 빈칸)
이렇게 하면 dir요소가 두 4차원 벡터에 걸치게 되는데, 이는 한 요소가 두 벡터에 걸쳐지면 안된다는 HLSL의 규칙을 위반하는 것이다. 이를 방지하기 위해, HLSL은 셰이더 메모리 안에서 다음과 같이 빈칸들을 채워 넣는다.
벡터 1: (Pos.x, Pos.y, Pos.z, 빈칸)
벡터 2: (Dir.x, Dir.y, Dir.z, 빈칸)
만약 이 상수 버퍼에 대응되는 C++ 구조체가 다음과 같이 정의되어 있다고 하자.
// C++
struct Data
{
XMFLOAT3 Pos;
XMFLOAT3 Dir;
};
만약 앞의 규칙을 간과하고 무작정 memcpy를 해버리면 셰이더가 잘못된 상수 값을 사용하게 되어버린다. 제대로 HLSL 상수 버퍼에 복사되게 하려면 C++ 구조체를 다시 정의해야 한다. 우선, 채움 필드들의 존재를 명시적으로 해준다.
cbuffer cb : register(b0)
{
float3 Pos;
float __pad0;
float3 Dir;
float __pad1;
};
C++ 구조체도 동일하게 해준다.
// C++
struct Data
{
XMFLOAT3 Pos;
float __pad0;
XMFLOAT3 Dir;
float __pad1;
};
이제 memcpy를 수행하면 제대로 복사된다.