버텍스 및 프래그먼트 셰이더 예제(Vertex and fragment shader examples)
Cg/HLSL에서 셰이더 프로퍼티 액세스(Accessing shader properties in Cg/HLSL)

셰이더 시맨틱(Shader semantics)

HLSL 셰이더 프로그램을 작성할 때 입력 및 출력 변수는 semantics를 통해 각각의 “의도”를 명시해야 합니다. 이는 HLSL 셰이더 언어의 표준 개념이며 자세한 내용은 MSDN 의 시맨틱 문서를 참조하십시오.

아래 예제를 여기서 압축된 Unity 프로젝트 파일로 다운로드할 수 있습니다.

버텍스 셰이더 입력 시맨틱

메인 버텍스 셰이더 함수(#pragma vertex 지시자로 명시한 것)는 모든 입력 파라미터에 시맨틱이 있어야 합니다. 이는 버텍스 포지션, 노멀 메시, 텍스처 좌표 같은 개별 메시 데이터 요소에 대응됩니다. 자세한 내용은 버텍스 프로그램 입력을 참조하십시오.

다음은 버텍스 포지션 및 텍스처 좌표를 입력으로 받는 단순한 버텍스 셰이더의 예제입니다. 이 픽셀 셰이더는 텍스처 좌표를 컬러로 시각화합니다.

Shader "Unlit/Show UVs"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            struct v2f {
                float2 uv : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0 // first texture coordinate input
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                o.uv = uv;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return fixed4(i.uv, 0, 0);
            }
            ENDCG
        }
    }
}

모든 개별 입력 하나씩 시맨틱을 명시하는 대신 이 입력을 구조로 선언하고 구조의 각 개별 멤버 변수에 시맨틱을 명시할 수도 있습니다. 방법을 알아보려면 셰이더 프로그램 예제를 참조하십시오.

프래그먼트 셰이더 출력 시맨틱

프래그먼트(픽셀) 셰이더는 SV_Target 시맨틱을 가진 컬러를 출력하는 경우가 많습니다. 바로 위의 예제가 그런 경우입니다.

fixed4 frag (v2f i) : SV_Target

frag 함수는 fixed4 타입(낮은 정밀도 RGBA 컬러)을 반환합니다. 함수는 하나의 값만을 반환하므로 시맨틱을 : SV_Target로 함수 자체에 명시하였습니다.

출력에 구조를 함께 반환하는 것도 가능합니다. 위의 프래그먼트 셰이더는 다음 방식으로 재작성할 수도 있으며 동작은 동일합니다.

struct fragOutput {
    fixed4 color : SV_Target;
};
fragOutput frag (v2f i)
{
    fragOutput o;
    o.color = fixed4(i.uv, 0, 0);
    return o;
}

프래그먼트 셰이더에서 구조를 반환하는 것은 단일 컬러만을 반환하지 않는 셰이더의 경우에 특히 유용합니다. 프래그먼트 셰이더가 지원하는 추가 시맨틱은 다음과 같습니다.

SV_TargetN: 다수의 렌더 타겟

SV_Target1, SV_Target2 등은 셰이더가 작성한 추가 컬러입니다. 한 번에 하나 이상의 렌더 타겟에 렌더링할 때 사용되며 이를 멀티플 렌더 타겟 렌더링 기법 또는 MRT 라고 합니다. SV_Target0SV_Target과 동일합니다.

SV_Depth: 픽셀 셰이더 뎁스 출력

일반적으로 프래그먼트 셰이더는 Z 버퍼 값을 오버라이드하지 않으며, 기본값은 일반 삼각 래스터화에서 사용됩니다. 그러나 일부 효과의 경우 픽셀당 커스텀 Z 버퍼 뎁스 값을 출력하는 것이 유용합니다.

이러면 많은 GPU 에서는 일부 뎁스 버퍼 최적화를 끄게 되니 중요한 이유가 없다면 Z 버퍼 값을 오버라이드하지 말아야 합니다. 이는 SV_Depth에 대한 비용이 GPU 아키텍처에 따라 다양하지만 대체로는 HLSL 에서 빌트인 clip() 함수를 사용한 알파 테스팅과 비용은 비슷합니다. 뎁스를 수정하는 렌더 셰이더는 모두 일반 불투명 셰이더입니다(예를 들어AlphaTest 렌더링 대기열을 사용).

뎁스 출력값은 단일 float이어야 합니다.

버텍스 셰이더 출력 및 프래그먼트 셰이더 입력

버텍스 셰이더는 버텍스의 마지막 클립 공간 포지션을 출력해 GPU 에 화면의 어느 부분을 어느 뎁스로 래스터화할지 알려줍니다. 이 출력은 SV_POSITION 시맨틱을 가진 float4 타입이어야 합니다.

버텍스 셰이더로 생성된 다른 출력(“interpolators” 또는 “varyings”)은 특정 셰이더에 필요합니다. 버텍스 셰이더에서 출력된 이 값은 여러 렌더링된 삼각형의 표면에 보간되며 각 픽셀의 값은 프래그먼트 셰이더에 입력으로 전달됩니다.

많은 최신 GPU 는 변수의 시맨틱에는 관여하지 않지만 일부 구형 시스템(대표적으로 Direct3D 9 의 셰이더 모델 2 GPU)에는 시맨틱에 관한 특수 규칙이 있습니다.

  • TEXCOORD0, TEXCOORD1 등은 텍스처 좌표 및 포지션과 같은 임의의 고정밀도 데이터를 나타낼 때 사용합니다.
  • 버텍스 출력과 프래그먼트 입력의 COLOR0, COLOR1 시맨틱은 단순한 컬러 값과 같은 정밀도가 낮은 0–1 범위의 데이터입니다.

최상의 크로스 플랫폼 지원을 위해서는 버텍스 출력과 프래그먼트 입력을 TEXCOORDn 시맨틱으로 레이블해야 합니다.

방법을 알아보려면 셰이더 프로그램 예제를 참조하십시오.

인터폴레이터 개수 한계

버텍스에서 프래그먼트 셰이더로 정보를 넘기는 데 사용할 수 있는 전체 인터폴레이터 변수 개수에는 제한이 있습니다. 이 제한은 플랫폼 및 GPU 에 따라 달라지며 일반적인 가이드라인은 다음과 같습니다.

  • 인터폴레이터 최대 8 개: OpenGL ES 2.0 (iOS/Android), Direct3D 11 9.x 레벨 (Windows 휴대폰), Direct3 9 셰이더 모델 2.0 (구형 PC)입니다. 인터폴레이터 개수에 제한이 있지만 각 인터폴레이터는 4-컴포넌트 벡터가 될 수 있으며 일부 셰이더는 제한을 충족하기 위하여 여러가지를 합칩니다. 예를 들어, 텍스처 좌표 2 개를 float4 변수 하나로 패스할 수 있습니다. (첫 번째 좌표는 .xy 로, 두 번째 좌표는 .zw 로).
  • 인터폴레이터 최대 10 개: Direct3D 9 셰이더 모델 3.0(#pragma target 3.0)입니다.
  • 인터폴레이터 최대 16 개: OpenGL ES 3.0(iOS/Android), Metal(iOS)입니다.
  • 인터폴레이터 최대 32 개: Direct3D 10 셰이더 모델 4.0(#pragma target 4.0)입니다.

특정 타겟 하드웨어에 상관 없이 성능을 위해서는 인터폴레이터를 최소한으로 사용하는 게 좋습니다.

기타 특수 시맨틱

스크린 공간 픽셀 포지션: VPOS

프래그먼트 셰이더는 렌더링되고 있는 픽셀의 포지션을 특수 VPOS 시맨틱을 통해 받을 수 있습니다. 이 기능은 셰이더 모델 3.0 이후로만 지원되므로 셰이더에 #pragma target 3.0 컴파일 지시자가 있어야 합니다.

플랫폼에 따라 스크린 공간 포지션 입력의 기본 타입이 상이하므로 이식성을 최대한 높이려면 UNITY_VPOS_TYPE 타입을 사용해야 합니다(대부분의 플랫폼에서는 float4가 될 것이며, Direct3D 9 에서는 float2 가 됩니다).

또한 픽셀 포지션 시맨틱을 사용하면 버텍스를 프래그먼트로 넘기는 동일 구조에서 클립 공간 포지션(SV_POSITION) 및 VPOS 를 가지는 것이 어려워집니다. 따라서 버텍스 셰이더는 클립 공간 포지션을 별도의 “out” 변수로 출력해야 합니다. 아래의 예제 세이더를 참조하십시오.

Shader "Unlit/Screen Position"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            // note: no SV_POSITION in this struct
            struct v2f {
                float2 uv : TEXCOORD0;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                float2 uv : TEXCOORD0, // texture coordinate input
                out float4 outpos : SV_POSITION // clip space position output
                )
            {
                v2f o;
                o.uv = uv;
                outpos = UnityObjectToClipPos(vertex);
                return o;
            }

            sampler2D _MainTex;

            fixed4 frag (v2f i, UNITY_VPOS_TYPE screenPos : VPOS) : SV_Target
            {
                // screenPos.xy will contain pixel integer coordinates.
                // use them to implement a checkerboard pattern that skips rendering
                // 4x4 blocks of pixels

                // checker value will be negative for 4x4 blocks of pixels
                // in a checkerboard pattern
                screenPos.xy = floor(screenPos.xy * 0.25) * 0.5;
                float checker = -frac(screenPos.r + screenPos.g);

                // clip HLSL instruction stops rendering a pixel if value is negative
                clip(checker);

                // for pixels that were kept, read the texture and output it
                fixed4 c = tex2D (_MainTex, i.uv);
                return c;
            }
            ENDCG
        }
    }
}

페이스 오리엔테이션: VFACE

프래그먼트 셰이더는 렌더링된 표면이 카메라를 향하는지, 카메라를 등지고 있는지를 나타내는 변수를 받을 수 있습니다. 이 변수는 나뭇잎이나 얇은 오브젝트 같이 양면에서 볼 수 있어야 하는 지오메트리를 렌더링할 때 유용합니다. VFACE 시맨틱 입력 변수는 전면을 향하는 삼각형에 대해서는 양의 값, 후면을 향하는 삼각형에는 음의 값이 됩니다.

이 기능은 셰이더 모델 3.0 이후로만 지원되므로 셰이더에 #pragma target 3.0 컴파일 지시자가 있어야 합니다.

Shader "Unlit/Face Orientation"
{
    Properties
    {
        _ColorFront ("Front Color", Color) = (1,0.7,0.7,1)
        _ColorBack ("Back Color", Color) = (0.7,1,0.7,1)
    }
    SubShader
    {
        Pass
        {
            Cull Off // turn off backface culling

            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.0

            float4 vert (float4 vertex : POSITION) : SV_POSITION
            {
                return UnityObjectToClipPos(vertex);
            }

            fixed4 _ColorFront;
            fixed4 _ColorBack;

            fixed4 frag (fixed facing : VFACE) : SV_Target
            {
                // VFACE input positive for frontbaces,
                // negative for backfaces. Output one
                // of the two colors depending on that.
                return facing > 0 ? _ColorFront : _ColorBack;
            }
            ENDCG
        }
    }
}

위 예제의 셰이더는 스테이트를 사용하여 후면 컬링을 끄며 기본적로 후면을 향하는 삼각형은 전혀 렌더링되지 않습니다. 다음 예제에서는 다른 방향으로 회전하는 여러 사각형 메시에 셰이더를 적용했습니다.

버텍스 ID: SV_VertexID

버텍스 셰이더는 “버텍스 수”를 서명되지 않은 정수로 저장하는 변수를 받을 수 있습니다. 대부분 버텍스당 데이터를 텍스처 또는 컴퓨트 버퍼에서 추가로 가져오고자 할 때 유용합니다.

이 기능은 DX10(셰이더 모델 4.0) 및 GLCore / OpenGL ES 3 이후로만 지원되므로, 셰이더에 #pragma target 3.5 컴파일 지시자가 있어야 합니다.

Shader "Unlit/VertexID"
{
    SubShader
    {
        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #pragma target 3.5

            struct v2f {
                fixed4 color : TEXCOORD0;
                float4 pos : SV_POSITION;
            };

            v2f vert (
                float4 vertex : POSITION, // vertex position input
                uint vid : SV_VertexID // vertex ID, needs to be uint
                )
            {
                v2f o;
                o.pos = UnityObjectToClipPos(vertex);
                // output funky colors based on vertex ID
                float f = (float)vid;
                o.color = half4(sin(f/10),sin(f/100),sin(f/1000),0) * 0.5 + 0.5;
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                return i.color;
            }
            ENDCG
        }
    }
}

위 예제를 여기서 압축 파일로 다운로드할 수 있습니다.

버텍스 및 프래그먼트 셰이더 예제(Vertex and fragment shader examples)
Cg/HLSL에서 셰이더 프로퍼티 액세스(Accessing shader properties in Cg/HLSL)