Stacky_
매일 쌓이는 기록

HLSL 기초 본문

Game/Graphics

HLSL 기초

Stacky 2025. 6. 14. 15:16

개요

  • HLSL은 GPU에서 실행되는 언어로, C/C++과 매우 닮았다.
  • 정확히는 CPU에서 컴파일되어 바이너리 코드를 GPU로 넘겨주면, GPU에서 실행된다.
  • 이때, 컴파일은 빌드 시에 한번 그리고 런타임 중에 동적으로 파일이 변경되면 그때마다 한 번씩 컴파일된다.

HLSL의 변수

  • HLSL의 변수는 3가지의 종류로 나뉜다
    1. 스칼라 변수
    2. 벡터 변수
    3. 행렬 변수
  • 스칼라 변수는 C/C++에서 보는 그런 변수들과 동일하다.
  • HLSL에서는 bool, int, uint, half, float를 사용할 수 있다.
  • 벡터 변수는 배열처럼 float이 1~4개로 이어져 있는 형태의 변수이다.
  • 보통 스칼라 변수 키워드 바로 뒤에 숫자를 붙여 표현한다.
float4 color; // [r, g, b, a]
  • 행렬 변수는 이차원 배열처럼 float이 1~16개로 이어져 있는 형태의 변수이다.
  • 스칼라 변수 키워드 바로 뒤에 "숫자x숫자"를 붙여 표현한다.
float4x4 matrix;
/* [ a, b, c, d ]
*  [ e, f, g, h ]
*  [ i, j, k, l ]
*  [ m, n, o, p ]
*/

의미론 / 의미체계(semantics)

  • 의미론은 GPU가 데이터를 어떤 의미가 있는지 명시해 주는 것을 의미한다.
  • 만일 아래와 같은 코드가 있다고 해보자.
struct VOut
{
    float4 position;
    float4 color;
};

VOut VShader(float4 position, float4 color)
{
    VOut output;

    output.position = position;
    output.color = color;
    return output;
}
  • 이 코드에서, 우리가 보기에는 큰 문제가 없다.
  • 위치값과 색상값을 가져와서 구조체에 집어넣는 형식이고, 이를 통해 GPU에 전달하는 것이다.
  • 그런데 GPU의 입장에서는 동일한 벡터 변수 두 개가 전부인 데이터이다.
struct
{
    float4, float4
};
  • 결국 GPU는 위치값과 색상값을 가지고 처리해야 하는데, 무엇이 위치값이고 색상값인지 GPU로서는 알 길이 없다.
  • 따라서, GPU에게 명시적으로 각 변수의 의미를 알려줄 필요가 있다.
struct VOut
{
    float4 position : SV_POSITION;
    float4 color : COLOR;
};

VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
    VOut output;

    output.position = position;
    output.color = color;
    return output;
}

의미체계의 종류

  • 의미체계는 DirectX9 이상에서 사용되는 의미체계와 DirectX10 이상에서 사용되는 의미체계로 나뉜다.
struct VOut
{
    float4 position : SV_POSITION; // DirectX10이상 의미체계
    float4 color : COLOR; // DirectX9이상 의미체계
};

VOut VShader(float4 position : POSITION, float4 color : COLOR)
{
    VOut output;

    output.position = position;
    output.color = color;
    return output;
}
  • 보통 DirectX10 이상에서 사용되는 의미체계는 SV_라는 접두사가 붙고, 입력 레이아웃을 통해 등록할 필요가 없다.
  • 반면, DirectX9 이상에서 사용되는 의미체계는 SV_ 접두사를 붙이지도 않고, A입력 레이아웃을 통해 반드시 등록해야 한다.
D3D11_INPUT_ELEMENT_DESC ied[] =
{
    {"POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, 0, D3D11_INPUT_PER_VERTEX_DATA, 0},
    {"COLOR", 0, DXGI_FORMAT_R32G32B32A32_FLOAT, 0, 12, D3D11_INPUT_PER_VERTEX_DATA, 0},
};
  • 이는 역사적인 이유로 인해 나누어졌다.
  • DirectX9 이전에는 렌더링 파이프라인이 고정 기능 파이프라인(Fixed-Function Pipeline)이었다.
  • 즉, 렌더링 파이프라인이 이미 만들어져 있는 기다란 파이프였고, 여기에 개발자가 만질 수 있는 것은 파이프에 있는 밸브들 몇 가지(실제로는 Vertex Shader와 Pixel Shader)였다.
  • 그러나 DirectX10으로 넘어오면서, 더 이상 고정이 아닌 개발자들이 마음대로 이 파이프들을 다른 것으로 교체하는 것이 가능해졌다.
  • 문제는 여기서 발생하는데, 바로 통일된 의미론이 없다는 것이다.
  • 각 파이프 - 정확히는 셰이더들이 데이터를 해석할 때 제각각으로 해석할 여지가 커지고 이렇게 되면 데이터의 흐름자체가 꼬이게 되고, 처리가 제대로 이루어질 수 없다.
  • 때문에, 기존에는 프로그래머가 마음대로 지정했던 의미론을 하나의 시스템 값으로 고정해서 사용하도록 한 것이다.
  • 그래서 DirectX10 이상의 의미론에서는 접미사 SV_가 붙는다.

DirectX9에서는 정말 프로그래머 마음대로 의미론을 바꿀 수 있었나?

  • 결론부터 말하면, 그렇지 않지만 그렇다고 SV_처럼 시스템 값에 등록되어 있지는 않았다.
  • DirectX9 보다 훨씬 이전에는 정말 프로그래머 마음대로 의미론을 지정했었다.
  • 하지만, 시간이 지나면서 프로그래머 사이들에서 POSITION이나 COLOR 같은 값이 관습처럼 사용되었다.
  • 그러면서 GPU 드라이버 개발자들도 이에 맞춰서 개발하면서 어쩌다 보니 마치 시스템 값처럼 사용하게 된 것이다.
  • 따라서 만일 POSITION이나 COLOR 대신 다른 것들을 등록하면 GPU 드라이버가 인식을 하지 못해 제대로 동작하지 않는다.

참고한 자료

'Game > Graphics' 카테고리의 다른 글

Visual Studio에서 DirectX Tool Kit 라이브러리 적용  (0) 2025.06.10
Input Assembler(IA)  (0) 2025.06.05