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

(HLSL)아티스트를 위한 URP 셰이더 Shader #6 - 스위즐링

by 대마왕J 2021. 8. 28.

 

원래는 셰이더 그래프랑 코드랑 완전히 합을 맞춰서 동일한 내용을 동일한 타이밍으로 연재하려 했어요. 
우와 이상적이고 멋져 보이잖음. 셰이더 그래프로 뙇 설명하고 코드로 똑같은거 뙇 설명하고 캬 멋진 나의 모습 

...사실 지금도 그러려고 하고 있어요 
하지만 노드 사용법이 들어가니까 꼬이더군요. 노드에서는 감마 설명이 안되면 진도 자체가 안나가는 부분이 있어서. 
게다가 노드는 기본 사용법 설명이 있다 치더라도 코드는 기본 설명을 해버리려면

 

 

... 어디서부터 해야 돼

대충 이정도 수준으로 하면 되나

 아니 사실 할 수는있는데, 그렇게 되면 이놈의 기초에서 헤어나올 수 없게 되기 때문에요, 블로그 쓰는것도 좀 재미가 있어야 하는데 사실 기초는 그다지 재미가 없거든요. 빨리 중급 셰이더 들어가고 싶지 ... 

그러니 기초는 좀 듬성듬성 뛰어 넘겠습니다. 
사실 셰이더 코딩은

이걸 코딩이라 부르기엔 좀 창피하지만

의 수준이라서 어렵지 않은 편입니다. 
그러므로 기본 코딩이랑 변수, 함수 개념 정도만 알고 가시면 되지요. 

자 그걸 기대하시고 계시는 분도 있을지도 모르지만, 여기서는 그래서 그런 수준의 기초는 좀 패스하고 가기로 하겠습니다. 
왜냐면 
https://book.naver.com/bookdb/book_detail.nhn?bid=12518820 

 

유니티 쉐이더 스타트업

『유니티 쉐이더 스타트업』은 프로그래밍으로 완성해야 하는 쉐이더의 구현을 아티스트들의 역량으로도 충분히 발휘할 수 있도록 도움을 주는 책입니다. 저자의 오랜 게임그래픽 실무경험과

book.naver.com

이 책으로 공부하시고 난 분이라고 생각하고 시작한다고 했으니까요!!! <<광고>> 

 

 

그럼 이번에도 노드와 타이밍을 맞추기 위해서, 이번에는 코드로 스위즐링과 출력 실습을 해 보도록 하겠습니다
오늘만 하면 기본은 대충 되는거 아닌가.. 하는 헛된 희망을 가지고 출발합니다. 


스위즐링 swizzling

코드에서 스위즐링은 쉽습니다. 

노드에서 Vector4 는 이렇게 구성되어 있지요? (RGBA라고 해도 됩니다)
또한 이것은 Vector3 , Vector2 같은 것들도 있고, 이를 코드로 하면 

float4(r,g,b,a);
float3(r,g,b);
float2(r,g);

이렇게 되어 있고요. 

float(r) 따위는 없습니다 ... 한 자리는 그냥 숫자를 써요. 
셰이더 코드 내에서는 float , half  등등을 쓸 수 있습니다. 정밀도만 다르고 같은거예요. 
단 URP에서는 real 을 써도 됩니다. 유니티 내부에서 real을 쓰면 상황에 따라 알맞게 전환해 주도록 선언되어 있어요. 전 그래도 걍 float 쓸거지만. 캬캬캬

이 녀석을 자유롭게 왔다갔다 할 수 있습니다. 변수를 이용해서 해도 되고요. 

에... 이녀석을 설명하는 것도 좀 그렇군요. 이제 좀 기초는 빨리
직접 실습해 보면서 해 보도록 하죠 

우선 코드는 여기에서부터 시작합니다. 

Shader "Example/URPUnlitShaderBasic"
{
    Properties
    { }

    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;
            };

            struct Varyings
            {
                float4 positionHCS  : SV_POSITION;
            };

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

            half4 frag() : SV_Target
            {
                half4 customColor = half4(0.5, 0, 0, 1);
                return customColor;
            }
            ENDHLSL
        }
    }
}

다운로드 받고 싶으면 여기에서.

NewUnlitShader.shader
0.00MB

 

자 저게 준비되었으면, 지금은 스위즐링만 해볼거니까 맨 아래에서.. 

half4 frag() : SV_Target
{
	half4 customColor = half4(0.5, 0, 0, 1);
    return customColor;
}

요기만 신경쓰면 됩니다.  픽셀셰이더만 가지고 놀께요. 

저걸 적용시키면 지금은  

미묘하게 우울한 붉은색이 나옵니다. 제 마음처럼. 

출근할 생각에 우울함

여기서 재미있는건, 

half4 customColor = half4(0.5001);

라고 썼으면 customColor 라는 변수 안에 (0.5, 0, 0, 1) 이 들었다는 얘기거든요. 
그러니 이렇게도 쓸 수 있습니다.  변수명 뒤에 . 을 붙여서 rgba (또는 xyzw) 를 쓰면 

customColor.r == 0.5 
customColor.g == 0
customColor.b == 0
customColor.a == 1

인 것과 같습니다 .
(프로그래밍에서 == 이 같다라는 표시는 아시죠? 모르면 위에 소개한 책을.. 구매를... 출판사 사장님.. 보고 계십니까...) 
그리고 또한 

customColor.rg == float2(0.5, 0)
customColor.gb == float2(0, 0)
customColor.ba == float2(0, 1)

인 것도 같지요. float4 의 변수를 가지고 [.] 을 찍고 그 내부에 있는 요소들을 적으면 그대로 새로운 float을 만들 수 있다는 말입니다. 그리고 뒤바뀌는 것도 자유롭지요. 예를들어 
customColor.rgb 가 있다면 이것은 float3(0.5, 0, 0) 을 의미한다는 것은 이제 쉽게 알 수 있을 것입니다. 

그렇지만 자유롭게 요소들을 뒤바꿀 수 있다고 했지요?
그러므로 이것도 가능합니다. 

customColor.gbr == float3(0, 0, 0.5)
customColor.arg == float3(1, 0.5, 0)

오 자유롭게 뒤바꿀 수 있군요. 좋아요 
근데 이게 끝이 아님. 이런것도 돼요 

customColor.rrr == float3(0.5, 0.5, 0.5)
customColor.ggg == float3(0, 0, 0)
customColor.aar == float3(1, 1, 0.5)

와 맘대로죠? 근본도 없네 이색히들

게다가 이렇게 지멋대로 할 수 있다면 
아래와 같이 써도 된다는 말이잖아요. 

half4 frag() : SV_Target
{
  half4 customColor = half4(0.5, 0, 0, 1);
  //아래 두 개는 같다. 물론 실행하면 맨 처음 return이 나오겠지만
  return customColor;
  return customColor.rgba;  
}

그렇다면 실험으로 한 번 해봅시다

rrrr 이라고 쓰면 전부 0.5 일테니까 회색 맞네요. 알파는 뭐 지금 뭘 집어 넣어도 동작 안하는게 정상이고. 

이렇게 하면 r과 g 가 뒤바뀌었으므로 칙칙한 빨강이 칙칙한 녹색이 되는게 당연. 
이렇듯 쩜 하나 찍고 내부 데이터들을 마구 뒤섞을 수 있는것을 스위즐링이라고 합니다. 

 

참고로 이 함수는 half4 (float4)를 리턴하지 않으면 에러가 나므로 아래의 경우는 에러납니다. 노드는 막 연결해도 되지만, 코드에서는 float3 인지 4인지를 정확히 맞춰 주도록 하는게 좋을거예요.  


스위즐링과 변수를 이용한 응용

이런 방식을 변수와 함께 응용할 수도 있어요. 
각 float을 쪼개서 변수를 만들고, 이걸 다시 합치고 할 수 있다는 거죠 

노드로 따지면 split, combine 에서 했던 것들.

자 일단 이게 있다고 하고요

half4 customColor = half4(0.5, 0, 0, 1);

그럼 customColor라는 변수에는 float4이고 숫자가 들어가 있는거지요. = 는 프로그래밍에서 변수에 데이터를 대입시키는 명령이니까. 

그럼 또 이렇게도 쓸 수 있답니다. 

half4 customColor = half4(0.5, 0, 0, 1);
half2 test2Vector = customColor.rg;
half2 test2Vector2 = customColor.ba;

헤에 뭘 했는지 보세요. float4 짜리 customColor를 rg, ba 로 둘로 갈가서  각각 test2Vector 와 test2Vector2 에다가 넣어줬어요. 새로 생긴 변수는 half2 라는 점을 주목해 주세요! 크기를 맞춰 변수를 만드는건 중요하거든

자... 변수를 둘로 갈라서 각각 새로 생성한 변수에 나눠 넣었다.. 재미있는 사용방법인거지요. 

그럼 이번엔 또 이걸 이용해서 합칠 수도 있습니다. 
이렇게 말입니다. 

half4 customColor = half4(0.5, 0, 0, 1);
half2 test2Vector = customColor.rg;
half2 test2Vector2 = customColor.ba;

//굳이 분리한걸 합치는 짓을 한다
half4 finalColor = half4(test2Vector, test2Vector2);

여기서 한 걸 보세요. half4 (    ) 안에다가, half2 변수 두 개를 넣어서 총합 half4로 만들어 놨습니다. 

이렇듯 변수는 조각조각 나눠서 숫자처럼 쓸 수 있으며, 어디든 규격에만 맞다면 끼워 넣을 수 있습니다. 
심지어 숫자와 섞어쓸 수도 있어서, 
half4 finalColor = half4(1, 0, test2Vector2.rg );
이렇게도 막 쓸 수 있다는 겁니다 .

이것이 스위즐링. 그리고 변수와 같이 응용하는 방법이었습니다. 

 

변수를 만들고 스위즐링을 통해 부분 요소 값의 자리를 바꾸어 보고, 변수를 만들어 마구 섞어서 써 보도록 합니다. 

자 그럼 다음 편엔 노드에서처럼 프로퍼티와 연결을!! 

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

 

반응형

댓글