Transparent 한 물체, 즉 투명 물체를 렌더링하는 것은 일반적인 렌더 파이프라인과는 조금 다르다.
보통 transparency를 표현하기 위해서는 alpha blend 를 사용하게 되며, alpha blend 의 기본 원리는 현재 draw attachment 의 color 와 fragment 의 color 를 blend function 을 이용하여 더하는 것이다.
만약 일반적인 렌더링과 같이 그린다고 가정하고, FBO clear -> draw transparent objects -> draw opaque objects 순으로 렌더링을 진행했다면, transparent obejcts 는 clear color 와 blending 될 것이며, 렌더링 결과물은 투명한 물체 뒤에 불투명한 물체가 그려진 것이 아닌, 투명한 물체뒤에 뻥 뚫린 clear color만 접하게 될 것이다.
따라서 transparent 렌더링은 일반적으로 opaque objects만 모아 모두 렌더링 한 후, transparent obejcts 만 그리는 식으로 진행되며, Unity나 UE4 등의 게임 엔진도 base pass 후에 translucency pass 를 진행하게 된다.
이러한 object space에서의 ordering 은 일시적인 해답이 될 수 있지만 모든 경우에서 해답은 될 수 없다. 만약 translucent object 가 서로 겹쳐있다면? 또는 translucent object가 복잡한 모양이라면? 이런 상황에서는 Primitive 의 가장 기본이 되는 triangle 이 겹쳐져 있는 경우가 발생할 수 있고, 위와 같은 방법으로는 정렬할 수 없다. 이러한 경우에는 나아가 fragment 단위의 ordering이 되어야 할 것이다.
설명한 것과 같이 Object 를 모두 sort 할 수는 없으며 Object sorting 후에도 발생할 수 있는 문제점도 존재한다. 이를 해결하기 위해 정렬하지 않고도 translucency object를 그릴 수 있는 방법들(Order Independent Transparent, OIT)이 몇가지 소개되어 있다.
Interactive Order-Independent Transparency(api.semanticscholar.org/CorpusID:5813703) 는 가장 기초적인 방법으로, depth peeling을 이용한 OIT 방법을 소개한다.
간략하게 하자면 depth buffer를 이용하여 각 물체를 레이어 상태로 벗겨내고 (peeling), 결과물들을 blending 시켜서 투명한 렌더링 결과물을 얻는 것이다.
일반적인 depth test는 view frustum에서 가장 가까운 fragments만 보여줄 수 있다. 하지만 실질적으로 late-z 가 일어나기 전에는 두번 째, 세번 째 가까운 fragment 들이 존재할 것이다. 즉 일반적인 depth test로는 이러한 순차적인 fragment 를 보여줄 수는 없다.
Depth peeling은 이를 해결하기 위한 기본적인 방법이며, n 번 반복하여 scene을 그린 뒤에는 n개의 순차적인 layer를 얻을 수 있다. Depth peeling은 multi-pass algorithm으로 이루어져있으며, 첫번 째 pass에서는 depth map과 함께 scene color를 그리고, 두번 째 pass는 첫번 째 pass에 그린 depth map을 이용하여 peeling된 depth를 생성하는 것이다.
for (i=0; i<num_passes; i++)
{
clear color buffer
A = i % 2
B = (i+1) % 2
depth unit 0:
if(i == 0)
disable depth test
else
enable depth test
bind buffer A
disable depth writes
set depth func to GREATER
depth unit 1:
bind buffer B
clear depth buffer
enalbe depth writes
enalbe depth test
set depth func to LESS
render scene
save color buffer RGBA as layer i
}
위의 pseudo code는 framebuffer가 attachment 로 multi detph buffer가 가능하다는 가정으로 작성되어 있다.
0 pass에서는 0번째 depth attachment (depth unit 0) 에 depth test가 disable 되어있고 write도 하지 않으므로 아무 작용을 하지 않는다. Depth unit 1의 경우는 clear, enable depth write and test인 상황이며 이후 scene 을 render하게 되므로 layer 0은 온전한 scene 이 그려지며 depth buffer B (depth buffer[1])에는 nearest fragment 들의 정보가 있을 것이다.
1 pass에서는 depth unit 0에 depth buffer[1]이 binding되고 clear되지 않고, depth test가 enable되고 depth test funciton 이 GREATER로 설정되어있다. Depth unit 1 은 depth buffer[0]이 binding 되며 clear 및 depth write, test 되고 test function은 LESS가 된다. 이 상태에서 scene을 그리게 되면, depth unit 0에 의해 가장 nearest fragment는 그려지지 않게 될 것이며 (depth unit 0에는 nearest fragment 의 depth 가 저장되어 있고 test function 은 GREATER임), depth unit 1에 의해 가려진 fragment 들은 그려지지 않게 되어 depth peel 된 이미지가 그려질 것이다.
위 과정이 원하는 pass마다 반복된다면 각 layer 별로 depth peeling 된 fragment space 에서 ordering 된 렌더링 결과를 볼 수 있다. 이 layer를 back to front로 blending 시킨다면 최종적으로 translucent object의 렌더링 결과를 얻을 수 있을 것이다.
FBO에 2개의 depth buffer를 달 수 없는 경우에는 내부적으로 pass를 한번 더 쪼개어 해결할 수 있을 것이다. 예로
bind buffer[0]
clear depth buffer
enalbe depth writes
enalbe depth test
set depth func to LESS
render scene
save color buffer RGBA as layer 0
for (i=1; i<num_passes; i++)
{
clear color buffer
A = buffer[i % 2]
B = buffer[(i+1) % 2]
bind buffer A
clear depth buffer
enalbe depth writes
enalbe depth test
set depth func to LESS
copy B to texture T
set T as sampler in shader
render scene
if depth of current fragment < texture(T)
discard
save color buffer RGBA as layer i
}
위처럼 이전 pass의 depth 결과를 shader에서 texture sampling 한 후, 해당 값보다 작은 depth를 가진 fragments를 discard해준다면, 이전 pass의 nearest layer 보다 뒤에 위치한 fragments들을 렌더링할 수 있다.
이러한 depth peeling 기법의 단점은 N 개의 layer 를 만들고 blend 하는 과정에서 N번의 geometry를 렌더링해야 한다는 것에 있다. Geometry의 complexity 및 draw call 이 많다면 그 만큼 부하는 늘어날 것이다.
이러한 단점을 극복하기 위하여 Dual depth peeling 기법이 사용 될 수 있으며, 그 외에 Weighted blended OIT, per-pixel linked list 를 이용한 OIT 등이 쓰일 수 있을 것이다.
'잡다' 카테고리의 다른 글
Bitonic sort (0) | 2020.12.22 |
---|---|
OIT : Weighted, Blended OIT (0) | 2020.12.09 |
Android Vulkan layer 적용 (0) | 2020.11.18 |
Vulkan Layer (0) | 2020.11.14 |
Ogg vorbis (1) | 2015.09.04 |