개발/유니티

흑백 셰이더를 만들어보자(feat. dot 함수에 관하여)

jeonjoy 2023. 5. 31. 09:31

유니티 셰이더 스타트업 책을 읽으면서 흑백 셰이더를 만들어보았다.

책에서는 r.g.b의 각 값을 더한 후 해당 값만큼 나눈 값을 계산하면 흑백이 나온다고 적혀있었다.

예를들면

 o.albedo = (c.r + c.g + c.b)/3;

이렇게 써주는데, 이는

-float3( (c.r + c.g + c.b)/3,  (c.r + c.g + c.b)/3,  (c.r + c.g + c.b)/3)를 하나로 축약하여 적은것과 같다.

코드는 다음과 같다.

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

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

CGPROGRAM

#pragma surface surf Standard

sampler2D _MainTex;

struct Input
{
float2 uv_MainTex;
};




UNITY_INSTANCING_BUFFER_END(Props)

void surf (Input IN, inout SurfaceOutputStandard o)
{
fixed4 c = tex2D (_MainTex, IN.uv_MainTex) ;
o.Albedo = (c.r + c.g + c.b)/3; //수정한 코드//
o.Alpha = c.a;
}
ENDCG
}
FallBack "Diffuse"
}

결과물:

이 등식은 그런데 Grayscale = o.2989 * R + 0.5870 * B + 0.1140 * B로도 표현할 수 있다고 한다.

RGB to YIQ 변환 기준 및 그 외 등식도 존재하는데, 이 페이지에서 관련 등식을 구할 수 있었다.

이때 YIQ란? 컬러시스템에서 사용되는 색 공간으로, 색을 인코딩하는데 사용된다.

Y= 밝기 I =색상 차이(Red vs Green), Q=색상차이(Blue vs Yellow)

RGB to YIQ의 수식은 다음과 같다.

Y = 0.229R + 0.587G + 0.114B

I  = 0.595716R - 0.274453G - 0.321263B

Q = 0.221456R - 0.522591G + 0.311135B

자세히 이해하려면 더 많은 정보가 필요하지만, 지금은 여기까지만 하고 넘어가도록 하자.

근데 지금처럼 아무것도 모르는 초짜인 나는 사실 이 코드를 봐서 입력을 해봐도 셰이더가 깨지기만 하고 결과물이 나오지 않았다.

아직 뭐가 잘못된건지도 구분이 안되는 상태 ;;

그리고 찾아보다보니 이 단순한 RGB/3 등식은 결과물이 가벼운데 연산은 무거운, 최적화에 좋지 않은 방식임을 알았다.

그래서 다른 등식은 어떻게 쓰는지 찾아보다 보니 몇가지 공통점을 발견했다. 바로 dot 함수를 쓰고 있다는 것

texcol.rgb = dot(texcol.rgb, float3(0.3, 0.59, 0.11));

도대체 dot 이 뭐지? 하고 ChatGPT에게 물어보니 이렇게 답이 돌아왔다

수학을 잘 모르는 나는 사실 각 백터의 내적에서 출력이 왜 32가 나오는지 이해하기까지 진짜 한참이 걸렸다.

닷은 Dot Product라고도 불리는데, 벡터의 내적을 구하는 함수이다.

벡터는 내적과 외적으로도 구분하는데 보통 기호를 쓸 때 외적의 경우 A X B 에서처럼 X 기호를 사용하고,

내적을 구할 때에는 • 기호를 써서 dot product라고 부른다.

이 내적은 각 벡터 a와 b를 곱한 값으로 chat GPT가 예시로 든 방식의 답이 왜 32인지도 구할 수 있다.

(a.x * b.x) + (a.y * b.y) + (a.z * b.z) = (1x4) + (2x5) + (3x6) = 4 + 10 + 18 = 32

이 Dot 함수가 Grayscale을 구현하는 셰이더에 쓰인 이유가 무엇일까?화면 기록 2023-03-17 오후 3.11.45

RGB의 수치를 0-1로 환산했을 때 해당 값과 Grayscale 공식 rgb 수치를 곱하여 텍스쳐가 표현해야할 컬러값의 좌표(내적 스칼라 값)을 구할 수 있기 때문이다.

여기서 Lerp 함수를 응용하여 선형 보간하여 좀 더 부드러운 형태의 Grayscale 흑백 셰이더를 제작할 수도 있다.

아래는 해당 코드 예문이다.

{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
        _Color ("Tint", Color) = (1,1,1,1)

        [Header(Grayscale)]
        _EffectAmount ("Gray Effect Amount", Range(0,1)) = 0



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


        CGPROGRAM

        #pragma surface surf Standard


        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;

        };


            float _EffectAmount;

        UNITY_INSTANCING_BUFFER_END(Props)

        void surf (Input IN, inout SurfaceOutputStandard o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex) ;
            //o.Albedo = (c.r + c.g + c.b)/3;//
            o.Albedo = lerp(c.rgb, dot(fixed4(0.2989,0.5870,0.1140,1), c.rgb), _EffectAmount);
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

 

결과물:

 

 

 

반응형