Unity URP Shader Study (1) - HLSL : 흔들리는 깃발 구현

KAY·2023년 6월 11일
0

Study

목록 보기
2/5
post-thumbnail

친구들과 게임을 개발했을 때 사용했던 쉐이더들을 차근히 벨로그에 정리해보려고 한다.
부끄럽게도, 개발하던 당시에는 쉐이더에 대한 지식이 얕아, 인터넷에서 찾았던 쉐이더 코드를 오류가 나지 않을 정도만 원하는 효과 한 두 개만 추가하는 정도로 수정하여 거의 그대로 사용했었다. 그렇기 때문에 당시 이해하지 못한 구조가 꽤 있었다.

다시 예전 코드들을 뜯어보면서 공부해야지, 공부하면서 정리 해야지 했는데 계속 못하고 있었다. 최근에 많은 일들이 정리되고 본격적으로 다시 HLSL와 ShaderGraph를 공부하기 시작하면서 그 기회가 생겼다. 급하게 공부했던 내용들을 최근에 다시 채우고 있으니 구조가 드디어! 이해가 되어 괜히 뿌듯함을 느끼고 있다.

아직 원인을 잘 모르는 이슈도 있지만, 우선은 구현해낸 것을 정리하는 것을 목표로 한다.
공부하면서 직접 구현해본 것들도 Study 시리즈에 정리해서 올릴 예정이기에, 부족한 부분이 많고 최적화 이슈 등이 있을 수도 있다.


(1)바람에 흔들리고, (2)낡아서 구멍이 뚫린 깃발을 구현해야 했다.
바람에 흔들리는 연출은 버텍스 애니메이션으로 구현하고자 했고, 구멍 뚫린 것은 텍스쳐를 알파값을 가지는 png로 만들어 그 알파값을 받아왔다.

깃발 쉐이더의 베이스는 Станислав Коваленко의 유튜브 영상을 참고하였다.
영상의 더보기에 보면 드라이브 링크가 있어서, 거기 코드를 베이스로 가져왔었다. 다만 코드를 보면 Alpha Clip을 사용하는 게 아닌 Transparent 설정에 Blend가 선언되어있다. 구현이 안 되는 것은 아니지만 굳이 Transparent를 쓸 이유는 없다고 생각해서 수정하였다.(투명 오브젝트가 렌더링 할 때 무겁다고 들어서 최소한으로 사용하는 게 최적화에 좋다고 들었음.) 또한 필요 없는 부분이라 생각한 영역은 내 입맛대로 조금씩 바꿨다. 그리고 CG언어로 작성되어 있길래 HLSL로 교체했다.


깃발 매쉬는 유니티의 디폴트 3D 매쉬인 Plane을 사용했다. 버텍스가 입체적으로 움직여야 하기때문에 버텍스 수가 적은 Quad는 사용하면 안 됨.

전체 쉐이더 코드는 다음과 같다.
주석 처리한 것은 빛과 환경광을 받아오는 테스트를 하기 위한 내용이다. Directional Light의 값을 사용하려 한다면 코드 앞에 있는 //를 지우면 된다.
내가 구현한 바를 주석으로 달아놓았다.


코드

Shader "Unlit/FlagShader"
{
    Properties
    {
        [NoScaleOffset] _MainTex ("Texture", 2D) = "white" {} //텍스쳐를 받음.
        //텍스쳐의 스케일 조절과 위치 이동이 필요 없기 때문에 NoScaleOff해줌
        _AlphaClip ("AlphaClip", Range(0, 1)) = 0.5 //알파클립값
        _XCycle ("XCycle", Float) = 1 //오브젝트의 X축 사이클
        _ZCycle ("ZCycle", Float) = 1 //오브젝트의 Z축 사이클
        _WindIntensity("WindIntensity", Range(0, 5)) = 1 //얼마만큼 펄럭거리게 할 것인지
        _WindSpeed("WindSpeed", Float) = 1 //바람 부는 속도
        _Amount ("Amount", Range (0, 1)) = 1 //바람 부는 양
    }
    SubShader
    {
        Tags
        {
            "RenderType"="TransparentCutout" //알파 클립을 사용하기 위한 렌더 타입 설정
            "RenderPipeline" = "UniversalPipeline"
            "Queue" = "AlphaTest" //알파 클립을 사용하기 위한 렌더 큐
        }
        
        LOD 100
        Cull off //깃발이 앞뒤로 렌더링 되어야할 수 있기 때문에 Cull Off

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Core.hlsl"
            #include "Packages/com.unity.render-pipelines.universal/ShaderLibrary/Lighting.hlsl"
            //이 Include된 Lighting은 빛을 사용하기 위해 선언된 것,
            //Unlit으로 사용한다면 지워도 괜찮다.
            
            struct Attributes
            {
                float4 vertex   : POSITION;
                float2 uv       : TEXCOORD0;
                //float3 normalOS : NORMAL;
            };

            struct Varyings
            {
                float2 uv       : TEXCOORD0;
                float4 vertex   : SV_POSITION;
                //float3 normal   : TEXCOORD1;
                //float3 lightDir : TEXCOORD2;
            };

            sampler2D _MainTex;
            
            CBUFFER_START(UnityPerMaterial)
            half4 _MainTex_ST;
            half  _XCycle, _ZCycle, _AlphaClip, _WindIntensity, _WindSpeed, _Amount;
            CBUFFER_END

            Varyings vert (Attributes v)
            {
                Varyings o;
                o.vertex = TransformObjectToHClip(v.vertex.xyz);

                half WindIntensity = v.uv.x * _WindIntensity;
                //깃발이 시작하는 위치는 흔들리면 안 되기 때문에 v.uv.x 곱해줌
                half FlagWave = sin((v.vertex.x * _XCycle + v.vertex.z * _ZCycle) * _Amount + _Time.y * _WindSpeed)
                                * WindIntensity;
                //Sin(xa) 해주면 a값에 따라 Sin 함수의 주기가 바뀌고,
                //Sin(x)*a 해주면 a 값에 따라 Sin 함수의 높낮이가 바뀐다.
                //즉, XCycle, ZCycle, Amount와 Speed에 따라 주기가 바뀌고
                //WindIntensity에 따라 깃발의 큰 흔들림 값이 바뀐다.
                
                v.vertex.y +=  FlagWave;
                //y축으로 FlagWave값을 더해주어, y축으로 버텍스 애니메이션을 줌

                o.vertex = TransformObjectToHClip(v.vertex.xyz);
                //vertex데이터를 두 번 선언해줬는데,
                //안 쓰여있으면 깃발이 안 흔들린다. 자세한 원인은 아직 모르겠다.
                //그렇다고 v.vertex.y +=FlagWave; 를 o.vertex.y로 계산하면
                //깃발이 위/아래로는 흔들려도 입체적으로 볼륨이 생기지 않는다.
                
                o.uv = TRANSFORM_TEX(v.uv, _MainTex);
               
                //o.normal = TransformObjectToWorldNormal(v.normalOS);
                //o.lightDir = normalize(_MainLightPosition.xyz);
                
                return o;
            }

            half4 frag (Varyings i) : SV_Target
            {
                //i.normal = normalize(i.normal);
                //float NdotL = saturate(dot(i.normal, i.lightDir));
                //half3 ambient = SampleSH(i.normal);
                //half3 lighting = NdotL * _MainLightColor.rgb + ambient;

                half4 col = tex2D(_MainTex, i.uv);
                //col.rgb *= lighting;
                
                clip(col.a - _AlphaClip); //Clip함수를 이용하여 알파값을 빼준다. 구멍 송송 깃발이 된다.
                return col;
            }

0개의 댓글