티스토리 뷰
이 포스팅은 '대마왕의 유니티 URP 셰이더 그래프 스타트업'을 학습하고 개인 학습용으로 정리한 내용입니다.
19. 알파와 뎁스 버퍼 (Alpha & Depth Buffer)
1. Z버퍼(깊이 버퍼, Depth 버퍼)와 불투명
게임을 시작할 때, 한 프레임마다 배경이나 캐릭터, 이펙트, UI들이 모드 그려지는 과정을 거치게 된다. 특정한 경로를 따라서 모든 것이 재계산되어 화면에 그려지면서 한 프레임의 화면이 완성된다.
기본적으로는 '계산이 끝난 녀석을 그린다' 라는 원칙으로 그려지고 있으니 어떤 것이 먼저 그려질지 알 수가 없다.
순서를 정하고자 하면 '앞에 그려진 녀석이 누구인가'를 알아야 한다.
앞뒤 판정을 위해서 각 픽셀마다 카메라를 기준으로 가장 가까운 오브젝트의 깊이 값이 저장되어 있는 데이터 시트가 필요한 격이다.
이 깊이값을 각 픽셀별로 써 놓은 그림을 바로 Z버퍼 또는 깊이버퍼라고 한다.
그래픽 카드는 우리 눈에 보이지 않는 여러 버퍼를 렌더링하고 있다. Depth버퍼, Back버퍼, Stencil버퍼 등 이렇게 눈에 보이지는 않지만 그림으로 그려서 메모리에 가지고 있는 것을 버퍼라고 한다.
실제로 유니티에서 깊이값을 확인해 보자.
적당히 오브젝트가 나열된 씬에서 Window > Analysis > Rendering Debugger를 실행해보고
Rendering Debug 메뉴에서 Map Overlay를 Depth로 바꿔 준다. Map Size로 화면에 보이는 Depth창의 크기를 바꿀 수 있다.

확인해보면, 가까운 쪽은 1이고 먼 쪽은 0이라는 것을 알 수 있다.
이런 정보를 가진 버퍼가 매 프레임 그려지고 있다.

이렇게 버퍼가 늘 그려지고 있으므로, 뒤의 오브젝트가 나중에 그려지도라도, 앞의 오브젝트가 미리 써 놓은 Z버퍼 값을 참조해서 그 부분을 그리지 않으면, 앞뒤 판정은 문제가 없게 된다.
각 픽셀마다 내 앞에 누가 먼저 그려져 있는지 알 수 있으니까!

하지만 여기서 생각해야 할 것이 있다.
이 모든 경우는 알파가 없는, 불투명 오브젝트에 한정된 이야기라는 것.
불투명 오브젝트의 앞뒤판정은 비교적 명확하기 때문에 Shader Graph를 만들면 기본적으로 알파가 활성화되어 있지 않는 불투명 상태에서 시작하는 것이다. 명확하고, 그릴 때 순서를 생각하지 않아도 되나 스트레스가 없다. 그리고 사실 게임의 대부분 오브젝트도 Opaque이다.
완전히 같은 Z값에 존재하는 경우도 있다.
이런 경우 각 픽셀들이 아래처럼 매 프레임마다 '서로 앞으로 나가려고' 싸우게 된다.

2. 반투명: 알파 블렌딩
반투명에서의 소팅 문제는 다음과 같은 방법을 사용한다.
- 불투명 오브젝트는 먼저 그린다.
- 반투명 오브젝트는 나중에 그린다.
- 반투명 오브젝트들끼리는 '멀리 있는 것'부터 가까운 것까지 차례대로 그린다 (뒤에서부터 그린다)
또한 매 프레임 계산되어야 하는 연살을 생각해보면
- 불투명한 오브젝트들은 그저 계산 되는대로 그려주면 아무 문제가 없지만, 반투명 오브젝트들은 일단 불투명한 오브젝트가 다 그려질 때까지 굳이 대기하고 있어야 한다. 즉, 셰이더가 Transparent인 녀석들은 Opaque가 다 그려질 때까지 대기하고 있어야 한다는 말이다.
- 그리고 이번엔 반투명 오브젝트들끼리 그려질 때, 각각 카메라와의 거리를 체크하는 연산을 추가해야 한다.
- 불투명한 오브젝트는 앞에 커다란 벽이 먼저 그려지면 그 뒤의 오브젝트의 Z테스트를 통과하면서 그려주지 않아도 되므로 렌더링을 절약할 방법이 있는데, 반투명한 오브젝트는 뒤에서부터 그리므로 앞에 커다란 반투명이 있으면 오히려 계산이 더 무거워지는 결과를 초래하게 되어서 전혀 절약할 수 없게 된다.
- 디퍼드 렌더링에서는 아예 반투명을 처리할 수 없어서, 포워드 렌더링으로 반투명을 따로 그려줘야한 한다. 이렇게 반투명이 들어가게 되면 퍼포먼스에 악영향을 끼칠 수 밖에 없는 여러가지 문제들이 많이 발생된다.
알파 블랜딩을 만들어보자
예제를 통해 알파 블랜딩을 만들어보자.
Unlit 셰이더에서 그래프 설정의 SurfaceType을 Transparent로 바꾸고 텍스쳐의 Color와 알파 채널을 연결해준다.
Depth Write는 Force Enabled 로 설정하여 강제로 Z버퍼에 쓰게 만들어 준다.


Transparent로 바꿨으니 알파 채널이 활성화되었고, Depth Write를 강제로 ForceEnable 했으니
현재는 Z버퍼에 쓰고 있는 중이다.

깻잎 소환~
알파 소팅의 한계와 Z Write On (Depth Write Enable)
오브젝트를 복사해서 같은 오브젝트 하나를 더 만든 뒤 겹쳐보면
왼쪽과 같이 잘 나오다가도 카메라를 조금 틀거나 위치를 미세하게 조정하면 오른쪽 처럼 뒤의 오브젝트가 뚝 잘려나간다.


왜 그럴까?
이전에 말했던 '알파 블렌딩들은 카메라에서 먼 것부터 그린다'는 원리를 떠올려보자.
무슨 기준으로 카메라에서 먼 오브젝트인 것을 알 수 있을까?
각 오브젝트는 피봇점이 있다.
카메라의 위치에서 그 피봇점의 거리를 구하면, 이 오브젝트가 얼마나 멀리 있는지 알 수 있다.
카메라가 비스듬히 있기 때문에 미묘하게 앞, 뒤가 달라지고 있는 것

그래서 책에서는 이렇게 말한다
알파 소팅을 써 봤자 완벽하게 앞뒤를 제대로 판정할 수 없으니, 알파 블렌딩을 사용하였을 때 앞뒤 문제는 늘 일어날 수 밖에 없다. 간단히 포기하도록 하자.
Z Write Off (Depth Write Disable)
다소 먼 길을 돌아왔지만
'알파 블렌딩 셰이더는 Z 값에 쓰지 않는다' 라는 것이 일반적이라는 중요한 사실을 알게 되었다.
그렇다면 Z Write를 하지 않으면 무슨 일이 일어날까?

다시 설정을 Force Disabled로 바꿔주면


여전히 앞 뒤 문제는 해결되지 않았지만 그래도 짤려나가는 현상은 없어졌다..
지금까지 배운 것
- 불투명 오브젝트는 먼저 그린다.
- 반투명 오브젝트는 나중에 그린다.
- 반투명 오브젝트는 뒤에서부터 그린다 (Alpha Sorting)
- 알파 소팅을 써 봤자 완벽하게 앞뒤를 제대로 판정할 수가 없으니 알파를 썼을 때 문제는 늘 일어날 수 밖에 없다
- 그래서 알파 블렌딩을 사용하였을 때에는 Zwrite를 하지 않는다
- 이 문제들을 해결하기 위해 각종 방법이 연구되고 있다
3. 알파 테스팅/ 클리핑
알파 블렌딩 대신 사용하는 "알파 테스팅(클리핑)"이 있다.
알파 테스팅 가동 방법
- Surface Type은 Opaque로 설정
- Alpha Clipping 체크
- Fragment에 Alpha Clip Threshold 생기는 것 확인, 0.5 정도로 맞춤

Alpha Clip Threshold의 수치로 각 픽셀은 '잘라버릴' 정도를 구하게 된다. 0.5로 해놓으면 회색을 기준으로 더 어두운 색의 알파는 아예 그려지지 않고, 회색보다 밝은 색의 알파는 불투명처럼 그려진다. 그래서 알파 테스트는 '반'투명이 되지 않는다. 완전히 투명이거나 완전히 불투명이다.


알파 블랜딩에 비해 외곽은 거칠게 표현되지만 반투명이 없어서 Z버퍼에 알파가 빠진 모양대로 쓸 수 있기 때문에 알파 소팅의 문제도 일어나지 않는다. PC에서는 연산도 빠르다!
거친 외곽을 해결하는 안티 에일리싱도 있다. 동시에 켜는 방법도 있다.
4. Depth Test

Depth Test: '내가 그려질 때 이미 있는 Z버퍼를 읽어서 그 값으로 어떻게 할 거냐'를 결정하는 것
| Never | 절대로 그리지 않습니다 |
| Less | 내 z값이 이미 그려진 값 보다 더 가까울 때만 그립니다 |
| Equal | 내 Z 값이 이미 그려진 값과 같을 때만 그립니다. 이대로라면 Z Fighting을 만드는 전용기능이 되겠군요 |
| L Equal | Less And Equal. 내 z값이 이미 그려진 값 보다 더 가깝거나 같을 때만 그립니다 (기본값) |
| Greater | 내 Z값이 이미 그려진 값보다 더 멀 때만 그립니다. (아마도 가려져서 보이지 않겠죠) |
| Not Equal | 내 Z 값이 이미 그려진 값과 다를 때만 그릷니다. |
| G Equal | Greater and Equal, 내 Z값이 이미 그려진 값 보다 더 멀거나 같을 때만 그립니다. |
| Always | 이미 그려진 Z값과 상관없이 언제나 그립니다. |
대부분 사용하지 않으니 이해만 하고 넘어가면 된다고 한다...(책에서)
5. Blending Mode
지금까지 배웠던 내용을 정리하자면
알파 블렌딩은 알파 소팅으로 앞뒤를 가리지만 Zwrite를 하지 않기 때문에 앞뒤를 완벽하게 구분하는 것은 사실상 불가능하다. 그래서 나뭇잎이나 머리카락처럼 앞뒤를 반드시 구분해야 하는 것이면 알파 테스팅을 사용한다. 이는 반투명이 불가능한 문제가 있지만, Zwrite를 하기 때문에 앞뒤 문제가 완벽히 해결된다.
인데, 반투명이 필수적인 이펙트도 있을 것이다. 이를 어떻게 해결하는지 알아보도록 하자!
Alpha/ Premultiply
우선 그래프 세팅을 Transparent로 설정해서 셰이더를 만들어준다. 예제 이펙트 텍스쳐도 넣어준다.
그리고 Blending Mode는 Alpha로 설정해준다.

Quad 오브젝트에 적용해보자

Flipbook이라는 노드를 사용해서 애니메이션이 나오도록 UV를 연결해준다. (대박신기해)

씬에서 보면

두 이펙트를 나란히 놓고 보면 역시 카메라의 위치에 따라 앞뒤가 바뀌는 것을 확인할 수 있다.

Additive
Additive는 말 그대로 '더하기' 모드이다.
현재 픽셀 색상에 새로 그리는 색상을 단순히 더하는 방식으로 동작한다.
아래 텍스쳐는 알파 채널이 없어서 그냥 연결하면 뒤에 검은 배경이 나오지만


Blending모드를 Additive로 변경하면

이렇게 0의 값을 가진 검은 배경이 더해져서 투명하게 나온다.


배경+A+B 나 배경+B+A나 결과가 같기 때문에, 앞뒤가 바뀌어도 전혀 문제가 없다.
Multiply
multiply는 이름대로 배경에 곱해주는 방식의 블렌딩이다.
알파 채널 필요없이, 흰 색이 자동으로 투명해지는 방식이다.


이 또한 배경xAxB나 배경xBxA나 같기 때문에 역시 앞 뒷면이 바뀌어도 별 문제가 없다.
GraphSetting
| Precision | Shader 연산 정밀도 선택 • Single (float, 32bit): 높은 정밀도, 성능 부담 • Half (float16, 16bit): 성능 최적화, 낮은 정밀도 |
| Target Settings | Shader가 동작할 렌더 파이프라인 지정 • Built-in: 기본 파이프라인 • Universal (URP): 유니버설 렌더 파이프라인 |
Material 옵션
| Surface Type | • Opaque: 불투명, 알파 무시 • Transparent: 투명, 알파 활용 |
| Blend Mode | • Alpha: 일반적인 투명 블렌딩 • Premultiply: 프리멀티플라이 알파 텍스처용 • Additive: 색 더하기, 검은색은 투명, 빛/불꽃 표현에 적합 • Multiply: 색 곱하기, 배경 어둡게 |
| Allow Material Override | 머티리얼 인스펙터에서 Shader Graph 속성을 재정의할 수 있도록 허용 |
Render&Depth 옵션
| Render Face | • Front: 앞면만 렌더 • Back: 뒷면만 렌더 • Both: 양면 렌더 |
| Depth Write | 픽셀을 깊이 버퍼에 기록할지 여부 • 보통 Transparent는 비활성화 |
| Depth Test | 깊이 버퍼 비교 여부 • Auto: 기본 동작 • Force Enabled: 무조건 깊이 테스트 • Force Disabled: 깊이 테스트 안 함 |
| Alpha Clipping | 알파값이 임계치 이하일 경우 픽셀 제거 • 나뭇잎, 펜스 같은 컷아웃 표현 |
| Cast Shadow | 그림자 생성 여부 • Transparent는 기본적으로 그림자 없음 |
| Custom Editor GUI | 머티리얼 인스펙터를 커스텀 GUI로 대체 가능 |
'개발 > Unity' 카테고리의 다른 글
| [대마왕] 굴절, 점멸, 디졸브 셰이더 (0) | 2025.09.21 |
|---|---|
| [대마왕] 투명해지는 오브젝트 만들기 (0) | 2025.09.14 |
| [대마왕] 큐브맵 리플랙션 (0) | 2025.09.06 |
| [대마왕] NPR 렌더링: 외곽선 만들기 ( 툰 셰이딩 ) (7) | 2025.08.17 |
| [대마왕] 그림자 감쇠 (6) | 2025.08.16 |
- Total
- Today
- Yesterday
- 포톤
- unityAR
- map
- mutex
- 게임개발
- ARface
- StartActivityForResult
- 바이너리세마포
- 유니티슈팅게임
- Unity
- 지크슈
- semaphore
- dependencyResilutionManagement
- SpinLock
- 동기화
- Java
- unorderedmap
- photon
- 세마포
- 스핀락
- Vector
- unorderedset
- 뮤텍스
- ㅗㅂ
- 유니티
- C++
- NotFoundException: String resource ID #0x0
- registerForActivityResult
- 안드로이드스튜디오
- 광유다
| 일 | 월 | 화 | 수 | 목 | 금 | 토 |
|---|---|---|---|---|---|---|
| 1 | 2 | 3 | 4 | |||
| 5 | 6 | 7 | 8 | 9 | 10 | 11 |
| 12 | 13 | 14 | 15 | 16 | 17 | 18 |
| 19 | 20 | 21 | 22 | 23 | 24 | 25 |
| 26 | 27 | 28 | 29 | 30 |