아직 완전하지는 않지만 엔진에 SW Occlusion Culling기능을 넣었다.
물론 HW Occlusion Culling이 가능하다. Compute Shader를 이용한 HW Occlusion Culling과 D3DQuery를 이용한 HW Occlusion Culling기능도 이미 오래 전에 만들어놨다.
그런데 왜 SW Occlusion Culling기능을 따로 만들었는가..하면
렌더링을 위해 KD-Tree를 탐색하며 뷰프러스텀에 들어가는 복셀 오브젝트들을 탐색한다.
뷰프러스텀에 들어가는 오브젝트만 골라내도 사실 그 수가 적지 않다. 트리 순회 단계에서 가려지는 오브젝트를 제거할 수 있으면 효율이 상당히 좋아질텐데.
KD-Tree를순회할때는 카메라에서 가까운 순서대로 탐색이 가능하다. 따라서 카메라에 가까운 노드부터 탐색하며 렌더링 되는게 확실한 복셀 오브젝트들을 Occluder로 z-buffer에 그려놓으면 이후에 수집하는 복셀 오브젝트와, 아예 노드를 Occludee로 테스트해서 통째로 culling할 수 있다.
그런데 이걸 HW Occlusion Culling으로 구현하려면 문제가 있다.
GPU는 물론 아주 빠르게 HW Occlusion Culling을 처리할 수 있다. 하지만 그래픽 API를 사용해서 오브젝트들을 GPU자원에 맵핑하고 그려서 그 결과를 가져오려면 적지 않은 오버헤드가 있다.
일단 API에 기하 데이터를 전송하고 Draw Call을 할때까지 걸리는 시간이 적지 않다.
그리고 HW Occlusion test를 끝내고 그 결과를 GPU에서 다시 가져오는데 또 상당한 오버헤드가 있다. 마치 비행기는 빠르지만 타고 내리는데 상당히 많은 시간을 소모하는것과 같은 이치다.
이걸 렌더링 프레임에서 한두번 하는 정도는 상관 없지만 트리 탐색 중에 node바뀔때마다 한다는건 오버헤드가 너무 크다.
만약 Occluder로 사용할 복셀 오브젝트를 100개 이상 수집한 후에 한번에 Occluder로서 버퍼에 그리고 Occludee로 사용할 오브젝트들을 또 수백개 이상 모아서 한번에 Culling테스트를 한다면 당연히 HW Occlusion Culling을 하는 편이 낫다.
그런데 내가 원하는것은 트리 순회 중에 적은 수의 Occluder와 Occludee오브젝트들을 그리고 테스트하는 것이다. 이 경우 throughput보다 응답성이 더 중요하다.
그래서 SW Occlusion Culling 기능을 직접 만들기로 했다.
[구현]
- 우선은 SW Rasterizer를 만들었다. 텍스쳐나 컬러 대신 View*Proj변환 후에 나온 z값을 써넣도록 했다. z테스트 용이니까.
- Occluder로 사용할 오브젝트는 가공된(조각된) 복셀 오브젝트를 뚫지 않는 AABB를 사용했다. 이것은 복셀 편집이 이루어질때마다 갱신된다.
- 트리 순회중에 뷰프러스텀에 들어간다고 판정된 Node의 AABB, 그리고 Node안의 복셀 오브젝트들이 Occludee가 된다. 노드의 culling 테스트는 확실히 하는건데 복셀 오브젝트를 앞으로도 Occludee로 테스트 할지는 확실치 않다. 복셀 오브젝트 개수가 많아지면 Occlusion Culling자체로 너무 많은 CPU싸이클을 소모할 수 있어서 이건 더 테스트 해보고 그대로 갈지 여부를 결정해야겠다.
[더 생각해봐야할 것들]
- 현재 256×256짜리 버퍼를 사용중인데 좀더 줄여도 되는지 테스트를 해봐야겠다.
- Barycentric Algorithm으로 삼각형을 그리고 있는데 아무래도 낭비가 좀 있어서 다른 방식으로 그려아할지 모르겠다. 이전에 위에서 아래로 스캔라인 따라 그리는 코드도 예전에 만들어둔게 있어서 그쪽으로도 테스트 해봐야겠다.
- 트리 순회중에 Voxel Object에 대한 Culling Test를 할지 여부.그냥 노드단위로의 Culling만 하고 오브젝트 단위의 Culling은 HW로 처리하는게 좋지 않을까…
- Draw및 Test에서 멀티스레드 지원
- Hierarchical Z Test를 적용하면?
캐릭터 앞에 쌓아놓은 블럭들로 저 멀리의 오브젝트들이 있는 노드를 통째로 culling하고 있다.