본문 바로가기
유니티 엔진

Depth Texture를 언제 그릴까? Depth Prepass / After Opaque

by 대마왕J 2023. 7. 20.

쉽게 설명해주시려고 노력해주신 고정석님께 감사 인사를 드립니다. 

 

Depth(깊이) 

Depth란 .. Z 버퍼라고도 불리던 그것입니다. 게임하면서 깊이값을 알아야 할 때가 있지요? 
그게 depth입니다. 이게 있어야 반투명들이 나올때 어디에 가려질지 파악할 수 있다던가, 기타 여러 효과들을 위해서라도  꼭 있어야 하는 데이터예요. 
이게 없으면 반투명들이 그려질때 Z 가 없으니까 전부 다 그냥 화면 앞에 와다다다다 그려질걸요. 
혹은 굴절이나 DOF와 같은 포스트 프로세스, 기타 소프트 파티클 등 깊이를 가지고 조작해야 하는 모든 기법들에 필요한 것이 이 깊이값입니다. 

이건 자동으로 생기는게 물론 아니죠. 저것도 날 잡아 (?) 그려야 합니다. 
그래서 '언제 그릴 것이냐' 라는 말이 생기게 되는 것입니다. 

마 언제 그릴건데!!!

일반적으로 Depth를 그리는 순서 

일반적으로 그릴때에는 그냥 그립니다. 네, 소팅이나 기타 명령이 있지 않으면... 그냥 그립니다. Early - Z 등을 위해 앞에서부터 그리는 경우가 있지만 그거 뭐 .. 네 되긴 하지만 그게 그렇다고 막 확실하게 정확하지도 않고.. 

일단 뭐 니 맘대로


어쨌거나 불투명 오브젝트(Opaque)들을 그리는 것은 일반적으로 거의 처음에 일어나는 일이죠 (Shadow 그리는 순서 같은것도 있지만 각설하고) 

아래 그림에서 보면 Shadow 다음에 Clear하고,  Opaque Pass를 그리는 것을 알 수 있습니다 . 그후에 스카이박스를 그리고 Transparent를 그리지요.  

즉 이게 흔히 생각할 수 있는 일반적 방법입니다. 
Shadow - Opaque - Skybox - Transparent 
네 맞아요 이게 일반적인 그리는 순서죠. 사실 하드웨어 버퍼에 댑스는 여전히 그리고 있습니다. Opaque그릴때에 말이죠. 
그게 있어야 Transparent 때 사용하지... 

 

 

Depth Texture Mode

하드웨어 버퍼에 깊이값을 그리는건 뭐 저 타이밍에 알아서 그립니다만, 문제는 우리가 Depth Texture를 '텍스쳐 형태로'쓸 때가 있단 말입니다. 하드웨어 버퍼에 있는 Depth값 말고, '텍스쳐 파일로 되어서 따로 빼서 쓸 수 있는 Depth Texture' 를요.
 
어디에서 쓰냐면 주로 Shader 에서 씁니다. Post Process에서도 쓰고요 (PP도 사실 Shader니까) 
더 자세히 예를 들자면 물 표현 같은데에서도 씁니다. 깊이따라 효과가 생기거나, 물과 맞닿는 부분에 포말이 생기거나 부드럽게 빠질때 쓴다던가...
혹은 Soft Particle 이나 Decal 같은데에서도 씁니다. 또는 DOF 같은 PP에서도 필수로 사용되지요. 

이런 것들이 다 Depth Texture를 셰이더로 가져와서 사용하는 겁니다.

즉 요즘에는 이걸 안쓰는 경우가 거의 없을 정도입니다. 뭔가 효과를 Shader 단계에서 만들기 위해서는 . 이 '텍스쳐화된 Depth ' 가 존재해야 해요. 
Deferred Rendering 상태에는 거의 이 버퍼가 마련되어 있어 공짜에 가깝지만, Forward Rendering에서는 '굳이' 이 Depth Texture를 따로 준비해 줘야 하는 문제가 있습니다. 

뭐 앞에가 0이고 뒤가 1인 경우와 뒤집어진 경우 등등이 있지만 그건 또 얘기가 길어지고...

뭐 어쨌건 그래서 결국 이걸 텍스쳐로 만들려면 하드웨어 버퍼에 있던걸 텍스쳐에 옮겨야 쓸 수 있는데요, 이 타이밍을 결정하는게 Depth Texture Mode 입니다. Depth Texture를 언제 만들거냐!! 라는 거죠 

 

After Opaques - 불투명 그린 후에 만드는 모드 

이름 그대로, '불투명(Opaque) 들을 그린 다음에 Depth Texture를 만드는 모드입니다. 

이걸 선택하고, Depth Texture 모드를 켜주고
Post Process나 기타 Depth Texture 모드가 필요한 순간이 생기면 특정 패스가 갑자기 생겨나기 시작합니다. 

재미있게도 Depth Texture 를 사용한다고 렌더 에셋에서 켜줘도, DoF 같이 진짜로 Depth Texture를 사용하는 상황이 만들어지지 않으면 Depth Texture를 만들지 않습니다. 똑똑해졌네요. 
이런 것들은 버전이나 렌더러 따라 다르게 동작할 수 있으니 모든 경우에서 꼭 이렇게 동작하리라고는 보장할 수 없습니다. 

오우 Copy Depth라는 Pass가 뿅하고 생겼네요. _CameraDepthTexture 라는 렌더 타겟에 그리고 있는게 보입니다. 그리고 Pass 가 1회인게 보이죠? 즉 앞에 Opaque를 다 그린 후 (여기서는 Transparent 까지도 다 그린후 네요) 나중에 버퍼에 있는 Depth를 카피하는 겁니다. 그러니 1회만 작업하면 되는거지요.

이렇게 해서 Depth texture 를 만들었습니다. 뭐 심플해 보이지요? 하드웨어 버퍼에 있던 Depth를 Texture로 카피한다! 

 

 

Depth Prepass  - 깊이를 먼저 만드는 모드

Depth prepass 가 되면 일단 Depth만 먼저 그리는 방식입니다. 불투명한 오브젝트의 깊이값만 먼저 그리는 거죠 
아래 프레임 디버거를 보시면, Shadow Pass  이후에 "Depth Prepass" 가 가동되고, 이후에 Draw Opaque가 가동되는것을 볼 수 있습니다. 
즉 Shadow - Depth Prepass - Opaque - Transparent 입니다. 
와 Depth를 그리는 Pass가 하나 생겼네요!! 

그리고 자세히 더 보면, 
Depth Prepass를 그리면서 하드웨어 Depth와 _CameraDepthTexture에 동시에 그리고 있습니다. 

이렇게 해서 먼저 그린 Depth를 Clear 하지 않은 채, 이후에 Depth에 필요한 순간에 계속 쓴다는 작전(?) 인 것이지요. 

 

Depth Prepass 의 이점?

두 방식은 결과적으로는 별 차이가 없습니다. 어차피 하드웨어 Depth를 이용해서 물체의 앞뒤를 판정할거고, 
Depth Texture를 이용해서 나중에 Shader에서 사용할 깊이 텍스쳐를 만들어 내니까요. 

그럼.. 결국 둘의 차이는 속도나 메모리 혹은 기타 다른 이유일 것이라는 것을 짐작할 수 있습니다. 

일단 가볍게 생각해 보자면, Depth Prepass는 '굳이 하나의 Pass를 더 추가' 합니다. 
상식적으로 이게 더 무거워 보이는 군요? 호오.. .안좋을거 같다. 왠지 패스가 하나 더 추가되니까 안좋을것 같습니다.
그런데, 꼭 그렇지도 않다는군요. 요새는 Depth Prepass를 쓰는 경우가 더 많거나 권장되는 분위기랍니다. 

그럼 그게 뭔 장점이 있어서 그런건지.. 알아보았습니다. 

1. Depth를 먼저 그리는게 그다지 무겁지 않다 

일단 생각해 보세요. 일단 Depth를 그릴때 UV나 Normal등이 전혀 필요가 없이 Position만으로 그리기 때문에, 그리는 부하가 심각한게 아닙니다. 생각보다 굉장히 싸요 . 거의 Vertex Shader 만으로 그릴 수 있습니다.
그리고 깊이값을 미리 그려놨다는 것은, 이후에 Opaque를 그릴 때 '어라 뒤에 숨어있으니까 얘는 안그려도 되겠네' 라는 부분을 더 확실히 감지할 수 있다는 뜻이 됩니다. 
그리고 이것은 오버드로우가 많을수록, 그리고 셰이더가 비쌀 수록 더 유리해지게 됩니다. 
흐음.. 그렇군요. 하지만 아직 이걸로는 그냥 변명인 것 같습니다. 의심 의심 

2. Depth를 미리 그려두면 Read Only로 둘 수 있다  

게다가 하드웨어 Depth를 미리 그려두면 Read Only 상태로 둘 수 있습니다. 
After Opaque 때는 Color를 그리면서 Depth를 동시에 그려야 하기 때문에, Depth가 계속 Read/Write 상태여야 하죠. 
그렇지만 Depth 를 Prepass로 그려두면 이미 Depth그리는 것은 다 끝났기 때문에, Clear 하지 않고 그냥 Read Only상태로 놓아두면 됩니다. 이것은 캐쉬 히트(Cache hits)율을 올려주게 되어서 유리한 점을 가지게 됩니다. 

3. 구형 안드로이드 기기에서는 After Opaque가 더 느리거나 간혹 기기에 따라 Copy Depth가 실패할 수도 있다. 

이번에는 After Opaque 를 사용할 때에의 단점 얘기입니다 .
구형 안드로이드 기기에서는 캐시가 작아서 Read / White 상태의 Depth를 다 캐시에 넣고 그리지 못하는 경우가 있을 수도 있다고 합니다. 당연히 그러면 램억세스가 되면서 겁나 느려짐. (물론 요즘 기기는 다 캐시에 넣고 그리지만 말이죠.)  
혹은 특정한 칩에 따라서 하드웨어 Depth를 Texture Depth로 Copy를 할때 Precision 이 달라져야 하는데, 이 때 실패하는 경우가 생기기도 한다고 합니다 ... 두둥. 와 뭐야 엉망진창이잖아.. 

그래서 안드로이드 옛날 기기에서는 Opaque를 그릴때  차라리 Depth Prepass가 더 유리할 확률이 높다고 합니다.  

그럼 캐쉬의 크기가 얼마 이상이어야 캐시에 다 넣고 그릴 수 있느냐? 그건 렌더해상도와 MSAA 여부와 캐쉬 크기의 복잡다난한 관계에 따라 이루어지기 때문에 예측하기 더더욱 힘들다고 합니다. 아아 ... 그거 무섭죠. 예측하기 힘든거.. 
그래서 생긴게 Depth Priming 모드라고 합니다. 
이렇게 복잡다난한 Depth Texture 그리는 문제때문에, 상황을 여러 각도로 인지해서 어떤게 유리하냐를 분석해서 After Opque로 할지, Depth Prepass로 할지 자동으로 분석해 정해주도록 만든거라는거.. 과연 이래서 그랬군요. 
(하지만 유니티의 자동을 믿는다고요...? ㅎ)
여기서도 역시 MSAA 모드와 결합되면 성능 안좋아진다고 나와 있네요. 이게 위에서 말한 캐쉬 크기 문제의 경고인 것 같습니다. 

4. iOS 에서는 Depth Prepass를 하는게 더 효과가 없다 ?

iOS 쪽 문에서에서는 Depth Prepass를 하는게 그다지 효과가 없다고 합니다. 왜냐면 하드웨어 차원에서 이미 Depth Prepass 같은 행동을 해 주고 있기 때문이라고 하네요. 

뭐 .. 그래요 그럴 수도 있죠. iOS 에서는 좀 비효율적일 수 있습니다만
iOS 는 안 그래도 이미 빠릅니다. 아니 속도는 이미 안드로이드의 문제라고... 부자집은 저리 빠져 있어... 
그래서 사실 이 정도까지 생각하고 움직일 필요는 없다고 볼 수 있겠죠 




참고 아티클 

https://scahp.tistory.com/63

interplayoflight.wordpress.com/2020/12/21/to-z-prepass-or-not-to-z-prepass/amp/?__twitter_impression=true

 

[번역] To z-prepass or not to z-prepass – Interplay of Light

개인 공부용으로 번역한 거라 잘못 번역된 내용이 있을 수 있습니다. 또한 원작자의 동의 없이 올려서 언제든 글이 내려갈 수 있습니다. 출처 : interplayoflight.wordpress.com/2020/12/21/to-z-prepass-or-not-to-z

scahp.tistory.com

 

 
반응형

댓글