본문 바로가기
튜터리얼_스터디

(HLSL)아티스트를 위한 URP 셰이더 Shader #11 - 버텍스 칼라를 마스킹 기능으로 사용해 봅시다

by 대마왕J 2021. 11. 1.

 하하하 재택이 끝나고 출근을 시작하니까 급 체력이 아슬아슬해서 그동안 못했네요. 
적응하는대로 조금씩 텐션을 올리도록 하겠습니다. 

원 나이먹으니까 조금의 변화에도 몸이 맥을 못춤. 
계절이 변해도 그렇다는. 어여 몸이 적응해야 하는데 

후들후들

안내 !!!
여기 나오는 내용은 유니티 쉐이더 스타트업에 나오는 예제를 최신 URP에 맞추어 예제를 번역한 내용입니다. 
때문에 이론적 내용이 상당히 간략하거나 불친절하며, 예제에 대한 설명도 축약되어 있기 때문에
책과 같이 보시는 것을 추천하는 바입니다.

 

자 지난번부터 드디어 책을 옆에 가져다 놓고 씁니다 ㅋㅋㅋㅋㅋㅋㅋㅋ (그동안 머리속으로만 했다는 얘기) 

206페이지의 버텍스 컬러를 마스킹 기능으로 사용해 봅시다! 를 따라할 거예요. 

리소스는 .. 책 사신 분은 이미 다 가지고 계실 거구요 
이 블로그에서는 ... 어디 있을까요... 숨겨져 있을지도 몰라요... 잘 찾아봐요...  
너무 대놓고 드리면 출판사 사장님한테 혼난단 말야... ....                                                                              여기

아우 등이 가렵네 뭔가 긁어보고 싶네

 

지난 시간에 버텍스 칼라를 보이게 하는 것 정도는 해 봤지만 ..

그럼에도 불구하고 걍 첨으로 돌아가서 해봅시다 
어차피 뭐 다 잊었을테니까. 저는 여러분들을 믿지 않습니다 (?) 

우선 당연히 시작하는 것은, 텍스쳐 한 장 셰이더부터 시작하는 것입니다. 

지금쯤이면 뭐 .. 더 안드려도 되겠지요?

마지막이라는 느낌으로 올려드립니다. 

URPUnlitShaderBasic .shader
0.00MB


텍스쳐 4장을 적용시켜 봅시다 

좋아요 뭐 텍스쳐 4장 추가하는거, 예상하고 계셨잖아요? 
4장을 추가만 하는거라면 쉽잖아요. 

대신 출력은 아무거나 해도 돼요 일단. 첫 번째 것만 해 두죠 뭐. 

일단 Properties에서 텍스쳐 4장을 받게 하고요 

Properties
    {
        _BaseMap("BaseMap",2D) = "white"{}
        _BaseMap2("BaseMap2",2D) = "white"{}
        _BaseMap3("BaseMap3",2D) = "white"{}
        _BaseMap4("BaseMap4",2D) = "white"{}
    }

텍스쳐 오브젝트와 샘플러도 4개를 준비하고 . 아 복사 붙여넣기 좋다 

 TEXTURE2D(_BaseMap);
 TEXTURE2D(_BaseMap2);
 TEXTURE2D(_BaseMap3);
 TEXTURE2D(_BaseMap4);
 SAMPLER(sampler_BaseMap);
 SAMPLER(sampler_BaseMap2);
 SAMPLER(sampler_BaseMap3);
 SAMPLER(sampler_BaseMap4);

C버퍼에서도 _ST 변수를 4개 만들어 줘야 해요. 
_ST 변수는 뭔지 설명했죠? 텍스쳐 각각의 타일링과 옵셋값이예요

저게 TRANSFORM_TEX (uv , ST) 에 의해 각각  텍스쳐의 UV값에 영향을 주게 됩니다. 

CBUFFER_START(UnityPerMaterial)
  float4 _BaseMap_ST;
  float4 _BaseMap2_ST;
  float4 _BaseMap3_ST;
  float4 _BaseMap4_ST;
CBUFFER_END

자 여기까지 되었으면 텍스쳐를 본격 4장 받게 만들면 되지요 
버텍스 셰이더야 뭐 건드릴게 없고, 

픽셀셰이더만. 

half4 frag(Varyings IN) : SV_Target
  {
  half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
  half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
  half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
  half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));
  return color;
  }

리턴은 첫 번째 텍스쳐만... 

그럼 결과는 이렇게 됩니다. 

텍스쳐 4장을 넣어도

첫 번째 텍스쳐만 우직하게 출력하는... 말이죠. 

이제 텍스쳐 4개를 버텍스 칼라를 이용하여 마스킹하는 순서입니다. 
그래요 아시는 분들한테는 너무너무 쉬운 내용이지만 여기는 아직 생초보를 위한데니까..

더보기
Shader "VertexColor2"
{
    Properties
    {
        _BaseMap("BaseMap",2D) = "white"{}
        _BaseMap2("BaseMap2",2D) = "white"{}
        _BaseMap3("BaseMap3",2D) = "white"{}
        _BaseMap4("BaseMap4",2D) = "white"{}
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

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

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

            TEXTURE2D(_BaseMap);
            TEXTURE2D(_BaseMap2);
            TEXTURE2D(_BaseMap3);
            TEXTURE2D(_BaseMap4);
            SAMPLER(sampler_BaseMap);
            SAMPLER(sampler_BaseMap2);
            SAMPLER(sampler_BaseMap3);
            SAMPLER(sampler_BaseMap4);

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseMap2_ST;
                float4 _BaseMap3_ST;
                float4 _BaseMap4_ST;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
                half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
                half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
                half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));
                return color;
            }
            ENDHLSL
        }
    }
}

버텍스 칼라 칠하기 

이걸 하려면 일단, 버텍스 칼라를 칠해야겠지요. 
셰이더에서 버텍스 칼라가 보이게 만들어 둔 채, 칠해봐야겠어요 

잠시 텍스쳐는 안보여도 상관 없고, 위에 만들어둔 코드에서 버텍스 칼라만 나오도록 해 봅시다 

일단 VertexColor의 입력인 Attributes 와 출력인 Varyings에다가 각각 color를 만들어 줍시다. 
Attributes  의 칼라는 버텍스 정보로부터 받아와서 버텍스 셰이더로 전달해 주는 녀석이고, 
Varyings 의 칼라는 버텍스 셰이더에서 픽셀셰이더로 넘기는 변수죠

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

struct Varyings
  {
  float4 positionHCS  : SV_POSITION;
  float2 uv           : TEXCOORD0;
  float4 color        : COLOR;
  };

그럼 버텍스 셰이더에서 이렇게 받아서 전달만 해주면 됩니다. 픽셀셰이더가 가져갈 수 있게끔요. 

Varyings vert(Attributes IN)
  {
  Varyings OUT;
  OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
  OUT.uv = IN.uv;
  OUT.color = IN.color; //Attributes 에서 칼라를 받아 Varyings 으로 전달 
  return OUT;
  }

그럼 픽셀셰이더에서는 버텍스 칼라를 그냥 출력만 해주면 되는 겁니다. 출력 노드에 꽂듯이 말이죠

half4 frag(Varyings IN) : SV_Target
  {
  half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
  half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
  half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
  half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));
  return IN.color; //임시로 버텍스 칼라만 출력해본다 
  }

잘 되었는지 확인해 봅시다 

오오 잘 나오네요. 

만약 희게 나오신 분이 있으시면, 폴리브러쉬를 꺼내서 버텍스 칼라를 칠해주세요. 
칠하는 요령은 먼저 검은색으로 다 채우고, (그래야 0이 되니까)

자아 검은색 바탕에 완전한 빨강과 녹색, 파랑색 만으로 대충 그림을 그려보는 겁니다. 위와 같이요. 
아 뭐 섞여서 보라색 되는건 냅두고요. 그냥 브러쉬를 검은 바탕에 R G B 로만 그려보라는 말예요 

 

각 채널별로 출력해서 보는것도 재밌죠 

 


버텍스 칼라로 마스킹하기 

여기는 lerp 노드를 이용해서 마스킹해 줍니다. 

일단 텍스쳐 하나만. 

half4 frag(Varyings IN) : SV_Target
  {
  half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
  half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
  half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
  half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));
  return lerp(color, color2, IN.color.r); //R 마스킹
  }

마지막 줄을 보면 텍스쳐 두 개의 결과를 lerp의 첫번째와 두 번째에 넣었죠 
그리고 버텍스 칼라의 R만 꺼내서 세 번째에 넣었습니다.  
이제 버텍스 칼라 R 에 의해서 결과가 결정될 거예요. 

짜잔. 풀과 돌이 섞였어요. 풀이 너무 안보이길래 텍스쳐를 바꿈. 

 

이제 이 결과물에 다시 lerp를 해주... 면 되는데 보기가 복잡하니까, 먼저 변수를 하나 만들고 가죠
아래처럼 finalColor 하나 만들어서 넣어주면 편합니다. 

half4 frag(Varyings IN) : SV_Target
  {
  half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
  half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
  half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
  half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));

  // return lerp(color, color2, IN.color.r); //R 마스킹

  half4 finalColor = lerp(color, color2, IN.color.r); //R 마스킹
  return finalColor;
  }

그리고 이제 나머지 텍스쳐들을 lerp로 추가해 주기 시작 

half4 frag(Varyings IN) : SV_Target
  {
  half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
  half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
  half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
  half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));

  // return lerp(color, color2, IN.color.r); //R 마스킹

  half4 finalColor = lerp(color, color2, IN.color.r); //R 마스킹
  finalColor = lerp(finalColor, color3, IN.color.g); //G 마스킹
  finalColor = lerp(finalColor, color4, IN.color.b); //B 마스킹
  return finalColor;
  }

자 그럼 이렇게 됩니다.

 

풀코드는 이거입니다. 다운로드도 있으니 잘 안되면 참고해 보세요 

더보기
Shader "VertexColor2"
{
    Properties
    {
        _BaseMap("BaseMap",2D) = "white"{}
        _BaseMap2("BaseMap2",2D) = "white"{}
        _BaseMap3("BaseMap3",2D) = "white"{}
        _BaseMap4("BaseMap4",2D) = "white"{}
    }

    SubShader
    {
        Tags { "RenderType" = "Opaque" "RenderPipeline" = "UniversalPipeline" }

        Pass
        {
            HLSLPROGRAM
            #pragma vertex vert
            #pragma fragment frag

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

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

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
                float2 uv           : TEXCOORD0;
                float4 color        : COLOR;
            };

            TEXTURE2D(_BaseMap);
            TEXTURE2D(_BaseMap2);
            TEXTURE2D(_BaseMap3);
            TEXTURE2D(_BaseMap4);
            SAMPLER(sampler_BaseMap);
            SAMPLER(sampler_BaseMap2);
            SAMPLER(sampler_BaseMap3);
            SAMPLER(sampler_BaseMap4);

            CBUFFER_START(UnityPerMaterial)
                float4 _BaseMap_ST;
                float4 _BaseMap2_ST;
                float4 _BaseMap3_ST;
                float4 _BaseMap4_ST;
            CBUFFER_END

            Varyings vert(Attributes IN)
            {
                Varyings OUT;
                OUT.positionHCS = TransformObjectToHClip(IN.positionOS.xyz);
                OUT.uv = IN.uv;
                OUT.color = IN.color; //Attributes 에서 칼라를 받아 Varyings 으로 전달 
                return OUT;
            }

            half4 frag(Varyings IN) : SV_Target
            {
                half4 color = SAMPLE_TEXTURE2D(_BaseMap, sampler_BaseMap, TRANSFORM_TEX(IN.uv, _BaseMap));
                half4 color2 = SAMPLE_TEXTURE2D(_BaseMap2, sampler_BaseMap2, TRANSFORM_TEX(IN.uv, _BaseMap2));
                half4 color3 = SAMPLE_TEXTURE2D(_BaseMap3, sampler_BaseMap3, TRANSFORM_TEX(IN.uv, _BaseMap3));
                half4 color4 = SAMPLE_TEXTURE2D(_BaseMap4, sampler_BaseMap4, TRANSFORM_TEX(IN.uv, _BaseMap4));
                
                // return lerp(color, color2, IN.color.r); //R 마스킹

                half4 finalColor = lerp(color, color2, IN.color.r); //R 마스킹
                finalColor = lerp(finalColor, color3, IN.color.g); //G 마스킹
                finalColor = lerp(finalColor, color4, IN.color.b); //B 마스킹
                return finalColor;
            }
            ENDHLSL
        }
    }
}

VertexColor2.shader
0.00MB

 

 

자 그럼 오늘은 여기까지! 

블로그 주인장에게 커피값을 후원할 수 있습니다! 

 

반응형

댓글