본문 바로가기

유니티/셰이더 및 VFX

[Unity] 셰이더 코드 (2) - ShaderLab과 HLSL 기본 구조

반응형

지난 포스팅에서 셰이더 코드는 유니티 전용 셰이더 언어인 ShaderLab과 HLSL언어를 사용해 작성할 수 있다고 하였다.

이번 포스팅에서는 간단한 예시를 통해 실제 코드가 어떻게 작성이 되는지 나타내었다.

 

Built-in 파이프라인의 경우 Legacy가 될 예정이기 때문에 URP를 기준으로 글을 작성하였다.


셰이더 생성

유니티의 프로젝트 창에서 Create - Shader - Unit Shader를 눌러 파일을 생성한다.

 

Create / Shader / Unlit Shader

 

생성된 셰이더 코드를 열어보면 다음과 같다.

 

Unlit 셰이더 코드

 

참고로 이 기본 Unlit 셰이더 코드는 Cg로 작성되어있다. 유니티에서는 HLSL을 사용한 셰이더 제작을 권장하기 때문에 해당 구조를 HLSL로 변경하여 아래에 다시 작성하며 포스팅을 이어나간다.

 

우리는 위 코드에서 셰이더의 뼈대만 살펴보면 된다. 전체적인 구조는 같기 때문이다.

크게 Shader - (Properties / SubShader) - (Tags / Pass) - xxPROGRAM으로 되어있다는 것을 기억하자.

 

Shader "Custom/JslandDevlog"
{
    Properties
    {
        ...
    }
    SubShader
    {
        Pass
        {
        	...
        }
    }
}

HLSL 셰이더 프로그래밍

이제 HLSL코드를 보며 각 문장이 의미하는 바가 무엇인지 살펴보고자 한다.

 

ShaderLab

우선 시작하기 전에 알아두어야 할 것은, 우리는 셰이더 파일에 셰이더 언어를 통해 작성하려 하고 있으며 이것의 올바른 이름은 셰이더 오브젝트이다. 그리고 이러한 셰이더 오브젝트는 셰이더 랩(ShaderLab)을 사용해야 한다.

 

그렇다면 ShaderLab은 어떻게 사용할 수 있을까?

아래는 예시 코드이며, 이 셰이더는 Unlit 색상을 나타낸다.

 

Shader "Custom/JslandDevlog"
{
    Properties
    {
    	_BaseColor("Base Color", Color) = (1,1,1,1)
    }
    SubShader
    {
        Tags
        {
            "Render Type" = "Opaque"
            "RenderPipeline" = "UniversalPipeline"
            "Queue" = "Geometry"
        }
        Pass
        {
            Tags
            {
                "LightMode" = "UniversalForward"
            }

            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

            struct appdata
            {
                float4 positionOS : POSITION;
            };

            struct v2f
            {
                float4 positionCS : SV_POSITION;
            };

            float4 _BaseColor;

            v2f vert(appdata v)
            {
                v2f o;
                o.positionCS = TransformObjectToHClip(v.positionOS);
                return o;
            }

            float4 frag(v2f i) : SV_TARGET
            {
                return _BaseColor;
            }
            ENDHLSL
        }
    }
}

 

셰이더 키워드 (Shader"name")

여기서 첫 줄 Shader"파일이름"은 머테리얼에서 표시될 파일의 이름을 나타낸다.

위 예시처럼 '/'를 사용하여 폴더로 묶을 수도 있다.

 

프로퍼티 (Properties)

일반적인 프로그래밍에서 사용되는 프로퍼티와 동일하다. 이곳에 작성하는 변수는 머테리얼에서 노출되기 때문에 쉽게 변경할 수 있다.

 

여기 사용된 변수 선언 방식은 우리가 기존에 사용하던 사뭇 다르다.

 

_BaseColor("Base Color", Color) = (1,1,1,1)

각 단어는 어떤 의미를 갖고 있을까?

 

_BaseColor Computer readable name이며 _(언더바)로 시작
"Base Color" Human readable name이며 "(쌍따옴표)사이에 작성
Color 변수의 타입
(1,1,1,1) 변수 초기값

 

서브셰이더 (SubShader)

SubShader의 특징은 다음과 같다.

  • 하나의 파일에 여러 개의 서브셰이더를 작성할 수 있다.
  • 유니티는 셰이더를 로드할 때 서브셰이더의 리스트 중에서 기기가 지원하는 가장 위에 것을 선택한다.
  • 만약 어떠한 서브셰이더도 지원하지 않는다면 FallBack 셰이더를 사용한다.
  • 모든 서브셰이더가 실패하고 FallBack도 없다면 컴파일에 실패하고 마젠타색으로 나타난다.

이를 요약하면 다음과 같이 나타낼 수 있다.

 

Shader "something"
{
    Properties {...}
    SubShader {...}
    SubShader {...}

    Fallback "Unlit/Color"
}

 

(Pass와 Tags, HLSLPROGRAM, struct, #pragma 등은 다음 포스팅에서 이어서 작성됩니다.)

반응형