다음을 통해 공유


Direct3D 9에서 HLSL 셰이더 작성

Vertex-Shader 기본 사항

작업 중일 때 프로그래밍 가능한 꼭짓점 셰이더는 Microsoft Direct3D 그래픽 파이프라인에서 수행하는 꼭짓점 처리를 대체합니다. 꼭짓점 셰이더를 사용하는 동안 고정 함수 파이프라인에서 변환 및 조명 작업에 대한 상태 정보는 무시됩니다. 꼭짓점 셰이더가 비활성화되고 고정 함수 처리가 반환되면 모든 현재 상태 설정이 적용됩니다.

꼭짓점 셰이더가 실행되기 전에 상위 기본 형식의 테셀레이션을 수행해야 합니다. 셰이더 처리 후 표면 테셀레이션을 수행하는 구현은 애플리케이션 및 셰이더 코드에 표시되지 않는 방식으로 수행해야 합니다.

최소한 꼭짓점 셰이더는 동종 클립 공간에서 꼭짓점 위치를 출력해야 합니다. 필요에 따라 꼭짓점 셰이더는 텍스처 좌표, 꼭짓점 색, 꼭짓점 조명, 안개 요인 등을 출력할 수 있습니다.

Pixel-Shader 기본 사항

픽셀 처리는 개별 픽셀의 픽셀 셰이더에 의해 수행됩니다. 픽셀 셰이더는 꼭짓점 셰이더와 함께 작동합니다. 꼭짓점 셰이더의 출력은 픽셀 셰이더에 대한 입력을 제공합니다. 셰이더를 실행한 후 다른 픽셀 작업(안개 혼합, 스텐실 작업 및 렌더링 대상 혼합)이 발생합니다.

텍스처 스테이지 및 샘플러 상태

픽셀 셰이더는 이전에 텍스처 스테이지 상태에서 정의한 작업을 포함하여 다중 텍스처 블렌더에서 지정한 픽셀 혼합 기능을 완전히 대체합니다. 축소, 배율, 밉 필터링 및 래핑 주소 지정 모드를 위해 표준 텍스처 스테이지 상태에 의해 제어된 텍스처 샘플링 및 필터링 작업은 셰이더에서 초기화할 수 있습니다. 애플리케이션은 현재 바인딩된 셰이더를 다시 생성할 필요 없이 이러한 상태를 자유롭게 변경할 수 있습니다. 셰이더가 효과 내에서 디자인된 경우 상태를 더욱 쉽게 설정할 수 있습니다.

픽셀 셰이더 입력

픽셀 셰이더 버전이 ps_1_1에서 ps_2_0인 경우, 확산 색상과 반사 색상은 셰이더에서 사용되기 전에 0에서 1 사이로 채도(포화)됩니다.

픽셀 셰이더에 대한 색 값 입력은 원근감이 올바른 것으로 간주되지만 모든 하드웨어에서 보장되지는 않습니다. 텍스처 좌표에서 샘플링된 색은 올바른 원근감으로 반복되며 반복하는 동안 0~1 범위로 고정됩니다.

픽셀 셰이더 출력

ps_1_1 픽셀 셰이더 버전의 경우 - ps_1_4 픽셀 셰이더에서 내보낸 결과는 레지스터 r0의 내용입니다. 셰이더가 처리를 완료할 때 포함되는 모든 항목은 안개 단계 및 렌더링 대상 블렌더로 전송됩니다.

ps_2_0 이상의 픽셀 셰이더 버전의 경우 출력 색은 oC0 - oC4에서 내보내집니다.

셰이더 입력 및 셰이더 변수

셰이더 변수 선언

가장 간단한 변수 선언에는 이 부동 소수점 선언과 같은 형식 및 변수 이름이 포함됩니다.

float fVar;

동일한 문에서 변수를 초기화할 수 있습니다.

float fVar = 3.1f;

변수 배열을 선언할 수 있습니다.

int iVar[3];

또는 동일한 문에서 선언되고 초기화됩니다.

int iVar[3] = {1,2,3};

다음은 HLSL(상위 수준 셰이더 언어) 변수의 많은 특성을 보여 주는 몇 가지 선언입니다.

float4 color;
uniform float4 position : POSITION; 
const float4 lightDirection = {0,0,1};

데이터 선언은 다음을 비롯한 모든 유효한 형식을 사용할 수 있습니다.

셰이더에는 최상위 변수, 인수 및 함수가 있을 수 있습니다.

// top-level variable
float globalShaderVariable; 

// top-level function
void function(
in float4 position: POSITION0 // top-level argument
              )
{
  float localShaderVariable; // local variable
  function2(...)
}

void function2()
{
  ...
}

최상위 변수는 모든 함수 외부에서 선언됩니다. 최상위 인수는 최상위 함수에 대한 매개 변수입니다. 최상위 함수는 애플리케이션에서 호출하는 함수입니다(다른 함수에서 호출하는 함수와는 반대).

균일 셰이더 입력

꼭짓점 및 픽셀 셰이더는 다양하고 균일한 두 종류의 입력 데이터를 허용합니다. 다양한 입력은 셰이더의 각 실행에 고유한 데이터입니다. 꼭짓점 셰이더의 경우 다양한 데이터(예: 위치, 법선 등)는 꼭짓점 스트림에서 가져옵니다. 균일한 데이터(예: 재질 색, 월드 변환 등)는 셰이더의 여러 실행에 대해 일정합니다. 어셈블리 셰이더 모델에 익숙한 경우 균일한 데이터는 상수 레지스터에 의해 지정되고 v 및 t 레지스터에 따라 다양한 데이터가 지정됩니다.

균일한 데이터는 두 가지 방법으로 지정할 수 있습니다. 가장 일반적인 방법은 전역 변수를 선언하고 셰이더 내에서 사용하는 것입니다. 셰이더 내에서 전역 변수를 사용하면 해당 셰이더에 필요한 균일한 변수 목록에 해당 변수가 추가됩니다. 두 번째 방법은 최상위 셰이더 함수의 입력 매개 변수를 균일한 것으로 표시하는 것입니다. 이 표시는 지정된 변수를 균일한 변수 목록에 추가하도록 지정합니다.

셰이더에서 사용하는 균일한 변수는 상수 테이블을 통해 애플리케이션에 다시 전달됩니다. 상수 테이블은 셰이더에서 사용하는 균일한 변수가 상수 레지스터에 맞는 방법을 정의하는 기호 테이블의 이름입니다. 균일한 함수 매개 변수는 전역 변수와 달리 달러 기호($)가 앞에 추가된 상수 테이블에 나타납니다. 로컬 균일 입력과 동일한 이름의 전역 변수 간의 이름 충돌을 방지하려면 달러 기호가 필요합니다.

상수 테이블에는 셰이더에서 사용하는 모든 균일한 변수의 상수 레지스터 위치가 포함됩니다. 테이블에는 형식 정보 및 기본값(지정된 경우)도 포함됩니다.

다양한 셰이더 입력 및 의미 체계

다양한 입력 매개 변수(최상위 셰이더 함수)는 셰이더 실행에 대한 값이 상수임을 나타내는 의미 체계 또는 균일 키워드로 표시되어야 합니다. 최상위 셰이더 입력이 의미 체계 또는 균일 키워드로 표시되지 않으면 셰이더가 컴파일되지 않습니다.

입력 의미 체계는 지정된 입력을 그래픽 파이프라인의 이전 부분의 출력에 연결하는 데 사용되는 이름입니다. 예를 들어 꼭짓점 셰이더에서 입력 의미 체계 POSITION0 사용하여 꼭짓점 버퍼의 위치 데이터를 연결할 위치를 지정합니다.

픽셀 및 꼭짓점 셰이더에는 각 셰이더 단위로 공급되는 그래픽 파이프라인의 여러 부분으로 인해 입력 의미 체계 집합이 다릅니다. 꼭짓점 셰이더 입력 의미 체계는 꼭짓점 버퍼에서 꼭짓점 셰이더에서 사용할 수 있는 폼으로 로드할 꼭짓점별 정보(예: 위치, 법선, 질감 좌표, 색, 탄젠트, 이진 등)를 설명합니다. 입력 의미 체계는 꼭짓점 선언 사용량 및 사용 인덱스에 직접 매핑됩니다.

픽셀 셰이더 입력 의미 체계는 래스터화 단위에서 픽셀당 제공되는 정보를 설명합니다. 데이터는 현재 프리미티브의 각 꼭짓점에서 꼭짓점 셰이더 출력 간을 보간하여 생성됩니다. 기본 픽셀 셰이더 입력 의미 체계는 출력 색 및 질감 좌표 정보를 입력 매개 변수에 연결합니다.

다음 두 가지 방법으로 셰이더 입력에 입력 의미 체계를 할당할 수 있습니다.

  • 매개 변수 선언에 콜론 및 의미 체계 이름을 추가합니다.
  • 각 구조체 멤버에 할당된 입력 의미 체계를 사용하여 입력 구조를 정의합니다.

꼭짓점 및 픽셀 셰이더는 후속 그래픽 파이프라인 단계에 출력 데이터를 제공합니다. 출력 의미 체계는 셰이더에서 생성된 데이터를 다음 단계의 입력에 연결하는 방법을 지정하는 데 사용됩니다. 예를 들어 꼭짓점 셰이더의 출력 의미 체계는 래스터라이저에서 보간기의 출력을 연결하여 픽셀 셰이더에 대한 입력 데이터를 생성하는 데 사용됩니다. 픽셀 셰이더 출력은 각 렌더링 대상의 알파 혼합 단위에 제공된 값 또는 깊이 버퍼에 기록된 깊이 값입니다.

꼭짓점 셰이더 출력 의미 체계는 셰이더를 픽셀 셰이더와 래스터라이저 단계에 연결하는 데 사용됩니다. 래스터라이저에서 사용되며 픽셀 셰이더에 노출되지 않는 꼭짓점 셰이더는 위치 데이터를 최소한으로 생성해야 합니다. 텍스처 좌표 및 색 데이터를 생성하는 꼭짓점 셰이더는 보간이 완료된 후 해당 데이터를 픽셀 셰이더에 제공합니다.

픽셀 셰이더 출력 의미 체계는 픽셀 셰이더의 출력 색을 올바른 렌더링 대상과 바인딩합니다. 픽셀 셰이더 출력 색은 대상 렌더링 대상이 수정되는 방법을 결정하는 알파 혼합 단계에 연결됩니다. 픽셀 셰이더 깊이 출력을 사용하여 현재 래스터 위치에서 대상 깊이 값을 변경할 수 있습니다. 깊이 출력 및 여러 렌더링 대상은 일부 셰이더 모델에서만 지원됩니다.

출력 의미 체계의 구문은 입력 의미 체계를 지정하는 구문과 동일합니다. 의미 체계는 "out" 매개 변수로 선언된 매개 변수에 직접 지정하거나 "out" 매개 변수 또는 함수의 반환 값으로 반환되는 구조체의 정의 중에 할당될 수 있습니다.

의미 체계는 데이터의 원본을 식별합니다. 의미 체계는 셰이더 입력 및 출력을 식별하는 선택적 식별자입니다. 의미 체계는 다음 세 위치 중 하나에 나타납니다.

  • 구조체 멤버 뒤입니다.
  • 함수의 입력 인수 목록의 인수 뒤입니다.
  • 함수의 입력 인수 목록 뒤입니다.

이 예제에서는 하나 이상의 꼭짓점 셰이더 입력을 제공하는 구조체와 하나 이상의 꼭짓점 셰이더 출력을 제공하는 다른 구조를 사용합니다. 각 구조체 멤버는 의미 체계를 사용합니다.

vector vClr;

struct VS_INPUT
{
    float4 vPosition : POSITION;
    float3 vNormal : NORMAL;
    float4 vBlendWeights : BLENDWEIGHT;
};

struct VS_OUTPUT
{
    float4  vPosition : POSITION;
    float4  vDiffuse : COLOR;

};

float4x4 mWld1;
float4x4 mWld2;
float4x4 mWld3;
float4x4 mWld4;

float Len;
float4 vLight;

float4x4 mTot;

VS_OUTPUT VS_Skinning_Example(const VS_INPUT v, uniform float len=100)
{
    VS_OUTPUT out;

    // Skin position (to world space)
    float3 vPosition = 
        mul(v.vPosition, (float4x3) mWld1) * v.vBlendWeights.x +
        mul(v.vPosition, (float4x3) mWld2) * v.vBlendWeights.y +
        mul(v.vPosition, (float4x3) mWld3) * v.vBlendWeights.z +
        mul(v.vPosition, (float4x3) mWld4) * v.vBlendWeights.w;
    // Skin normal (to world space)
    float3 vNormal =
        mul(v.vNormal, (float3x3) mWld1) * v.vBlendWeights.x + 
        mul(v.vNormal, (float3x3) mWld2) * v.vBlendWeights.y + 
        mul(v.vNormal, (float3x3) mWld3) * v.vBlendWeights.z + 
        mul(v.vNormal, (float3x3) mWld4) * v.vBlendWeights.w;
    
    // Output stuff
    out.vPosition    = mul(float4(vPosition + vNormal * Len, 1), mTot);
    out.vDiffuse  = dot(vLight,vNormal);

    return out;
}

입력 구조는 셰이더 입력을 제공할 꼭짓점 버퍼의 데이터를 식별합니다. 이 셰이더는 꼭짓점 버퍼의 위치, 법선 및 블렌드 가중치 요소의 데이터를 꼭짓점 셰이더 레지스터에 매핑합니다. 입력 데이터 형식이 꼭짓점 선언 데이터 형식과 정확히 일치할 필요는 없습니다. 정확히 일치하지 않으면 꼭짓점 데이터는 셰이더 레지스터에 기록될 때 자동으로 HLSL의 데이터 형식으로 변환됩니다. 예를 들어 일반 데이터가 애플리케이션에서 UINT 형식으로 정의된 경우 셰이더에서 읽을 때 float3으로 변환됩니다.

꼭짓점 스트림의 데이터에 해당 셰이더 데이터 형식보다 적은 구성 요소가 포함된 경우 누락된 구성 요소는 0으로 초기화됩니다(w를 제외하고 1로 초기화됨).

입력 의미 체계는 D3DDECLUSAGE값과 유사합니다.

출력 구조는 위치 및 색의 꼭짓점 셰이더 출력 매개 변수를 식별합니다. 이러한 출력은 파이프라인에서 삼각형 래스터화(기본 처리)에 사용됩니다. 위치 데이터로 표시된 출력은 동종 공간에서 꼭짓점의 위치를 나타냅니다. 최소한 꼭짓점 셰이더는 위치 데이터를 생성해야 합니다. 화면 공간 위치는 꼭짓점 셰이더가 완료된 후 (x, y, z) 좌표를 w로 나누어 계산합니다. 화면 공간에서 -1 및 1은 뷰포트 경계의 최소 및 최대 x 및 y 값이며 z는 z 버퍼 테스트에 사용됩니다.

출력 의미 체계도 D3DDECLUSAGE값과 유사합니다. 일반적으로 꼭짓점 셰이더의 출력 구조는 픽셀 셰이더의 입력 구조로도 사용할 수 있습니다. 단, 픽셀 셰이더가 위치, 점 크기 또는 포그 의미 체계로 지정된 변수를 읽지 않는 경우에 가능합니다. 이러한 의미 체계는 픽셀 셰이더에서 사용하지 않는 꼭짓점당 스칼라 값과 연결됩니다. 픽셀 셰이더에 이러한 값이 필요한 경우 픽셀 셰이더 의미 체계를 사용하는 다른 출력 변수로 복사할 수 있습니다.

전역 변수는 컴파일러에 의해 자동으로 등록에 할당됩니다. 전역 변수는 셰이더가 호출될 때마다 처리되는 모든 픽셀에 대해 변수의 내용이 동일하기 때문에 균일한 매개 변수라고도 합니다. 레지스터는 상수 테이블에 포함되며, ID3DXConstantTable 인터페이스를 사용하여 읽을 수 있습니다.

픽셀 셰이더에 대한 입력 의미 체계는 꼭짓점 셰이더와 픽셀 셰이더 간의 전송을 위해 값을 특정 하드웨어 레지스터에 매핑합니다. 각 레지스터 형식에는 특정 속성이 있습니다. 현재 색 및 질감 좌표에 대한 의미 체계는 두 개뿐이므로 대부분의 데이터는 그렇지 않은 경우에도 텍스처 좌표로 표시되는 것이 일반적입니다.

꼭짓점 셰이더 출력 구조는 픽셀 셰이더에서 사용되지 않는 위치 데이터가 있는 입력을 사용했습니다. HLSL은 픽셀 셰이더에서 참조되지 않는 경우 픽셀 셰이더에 대한 유효한 입력 데이터가 아닌 꼭짓점 셰이더의 유효한 출력 데이터를 허용합니다.

입력 인수는 배열일 수도 있습니다. 의미 체계는 배열의 각 요소에 대해 컴파일러에 의해 자동으로 증가합니다. 예를 들어 다음 명시적 선언을 고려합니다.

struct VS_OUTPUT
{
    float4 Position   : POSITION;
    float3 Diffuse    : COLOR0;
    float3 Specular   : COLOR1;               
    float3 HalfVector : TEXCOORD3;
    float3 Fresnel    : TEXCOORD2;               
    float3 Reflection : TEXCOORD0;               
    float3 NoiseCoord : TEXCOORD1;               
};

float4 Sparkle(VS_OUTPUT In) : COLOR

위에 지정된 명시적 선언은 컴파일러에서 의미 체계를 자동으로 증가시키는 다음 선언과 동일합니다.

float4 Sparkle(float4 Position : POSITION,
                 float3 Col[2] : COLOR0,
                 float3 Tex[4] : TEXCOORD0) : COLOR0
{
   // shader statements
   ...

입력 의미 체계와 마찬가지로 출력 의미 체계는 픽셀 셰이더 출력 데이터의 데이터 사용량을 식별합니다. 많은 픽셀 셰이더는 하나의 출력 색에만 씁니다. 픽셀 셰이더는 깊이 값을 하나 이상의 여러 렌더링 대상에 동시에 쓸 수도 있습니다(최대 4개). 꼭짓점 셰이더와 마찬가지로 픽셀 셰이더는 구조를 사용하여 둘 이상의 출력을 반환합니다. 이 셰이더는 색 구성 요소와 깊이 구성 요소에 0을 씁니다.

struct PS_OUTPUT
{
    float4 Color[4] : COLOR0;
    float  Depth  : DEPTH;
};

PS_OUTPUT main(void)
{
    PS_OUTPUT out;

   // Shader statements
   ...

  // Write up to four pixel shader output colors
  out.Color[0] =  ...
  out.Color[1] =  ...
  out.Color[2] =  ...
  out.Color[3] =  ...

  // Write pixel depth 
  out.Depth =  ...

    return out;
}

픽셀 셰이더 출력 색은 float4 형식이어야 합니다. 여러 색을 작성할 때는 모든 출력 색을 연속적으로 사용해야 합니다. 즉, COLOR0 이미 작성되지 않은 한 COLOR1 출력이 될 수 없습니다. 픽셀 셰이더 깊이 출력은 float1 형식이어야 합니다.

샘플러 및 텍스처 개체

샘플러에는 샘플러 상태가 포함됩니다. 샘플러 상태는 샘플링할 텍스처를 지정하고 샘플링 중에 수행되는 필터링을 제어합니다. 텍스처를 샘플링하려면 다음 세 가지가 필요합니다.

  • 질감
  • 샘플러(샘플러 상태 포함)
  • 샘플링 명령

샘플러를 다음과 같이 텍스처 및 샘플러 상태로 초기화할 수 있습니다.

sampler s = sampler_state 
{ 
  texture = NULL; 
  mipfilter = LINEAR; 
};

다음은 2D 텍스처를 샘플링하는 코드의 예입니다.

texture tex0;
sampler2D s_2D;

float2 sample_2D(float2 tex : TEXCOORD0) : COLOR
{
  return tex2D(s_2D, tex);
}

텍스처는 텍스처 변수 tex0으로 선언됩니다.

이 예제에서는 s_2D라는 샘플러 변수가 선언됩니다. 샘플러에는 중괄호 내부에 샘플러 상태가 포함됩니다. 여기에는 샘플링될 텍스처와 필터 상태(즉, 래핑 모드, 필터 모드 등)가 포함됩니다. 샘플러 상태를 생략하면 텍스처 좌표에 대한 선형 필터링 및 래핑 모드를 지정하는 기본 샘플러 상태가 적용됩니다. 샘플러 함수는 2개 구성 요소 부동 소수점 텍스처 좌표를 사용하고 2개 구성 요소 색을 반환합니다. float2 반환 형식으로 표시되며 빨강 및 녹색 구성 요소의 데이터를 나타냅니다.

네 가지 유형의 샘플러가 정의되고(키워드참조) 텍스처 조회는 내장 함수인 tex1D(s, t) (DirectX HLSL), tex2D(s, t) (DirectX HLSL), tex3D(s, t) (DirectX HLSL), texCUBE(s, t) (DirectX HLSL)에 의해 수행됩니다. 다음은 3D 샘플링의 예입니다.

texture tex0;
sampler3D s_3D;

float3 sample_3D(float3 tex : TEXCOORD0) : COLOR
{
  return tex3D(s_3D, tex);
}

이 샘플러 선언은 필터 설정 및 주소 모드에 기본 샘플러 상태를 사용합니다.

해당 큐브 샘플링 예제는 다음과 같습니다.

texture tex0;
samplerCUBE s_CUBE;

float3 sample_CUBE(float3 tex : TEXCOORD0) : COLOR
{
  return texCUBE(s_CUBE, tex);
}

마지막으로 1D 샘플링 예제는 다음과 같습니다.

texture tex0;
sampler1D s_1D;

float sample_1D(float tex : TEXCOORD0) : COLOR
{
  return tex1D(s_1D, tex);
}

런타임은 1D 텍스처를 지원하지 않으므로 컴파일러는 y 좌표가 중요하지 않다는 지식과 함께 2D 텍스처를 사용합니다. tex1D(s, t) (DirectX HLSL)는 2D 텍스처 조회로 구현되므로 컴파일러는 y 구성 요소를 효율적으로 선택할 수 있습니다. 일부 드문 시나리오에서는 컴파일러가 효율적인 y 구성 요소를 선택할 수 없습니다. 이 경우 경고가 발생합니다.

texture tex0;
sampler s_1D_float;

float4 main(float texCoords : TEXCOORD) : COLOR
{
    return tex1D(s_1D_float, texCoords);
}

이 특정 예제는 컴파일러가 입력 좌표를 다른 레지스터로 이동해야 하기 때문에 비효율적입니다(1D 조회는 2D 조회로 구현되고 텍스처 좌표는 float1로 선언되기 때문). float1 대신 float2 입력을 사용하여 코드를 다시 작성하는 경우 컴파일러는 y가 무언가로 초기화되었음을 알고 있으므로 입력 텍스처 좌표를 사용할 수 있습니다.

texture tex0;
sampler s_1D_float2;

float4 main(float2 texCoords : TEXCOORD) : COLOR
{
    return tex1D(s_1D_float2, texCoords);
}

모든 텍스처 조회에는 "bias" 또는 "proj"(즉, tex2Dbias(DirectX HLSL), texCUBEproj(DirectX HLSL))이 추가될 수 있습니다. "proj" 접미사를 사용하면 텍스처 좌표가 w 구성 요소로 나눠집니다. "바이어스"를 사용하면 밉맵 레벨이 w 구성 요소에 의해 이동합니다. 따라서 접미사가 있는 모든 텍스처 조회는 항상 float4 입력을 사용합니다. tex1D(s, t)(DirectX HLSL)tex2D(s, t)(DirectX HLSL) 각각 yz-및 z 구성 요소를 무시합니다.

현재 샘플러의 동적 배열 액세스를 지원하는 백 엔드는 없지만 샘플러도 배열에서 사용할 수 있습니다. 따라서 컴파일 시간에 확인할 수 있으므로 다음이 유효합니다.

tex2D(s[0],tex)

그러나 이 예제는 유효하지 않습니다.

tex2D(s[a],tex)

샘플러의 동적 액세스는 리터럴 루프를 사용하여 프로그램을 작성하는 데 주로 유용합니다. 다음 코드는 샘플러 배열 액세스를 보여 줍니다.

sampler sm[4];

float4 main(float4 tex[4] : TEXCOORD) : COLOR
{
    float4 retColor = 1;

    for(int i = 0; i < 4;i++)
    {
        retColor *= tex2D(sm[i],tex[i]);
    }

    return retColor;
}

메모

Microsoft Direct3D 디버그 런타임을 사용하면 텍스처의 구성 요소 수와 샘플러 간의 불일치를 catch할 수 있습니다.

 

함수 작성

함수는 큰 작업을 더 작은 작업으로 분할합니다. 작은 작업은 디버그하기 쉽고 일단 입증되면 다시 사용할 수 있습니다. 함수를 사용하여 다른 함수의 세부 정보를 숨길 수 있으므로 함수로 구성된 프로그램을 더 쉽게 따를 수 있습니다.

HLSL 함수는 여러 가지 방법으로 C 함수와 유사합니다. 둘 다 정의와 함수 본문을 포함하고 반환 형식과 인수 목록을 선언합니다. C 함수와 마찬가지로 HLSL 유효성 검사는 셰이더 컴파일 중에 인수, 인수 형식 및 반환 값에 대한 형식 검사를 수행합니다.

C 함수와 달리 HLSL 진입점 함수는 의미 체계를 사용하여 함수 인수를 셰이더 입력 및 출력에 바인딩합니다(내부적으로 의미 체계를 무시라고 하는 HLSL 함수). 이렇게 하면 버퍼 데이터를 셰이더에 쉽게 바인딩하고 셰이더 출력을 셰이더 입력에 바인딩할 수 있습니다.

함수는 선언과 본문을 포함하고 선언은 본문 앞에 와야 합니다.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
{
    return mul(inPos, WorldViewProj );
};

함수 선언에는 중괄호 앞에 있는 모든 항목이 포함됩니다.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION

함수 선언에는 다음이 포함됩니다.

  • 반환 형식
  • 함수 이름
  • 인수 목록(선택 사항)
  • 출력 의미 체계(선택 사항)
  • 주석(선택 사항)

반환 형식은 float4와 같은 HLSL 기본 데이터 형식일 수 있습니다.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
{
   ...
}

반환 형식은 이미 정의된 구조체일 수 있습니다.

struct VS_OUTPUT
{
    float4  vPosition        : POSITION;
    float4  vDiffuse         : COLOR;
}; 

VS_OUTPUT VertexShader_Tutorial_1(float4 inPos : POSITION )
{
   ...
}

함수가 값을 반환하지 않으면 void를 반환 형식으로 사용할 수 있습니다.

void VertexShader_Tutorial_1(float4 inPos : POSITION )
{
   ...
}

반환 형식은 항상 함수 선언에서 먼저 나타납니다.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION

인수 목록은 함수에 대한 입력 인수를 선언합니다. 반환될 값을 선언할 수도 있습니다. 일부 인수는 입력 인수와 출력 인수입니다. 다음은 네 개의 입력 인수를 사용하는 셰이더의 예입니다.

float4 Light(float3 LightDir : TEXCOORD1, 
             uniform float4 LightColor,  
             float2 texcrd : TEXCOORD0, 
             uniform sampler samp) : COLOR 
{
    float3 Normal = tex2D(samp,texcrd);

    return dot((Normal*2 - 1), LightDir)*LightColor;
}

이 함수는 질감 샘플과 밝은 색의 혼합인 최종 색을 반환합니다. 이 함수는 4개의 입력을 사용합니다. 두 입력에는 의미 체계가 있습니다. LightDir에는 TEXCOORD1 의미 체계가 있고 texcrd에는 TEXCOORD0 의미 체계가 있습니다. 의미 체계는 이러한 변수에 대한 데이터가 꼭짓점 버퍼에서 온다는 것을 의미합니다. LightDir 변수에 TEXCOORD1 의미 체계가 있더라도 매개 변수는 텍스처 좌표가 아닐 수 있습니다. TEXCOORDn 의미 체계 형식은 미리 정의되지 않은 형식에 대한 의미 체계를 제공하는 데 자주 사용됩니다(조명 방향에 대한 꼭짓점 셰이더 입력 의미 체계는 없음).

LightColor와 samp의 다른 두 입력은 uniform 키워드로 레이블됩니다. 그리기 호출 간에 변경되지 않는 고정된 상수입니다. 이러한 매개 변수의 값은 셰이더 전역 변수에서 가져옵니다.

인수는 in 키워드를 사용하여 입력으로 레이블을 지정하고 out 키워드를 사용하여 인수를 출력할 수 있습니다. 인수는 참조로 전달할 수 없습니다. 그러나 inout 키워드로 선언된 경우 인수는 입력 및 출력이 될 수 있습니다. inout 키워드로 표시된 함수에 전달된 인수는 함수가 반환되고 다시 복사될 때까지 원본의 복사본으로 간주됩니다. inout을 사용하는 예제는 다음과 같습니다.

void Increment_ByVal(inout float A, inout float B) 
{ 
    A++; B++;
}

이 함수는 A와 B의 값을 증가시키고 반환합니다.

함수 본문은 함수 선언 이후의 모든 코드입니다.

float4 VertexShader_Tutorial_1(float4 inPos : POSITION ) : POSITION
{
    return mul(inPos, WorldViewProj );
};

본문은 중괄호로 둘러싸인 문으로 구성됩니다. 함수 본문은 변수, 리터럴, 식 및 문을 사용하여 모든 기능을 구현합니다.

셰이더 본문은 행렬을 곱하고 float4 결과를 반환하는 두 가지 작업을 수행합니다. 행렬 곱하기는 4x4 행렬을 곱하는 mul(DirectX HLSL) 함수를 사용하여 수행됩니다. mul(DirectX HLSL) 함수의 HLSL 라이브러리에 이미 기본 제공되어 있으므로 내장 함수라고 합니다. 내장 함수는 다음 섹션에서 자세히 설명합니다.

행렬은 입력 벡터 Pos와 복합 행렬 WorldViewProj를 곱합니다. 그 결과 위치 데이터가 화면 공간으로 변환됩니다. 이는 수행할 수 있는 최소 꼭짓점 셰이더 처리입니다. 꼭짓점 셰이더 대신 고정 함수 파이프라인을 사용하는 경우 이 변환을 수행한 후 꼭짓점 데이터를 그릴 수 있습니다.

함수 본문의 마지막 문은 return 문입니다. C와 마찬가지로 이 문은 함수에서 함수를 호출한 문으로 컨트롤을 반환합니다.

함수 반환 형식은 bool, int half, float 및 double을 포함하여 HLSL에 정의된 모든 단순 데이터 형식일 수 있습니다. 반환 형식은 벡터 및 행렬과 같은 복합 데이터 형식 중 하나일 수 있습니다. 개체를 참조하는 HLSL 형식은 반환 형식으로 사용할 수 없습니다. 여기에는 픽셀 셰이더, 버텍스 셰이더, 텍스처 및 샘플러가 포함됩니다.

다음은 반환 형식에 구조를 사용하는 함수의 예입니다.

float4x4 WorldViewProj : WORLDVIEWPROJ;

struct VS_OUTPUT
{
    float4 Pos  : POSITION;
};

VS_OUTPUT VS_HLL_Example(float4 inPos : POSITION )
{
    VS_OUTPUT Out;

    Out.Pos = mul(inPos,  WorldViewProj );

    return Out;
};

float4 반환 형식이 구조 VS_OUTPUT 대체되었으며, 이제 단일 float4 멤버가 포함됩니다.

return 문은 함수의 끝을 신호로 표시합니다. 가장 간단한 반환 구문입니다. 함수에서 호출 프로그램으로 컨트롤을 반환합니다. 값을 반환하지 않습니다.

void main()
{
    return ;
}

return 문은 하나 이상의 값을 반환할 수 있습니다. 이 예제에서는 리터럴 값을 반환합니다.

float main( float input : COLOR0) : COLOR0
{
    return 0;
}

이 예제에서는 식의 스칼라 결과를 반환합니다.

return  light.enabled;

이 예제에서는 지역 변수 및 리터럴에서 생성된 float4를 반환합니다.

return  float4(color.rgb, 1) ;

이 예제에서는 내장 함수에서 반환된 결과에서 생성된 float4와 몇 가지 리터럴 값을 반환합니다.

float4 func(float2 a: POSITION): COLOR
{
    return float4(sin(length(a) * 100.0) * 0.5 + 0.5, sin(a.y * 50.0), 0, 1);
}

이 예제에서는 하나 이상의 멤버를 포함하는 구조를 반환합니다.

float4x4 WorldViewProj;

struct VS_OUTPUT
{
    float4 Pos  : POSITION;
};

VS_OUTPUT VertexShader_Tutorial_1(float4 inPos : POSITION )
{
    VS_OUTPUT out;
    out.Pos = mul(inPos, WorldViewProj );
    return out;
};

흐름 제어

대부분의 현재 꼭짓점 및 픽셀 셰이더 하드웨어는 각 명령을 한 번 실행하여 셰이더 줄을 한 줄씩 실행하도록 설계되었습니다. HLSL은 정적 분기, 조건자 명령, 정적 루프, 동적 분기 및 동적 루핑을 포함하는 흐름 제어를 지원합니다.

이전에는 if 문을 사용하면 코드 흐름의 if 쪽과 다른 쪽을 모두 구현하는 어셈블리 언어 셰이더 코드가 생성되었습니다. 다음은 vs_1_1 위해 컴파일된 HLSL 코드의 예입니다.

if (Value > 0)
    oPos = Value1; 
else
    oPos = Value2; 

결과 어셈블리 코드는 다음과 같습니다.

// Calculate linear interpolation value in r0.w
mov r1.w, c2.x               
slt r0.w, c3.x, r1.w         
// Linear interpolation between value1 and value2
mov r7, -c1                      
add r2, r7, c0                   
mad oPos, r0.w, r2, c1  

일부 하드웨어는 정적 또는 동적 루핑을 허용하지만 대부분 선형 실행이 필요합니다. 루핑을 지원하지 않는 모델에서는 모든 루프를 언롤해야 합니다. 예를 들어 ps_1_1 셰이더에 대해서도 언롤된 루프를 사용하는 DepthOfField 샘플 샘플이 있습니다.

이제 HLSL에는 이러한 흐름 제어 유형 각각에 대한 지원이 포함됩니다.

  • 정적 분기
  • 조건부 명령
  • 정적 루프
  • 동적 분기
  • 동적 루핑

정적 분기를 사용하면 부울 셰이더 상수에 따라 셰이더 코드 블록을 켜거나 끌 수 있습니다. 현재 렌더링 중인 개체의 형식에 따라 코드 경로를 사용하거나 사용하지 않도록 설정하는 편리한 방법입니다. 그리기 호출 사이에 현재 셰이더에서 지원하려는 기능을 결정한 다음 해당 동작을 가져오는 데 필요한 부울 플래그를 설정할 수 있습니다. 셰이더 실행 시 부울 상수에 의해 비활성화된 모든 문은 건너뜁니다.

가장 친숙한 분기 지원은 동적 분기입니다. 동적 분기를 사용하면 비교 조건이 변수에 상주합니다. 즉, 컴파일 시간 또는 두 그리기 호출 간에 발생하는 비교와는 달리 런타임에 각 꼭짓점 또는 각 픽셀에 대해 비교가 수행됩니다. 성능 저하는 분기 명령의 비용과 실행된 분기 측의 명령 비용입니다. 동적 분기는 셰이더 모델 3 이상에서 구현됩니다. 이러한 모델에서 작동하는 셰이더 최적화는 CPU에서 실행되는 코드를 최적화하는 것과 유사합니다.

HLSL 프로그래밍 가이드