Unity/Rendering&Shader

[SHADER] URP Shader를 뜯어보자 - 기본형

김성인 2023. 11. 7. 00:28

[SHADER] URP Shader를 뜯어보자 - 기본형

 

 함께 회사에 다니는 친구를 위하여 기록합니다.


 URP 전에 흔하게 사용되던 레거시 셰이더의 경우, Unity 에디터를 받는 곳에서 따로 받을 수 있지만 URP 셰이더의 경우 받을 수 있는 곳이 없습니다.  심지어 프로젝트 뷰에서도 생성하지 못하고, 레거시 셰이더만 생성이 가능합니다.

 

 위와 같은 이유로 직업 Shader Library에서 Unlit을 열어 시작하겠습니다.

 

 유니티에는 렌더링 관련된 많은 코드들이 모여 있는 폴더가 존재하는데, 그중 Shader Library라는 것은 이름 그대로 유니티에서 사용되는 셰이더들을 모아놓은 폴더입니다. 

 

 버전마다 이름이 조금씩 다르긴 하지만 보통 유니티로 생성한 프로젝트 폴더 아래, "Library\PackageCache\com.unity.render-pipelines.core@14.0.8\ShaderLibrary"에 존재합니다.

14.0.8은 Core RP Library 버전을 뜻합니다.

윈도우 파일탐색기에서 Unlit.shader 로 검색하면 쉽게 찾을 수 있습니다.

 

Unlit.shader 파일을 열면 하단에 첨부한 코드 이미지와 같이 엄청나게 긴 코드 블럭이 나옵니다.

 

 위 셰이더를 메테리얼에 연결 해주고, 메테리얼을 확인하면, 인스팩터에서 하단에 첨부한 이미지와 같은 값을 수정할 수 있는 옵션들(변수)를 볼 수 있습니다.

 다음에 다시 한번 이야기할 테지만, 위의 인스팩터 창은 GUI가 커스텀 화 된 것입니다.  다시 코드로 돌아가서 제일 아래쪽에 CustomEditor를 주석 처리하면, 커스텀 화 된 GUI가 사라지고, 날것의 모습을 볼 수 있게 됩니다.

 

 다음에 자세히 설명하고, 만드는 방법을 설명할 테니, 우선은 Unlit.shader 파일에서 기본 골격만 남겨보도록 하겠습니다.  Unlit.shader 파일을 복사 붙여넣기 하여 다른 폴더에 넣고, 시작하면 데이터가 날아가지 않고, 잃어버릴 일도 적습니다. 

 

 기본적으로 텍스처를 한 장 이용하여 샘플러까지 보기 위하여 하나만 2D 텍스처 프로퍼티 하나만 남기고 다 삭제합니다.

 

 여기서 _MainTex와 _BaseMap이 존재하는데, 레거시 셰이더를 사용하던 시절에는 _MainTex가 자주 사용되었었습니다.  심지어 아직도 파티클 시스템에서는 _MainTex를 기본텍스쳐로 인식하고 있으니, 기억해 두면 좋을 것 같습니다.

 

 다음으로 Pass를 구문을 남기는데, Pass에는 셰이더를 직접적으로 사용하기 위한 코드 몸통 부위라고 생각하면 좋습니다.

  그다음으로 레거시 셰이더에서는 CGPROGRAM을 사용하는데, URP에서는 HLSLPROGRAM을 사용합니다.  씨지 프로그램 또한 URP에서 작동하긴 합니다.  동시에 UI 쪽에서는 아직도 씨지 프로그램이 대부분 사용되고 있습니다.

 

 HLSLPROGRAM으로 코드가 시작된다면, ENDHLSL로 코드가 끝나니, 잊지 말고 작성하길 바랍니다.

 

 다음으로 랜더파이프라인에서 우리가 원하는 결과물을 얻기 위하여, 직접 관여를 하기 위하여 셰이더를 이용하는데, 특히 버텍스 셰이더와 프레그먼트 셰이더를 마음껏 만들고, 수정할 수 있습니다.  실질적으로는 이 두 가지 셰이더가 우리가 관여할 수 있다고 생각하면 좋을 것 같습니다.  동시에 보통의 셰이더 프로그램은 이 두 가지 셰이더가 하나의 세트로 작동합니다.  색을 아무리 칠하고 싶더라도, 칠할 장소가 없다면 불가능한 것과 같은 이치입니다.

 이 두가지 셰이더도 미리 이름을 적어두고 밑에서 계산을 합니다.

 이름에서 볼 수 있듯이 vertex는 버텍스 셰이더를 뜻하며 뒤쪽에 있는 UnlitPassVertex가 버텍스 셰이더 부분의 이름입니다.

 

 다음으로 유니티에서는 자주 쓰이는 기능(함수)들을 미리 만들어 두었는데, 그 파일들을 코드에 포함시킵니다.

위에서 UnlitForwardPass.hlsl 파일을 열어 보면 버텍스 셰이더와 프레그먼트 셰이더가 나옵니다.

 

 셰이더를 보기 전에, 셰이더에게 정보를 넘기기 위한 구조체가 존재합니다.  이 구조체 역시 버텍스, 프레그먼트 셰이더와 같이 두 가지가 존재합니다.

 처음 이 글을 시작할 때, 텍스처만 그려보기로 했으니, 딱 두 가지가 필요합니다.  먼저 색을 칠할 픽셀들의 위치와 3D 오브젝트의 형태를 알기 위한 버텍스들의 위치와 텍스처를 입혀주기 위한 UV가 그것입니다.  그럼 상단에 보이는 코드 중 float2 uv와 float4 position 구문을 빼고 삭제합니다.

 

 그 후, 버텍스 셰이더, 프레그먼트 셰이더를 작성합니다.

 

필요 없는 부분들을 삭제 하면 하단의 코드와 같이 남게 됩니다.

 

 이 상태에서 사용할 변수들을 선언해주고, 유니티에서 제공하는 라이브러리를 추가하고, 버텍스 정보를 넘기기 위한 구조체, 버텍스 셰이더, 프레그먼트 셰이더를 작성해 주어야 합니다.


 여기까지 전부 진행하게 되면 하단의 코드가 됩니다.  전체 코드를 본 후, 각 코드 블럭을 설명하겠습니다.

 

Shader "CustomURP/OnlyTexture"
{
    Properties
    {
        _BaseMap("Base Map", 2D) = "white"
    }

        SubShader
    {
        Tags 
        { 
            "RenderType" = "Opaque"
            "IgnoreProjector" = "True"
            "RenderPipeline" = "UniversalRenderPipeline" 
        }

        ZWrite ON
        Cull Off

        Pass
        {
            HLSLPROGRAM
            #pragma vertex OnlyTexturePassVertex
            #pragma fragment OnlyTexturePassFragment

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"            

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float2 uv : TEXCOORD0;    
                float4 positionCS : SV_POSITION;
            };

            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
            CBUFFER_END

            Varyings OnlyTexturePassVertex(Attributes IN)
            {
                Varyings OUT;
                OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                // OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
                return OUT;
            }

            half4 OnlyTexturePassFragment(Varyings IN) : SV_Target
            {
                half4 color;
                color =  SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                        
                return color;
            }
            ENDHLSL
        }
    }
}

    Properties
    {
        _BaseMap("Base Map", 2D) = "white"
    }

인스팩터에 노출시킨 변수입니다.

#include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"

 기본적으로 core.hlsl은 포함시켜야 그나마 쉽게 셰이더를 작동시킬 수 있습니다.

            struct Attributes
            {
                float4 positionOS   : POSITION;
                float2 uv           : TEXCOORD0;
            };

            struct Varyings
            {
                float2 uv : TEXCOORD0;    
                float4 positionCS : SV_POSITION;
            };

 버텍스 정보를 프레그먼트 셰이더에게 넘기기 위한 구조체입니다.  이 외에도 다른 용도가 있는데, 뒤에서 설명하겠습니다.

            TEXTURE2D(_BaseMap);
            SAMPLER(sampler_BaseMap);

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
            CBUFFER_END

 텍스처 샘플러와 C 버퍼입니다.  C 버퍼 또한 뒤에서 설명하겠습니다.  지금은 간단하게 C 버퍼는 SRP베처를 위한 코드이고, 최적화를 하기 위함이라고 생각하고 계시면 좋겠습니다.

            Varyings OnlyTexturePassVertex(Attributes IN)
            {
                Varyings OUT;
                OUT.positionCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                // OUT.uv = TRANSFORM_TEX(IN.uv, _BaseMap);
                return OUT;
            }

 버텍스 셰이더 구문입니다.  이곳에서 버텍스의 위치들을 계산합니다.  코드 아래쪽에 주석처리 된 구문은 IN.uv;와 비슷한 역할을 합니다.  하지만 IN.uv;의 경우 인스팩터에서 제공하는 Tiling, Offset GUI가 작동하지 않습니다.

            half4 OnlyTexturePassFragment(Varyings IN) : SV_Target
            {
                half4 color;
                color =  SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, IN.uv);
                        
                return color;
            }

 프레그먼트 셰이더 구문입니다.  SAMPLE_TEXTURE2D 이 함수를 통하여 정해진 UV에 텍스처가 입혀집니다.

 

 여기까지 작성하면 텍스처가 입혀진 3D 오브젝트를 유니티 월드 안에서 볼 수 있습니다.


 렌더파이프라인에서 버텍스의 위치는 우리가 보는 3D 공간에서 몇 개의 단계를 거쳐야 합니다.  로컬(모델) 스페이스 > 월드 스페이스 > 뷰(카메라) 스페이스 > 클립 스페이스 > 스크린(모니터) 스페이스, 그리고 하단의 TransformObjectToHClip이 이 단계들을 계산해 주는 함수입니다.

 이 함수의 경우 SpaceTransform.hlsl에 들어있습니다.


 

'Unity > Rendering&Shader' 카테고리의 다른 글

[SHADERLAB] URP Shader 1편  (0) 2023.11.08
[SHADER] Object의 Scale 값을 이용한 UV Shader  (0) 2023.11.08
[RENDERING] Planar Reflection, Render  (0) 2023.10.27
[RENDERING] Rendering Layers  (1) 2023.10.24
[SHADER] Texture Channel Swap  (0) 2023.08.29