게임개발(Unity)

유니티 셰이더 (Unlit Shader) 뜯어보기

밧지성 2024. 7. 16. 15:09
728x90
반응형

Unlit Shader 뜯어보기

먼저 빛의 영향을 받지 않는 Unlit shader부터 뜯어보자.

먼저 shader를 unlit shader를 하나 생성해주고, material 또한 생성해 unlit으로 변경해준다.

이를 현재 object에 drag & drop 해보면 음영처리가 없고, 밋밋하게 바뀐다.

입체감이 전혀 없다.

먼저 생성한 shader를 클릭해서 안으로 들어가면 다음과 같은 script가 적혀있을것이다.

Shader "Unlit/TestShader"
{
    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
        LOD 100

        Pass
        {
            CGPROGRAM
            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

            #include "UnityCG.cginc"

            struct appdata
            {
                float4 vertex : POSITION;
                float2 uv : TEXCOORD0;
            };

            struct v2f
            {
                float2 uv : TEXCOORD0;
                UNITY_FOG_COORDS(1)
                float4 vertex : SV_POSITION;
            };

            sampler2D _MainTex;
            float4 _MainTex_ST;

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }
            ENDCG
        }
    }
}

그 전에 알아야 할 것이 DirectX 11과는 다르게 Vertex Shader 부분과 Fragment(Pixel) Shader부분이 통합되어있다.

한줄씩 뜯어보자.

셰이더 코드 뜯어보기

경로 및 이름

Shader "Unlit/TestShader"

위 코드를 변경하면, 셰이더를 재질에 할당하기 위해 선택해야 하는 경로가 변경된다.

특성

    Properties
    {
        _MainTex ("Texture", 2D) = "white" {}
    }

Inspector 창에서 보여질 특성을 여기서 선언한다. 기본적으로 Unlit에서는 하나의 텍스처만 선언되어 있다. (어떠한 것들은 선언할 필요가 없다)

하위 셰이더

    SubShader
    {

여기서 부터는 한 개 이상의 SubShader를 둘 수 있다. 하위 셰이더에는 몇 가지 종류가 존재하는데, 셰이더를 로딩할 때 유니티는 여러 셰이더 중 GPU에서 지원하는 첫 번째 하위 셰이더를 사용/한다.

각각의 하위 셰이더는 렌더링 패스의 리스트를 포함한다.

태그

        Tags { "RenderType"="Opaque" }

태그는 정보를 표현하는 키와 값의 쌍으로 구성되어 있다. 정보의 예를 들어보자면 위 코드처럼 렌더링 큐의 사용 여부 같은 것이다. 투명과 불투명한 게임 오브젝트는 각기 다른 렌더링 큐에서 렌더링 되므로 여기서 RenderType을 Opaque(불투명)로 지정한 이유이다.

Pass/CGPROGRAM/ENDCG

        Pass
        {
            CGPROGRAM
            ......생략
            }ENDCG

Pass는 블록( {…} )으로 구현된다.

먼저 Pass에는 렌더링을 위한 정보와 실제로 셰이더에서 계산하는 코드와 같은 정보를 포함한다. 이는 C#에서 하나씩 분리돼 수행할 수 있다.

        Pass
        {
            Name "BASE"
            ...//생략

          } ENDCG
        }

        Pass
        {
                Name "TEXT"
                ...//생략
            } ENDCG
        }  

쉽게 말해 위와같은 구조를 가진다는 것이다. (c#에서 선택적으로 호출할 수 있다는 개념은 아닌거 같다.)

PRAGMA 선언

            #pragma vertex vert
            #pragma fragment frag
            // make fog work
            #pragma multi_compile_fog

이는 셰이더 컴파일러에게 정보를 전달하는 방법이다. 즉 여기선 vertex shader와 pixel shader에 사용할 함수를 지정해준 것이다.

입출력 구조체

           #include "UnityCG.cginc"

           struct appdata
           {
               float4 vertex : POSITION;
               float2 uv : TEXCOORD0;
           };

           struct v2f
           {
               float2 uv : TEXCOORD0;
               UNITY_FOG_COORDS(1)
               float4 vertex : SV_POSITION;
           };

슬슬 DX에서 본 익숙한 구조들이 많이 나온다. Vertex Shader의 output은 Pixel Shader의 Input이 되는것이 Pipeline의 기본적인 흐름이었다. 즉 여기서 v2f는 Vertex to Fragment의 정보를 표현한 구조체이다.

appdata는 정점(vertex) 셰이더는 입력 구조체를 통해 특정 input을 요청할 수 있는데 이 구조체가 appdata이다. (쉽게 말하면 vertex의 input구조체 이다.)

변수 선언 이후에 있는 : POSITION 같은 구조를 Simentics(시맨틱) 이라고 한다. 이는 컴파일러에게 구조체 내 특정한 맴버 내애 어떤 종류를 저장할 것인지 알려주는 역할이다. (예를 들어 SV_POSITION은 화면상의 정점의 위치를 의미한다)

참고로 SV는 System Value의 약자로 이는 파이프라인의 특정한 위치를 참조한다.

변수 선언


            sampler2D _MainTex;
            float4 _MainTex_ST;

이전에 특성 블록에 저장한 모든 특성은 CGPROGRAM안에 따로 정의해주어야 한다. 여기서는 특성에서 하나의 Main Texture를 받았으므로, Sampler2D와 MainTex_ST로 정의했다.

정점 함수와 프래그먼트 함수

            v2f vert (appdata v)
            {
                v2f o;
                o.vertex = UnityObjectToClipPos(v.vertex);
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
                UNITY_TRANSFER_FOG(o,o.vertex);
                return o;
            }

            fixed4 frag (v2f i) : SV_Target
            {
                // sample the texture
                fixed4 col = tex2D(_MainTex, i.uv);
                // apply fog
                UNITY_APPLY_FOG(i.fogCoord, col);
                return col;
            }

pragma선언에서 지정해준 vertex와 fragment 셰이더로 동작할 함수이다.

처음 볼만한 것은 UnityObjectToClipPos일텐데, 이는 정점의 위치가 한 3D 좌표 공간에서 다음에 수행할 계산에 더 적합한 3D 좌표로 사영해주는 변환 함수이다.

반응형