[유니티 쉐이더 스타트업] Part 16 | 알파(Alpha)

jungizz_·2023년 3월 2일
0

Unity Shader StartUp

목록 보기
16/17
post-thumbnail
❗ 알파를 너무 많이 사용하면 문제가 발생할 수 있으며 생각 외로 이상한 결과가 나오는 경우도 있어 다루기 복잡하다. '알파는 투명한 것이다'라고만 하기에는 부족한 점이 많기 때문에 알파를 다루기 위한 기본적인 이론을 알아볼 것이다.

1. Z 버퍼 (Z Buffer)

  • 게임이 시작되면 매 프레임마다 특정한 경로(PipeLine)를 따라 계산되고 화면에 그려진다.
  • 기본적으로 그려지는 순서는 '계산이 끝난 것을 그린다'라는 원칙을 따르기 때문에 어떤 것이 먼저 그려질 지 알 수 없다.
  • 뒤에 있는 오브젝트가 먼저 그려지고 앞에 있는 오브젝트가 그려지는 경우 문제없이 그려진다. 이를 오버드로우(OverDraw)라고 한다.
  • 하지만, 앞에 있는 오브젝트가 먼저 그려지고 뒤에 있는 오브젝트가 그려지는 경우에는 뒤의 오브젝트가 앞의 오브젝트를 덮어버린다.
  • 이것을 방지하기 위해 픽셀 단위의 앞뒤를 판정하는 정보가 있어야 한다.
  • 즉, 앞뒤 판정을 위해서, 각 픽실마다 카메라를 기준으로 가장 가까운 오브젝트의 깊이값이 저장되어 있는 데이터 시트가 필요하다.
  • 깊이값은 각 픽셀마다 1.000···~0.000···의 숫자로 나타내며 이를 흑백으로 표현한 그림이 Z버퍼이다.
http://lunacsunshine.weebly.com/articles/between-the-lines-z-buffering

☑️ Buffering

  • 그래픽 카드는 눈에 보이지 않는 여러 버퍼를 렌더링 한다.
  • G버퍼, Back 버퍼, 스탠실 버퍼··· 등
  • 동영상 시청 시 인터넷이 느리면 버퍼링 이라고 뜨면서 영상을 받는 과정!
  • 쉐이더에서 _CameraDepthTexture를 사용하면 그래픽 카드에서 Z버퍼를 받아올 수 있다.
Shader "Custom/DepthShader"
{
    
    SubShader
    {
        Tags { "RenderType"="Opaque" }

        CGPROGRAM
        #pragma surface surf Lambert noambient noshadow
        #pragma target 3.0

        sampler2D _CameraDepthTexture;

        struct Input
        {
            float4 screenPos;
        };
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            float2 sPos = float2(IN.screenPos.x, IN.screenPos.y) / IN.screenPos.w;
            float4 Depth = tex2D(_CameraDepthTexture, sPos);
            o.Emission = Depth.r;
            o.Alpha = 1;
        }
        ENDCG
    }
    FallBack off
}

  • 만약 두 플랜이 완전히 같은 Z값에 존재한다면 겹쳐진 각 픽셀들이 매 프레임마다 서로 앞으로 나가려고 싸우는 것 처럼 보인다. 이를 Z Fightig이라고 한다.

2. 반투명: 알파 블렌드(Alpha Blend)

  • 만약 붉은 색 캡슐을 반투명으로 그린 후, 뒤에 있는 파란 색 캡슐이 그려지면, 붉은 캡슐이 그려진 영역은 '가려진 것'으로 인식하여 뒤에 있는 푸른 캡슐이 '자신의 일부는 가려졌다'라고 판단하고 그리지 않게 된다.

☑️ 이 문제를 해결하기 위해서는

  1. 불투명(Opaque) 오브젝트는 먼저 그린다.
  2. 반투명(Transparent) 오브젝트는 나중에 그린다.
  • 문제가 해결된 듯 보이지만, 반투명끼리의 문제도 나타난다.
  1. 반투명 오브젝트들끼리는 '멀리 있는 것'부터 가까운 것까지 정렬하여 차례대로 그린다. 이것을 알파 소팅 Alpha Sorting이라고 한다.

☑️ 반투명이 들어갔을 때의 악영향

  • 반투명 오브젝트들은 불투명한 오브젝트가 다 그려질 때 까지 대기
  • 반투명 오브젝트들끼리 거리를 체크하는 연산
  • 만약 앞에 커다란 오브젝트가 있다면 그 뒤의 불투명 오브젝트는 그려지지 않아 절약되지만, 반투명 오브젝트는 뒤에서부터 그리게 되므로 오히려 계산이 더 무거워진다.
  • Deferred Rendering에서는 아예 반투명을 처리할 수 없어서 Forward Rendering으로 반투명을 따로 그려야 한다.

3. 알파 블렌딩 (Alpha Blending)

  • 기본형 Lambert 쉐이더부터 시작해서 알파 블렌딩이 가동되는 쉐이더를 만들어 본다.
Shader "Custom/AlphaBlendShader"
{
    Properties
    {
        _MainTex ("Albedo (RGB)", 2D) = "white" {}
    }
    SubShader
    {
        Tags { "RenderType"="Opaque" }
       
        CGPROGRAM
        #pragma surface surf Lambert

        sampler2D _MainTex;

        struct Input
        {
            float2 uv_MainTex;
        };
        
        void surf (Input IN, inout SurfaceOutput o)
        {
            fixed4 c = tex2D (_MainTex, IN.uv_MainTex);
            o.Albedo = c.rgb;
            o.Alpha = c.a;
        }
        ENDCG
    }
    FallBack "Diffuse"
}

  • 알파 블렌딩 쉐이더로 바꾸고, Quad를 뒤집어도 이미지가 보이도록 cull off도 추가한다.

  • 바닥에 Plane을 설치해보면, 그림자가 Quad 모양으로 생기는데 이를 해결하기 위해서 Fallback "diffuse" 코드를 Transparent 계열의 쉐이더 이름으로 수정한다.

FallBack "Legacy Shader/Transparent/VertexLit"

☑️Fallback

  • '이 쉐이더를 표현하지 못할 하드웨어에서는 Fallback에 있는 쉐이더를 사용해라'라는 의미를 가진 '예비용' 쉐이더
  • 그림자 Pass와도 연결되어 있다는 것을 위 상황을 통해 짐작할 수 있다.
  • 그림자가 전부 사라지는데, Tranparent 계열 쉐이더의 기본 옵션은 그림자가 나오지 않는 것이 맞다.
  • 사실 Transparent 계열 쉐이더는 풀에도 거의 사용하지 않으며 풀 같은 자연물은 주로 AlphaTest(Cutout)쉐이더를 사용한다.


4. 알파 소팅(Alpha Sorting)의 문제

  • 위의 쉐이더로 알파 블렌딩을 가동시켰지만 아직 알파의 문제는 해결되지 않았다.

  • 아래와 같이 텍스쳐를 빼고 로봇에 적용시켜보면, cull off 때문에 앞뒷면이 전부 렌더링되는 상태에서 그림 그려지는 순서가 엉망이 된 모습이다.

  • cull off를 빼더라도 뒤에 가려져야할 오브젝트가 앞으로 그려지는 현상이 나타난다.

  • 분명 반투명 오브젝트들은 뒤에서부터 앞으로 그리는데 무슨 현상일까?

  • 확인을 위해 동일한 쉐이더를 적용한 쿼드 이미지 3장을 나란히 놓았다. 맨 뒤에 있는 것이 먼저 그려져 문제 없어 나타난다.

  • 하지만 뒤에 있는 쿼드를 옆으로 이동시키면 맨 앞으로 튀어나와 그려진다

  • 알파 블렌딩은 카메라의 위치에서 각 오브젝트는 피봇점의 거리를 구해서 카메라에서 먼 것부터 그린다. 아래 두 이미지 중 왼쪽은 1이 가장 짧고, 3이 가장 길어서 3, 2, 1 순서로 그린다. 하지만 오른쪽 이미지는 2가 가장 짧기 때문에 3, 1, 2 순서로 그려지는 것이다.

  • 위와 같은 문제는 오브젝트의 크기게 극명하게 다른 경우에도 자주 나타난다. (피봇점을 중심으로 하기 때문)

  • 알파 소팅을 써봤자 완벽하게 앞 뒤를 제대로 판정할 수 없으니, 알파를 썼을 때 문제는 늘 발생한다.

  • 그래서 이러한 상황을 피하고자 알파 블렌딩을 이용하는 Transparent 계열에 쉐이더들에게 z버퍼에 자신의 위치값을 입력하지 않는 기능이 추가된다. zwrite를 off한다.

  • 알파 블렌딩 처리가 되어 있는 쿼드끼리 앞뒤가 잘못 연산되더라도, z버퍼에 아예 값을 쓰질 않으니 사각형 쿼드 모양으로 잘리지 않고 오브젝트의 앞뒤만 바뀌게 된다.


✅ 알파 블렌딩 총정리

  • 불투명Opaque 오브젝트는 먼저 그린다.
  • 반투명Transparent 오브젝트는 나중에 그린다.
  • 반투명 오브젝트들끼리는 뒤에서부터 그린다.
  • 알파 소팅을 써봤자 완벽하게 앞뒤를 판정할 수 없으니, 알파를 썼을 때 문제는 늘 일어난다.
  • 알파 블렌딩Transparent을 사용하였을 때에는 zwrite를 하지 않는다.
❗ 이 문제들을 해결하기 위해 각종 방법이 연구되고 있다.

5. 알파 테스팅과 컷아웃

  • 위에서 언급했듯, 나무나 풀 등을 그려야 할 때는 알파테스킹(컷아웃)을 사용한다. 알파 블렌딩은 그림자도 나오지 않고 앞뒤 판정이 명확하지 않아 문제가 발생하기 때문이다.

☑️ 알파 테스팅 가동

  • _Color - Color 구문의 인자를 사용하여 그림자를 그리기 때문에 필요하다.
  • _Cutoff - 각 픽셀의 '잘라버릴' 정도를 구한다.
    0.5로 하면 회색을 기준으로 더 어두운 알파는 찍히지 않고, 더 밝은 알파는 알파가 없는 것처럼 그려진다.
  • "Queue"="AlphaTest" - 일반 오브젝트Geometry와 반투명Transparent의 사이를 의미한다.
    즉, [일반 불투명 오브젝트 ➡️ 알파 테스팅 ➡️ 알파 블렌딩] 순서대로 그려진다.
❗ 그림자도 나오고, 알파 소팅의 문제도 일어나지 않는다. PC버전에서는 알파블렌딩보다 연산이 더 빠르다. 알파가 많이 필요할 때 많이 사용할 수밖에 없는 기능이다.
profile
( •̀ .̫ •́ )✧

0개의 댓글

관련 채용 정보