S/W Occlusion Culling에 대한 미련을 못버리고 계속 튜닝하면서 사용해왔다. 다음번 프레임의 카메라 위치 예측 + 비동기 S/W Occlusion Culling으로 완료를 짓는가 싶었다. 성능도 만족스러웠다. 근데 생각해보니 어차피 비동기로 처리할거면 Hierarchical Z방식으로 처리하는게 더 낫지 않나? 그런 생각이 들었다. 512×512 -> 1×1까지 Down-sampling 비용이 항시 포함되어서 메인스레드에서 처리하긴 부담스러웠던것인데 어차피 비동기적으로 Raster스레드가 처리한다. 게다가 이 비용은 O(1)로 항상 일정하다. 일단 Down-sampling까지만 하면 Culling 비용은 비교도 안되게 적다.
코드의 흐름은 다음과 같다.
[메인스레드]
- 렌더링할 오브젝트를 수집하기 위해 메인스레드가 Find함수로 진입
- 결과 큐로부터 Raster스레드가 완료한 S/W Hi-Z Occlusion Culling결과가 있는지 확인.
- 있으면 현재 카메라 상태와 비교하여 유효한지 확인
- S/W Occ결과가 없거나 현재 카메라 상태와 달라서 유효하지 않을 경우 평범하게 KD-Tree를 순회하며 view-frustum culling만으로 보이는 leaf과 지형지물 삼각형을 수집. leaf에 포함된 렌더링될 오브젝트들도 함께 수집.
- S/W Occ결과가 유효하면 SW Occ결과로 얻은 bit table을 참조하여 수집한 leaf목록에서 가려지는 leaf들을 제거. leaf에 포함된 렌더링될 오브젝트들에 대해서는 SW Occ결과로 얻은 zbuffer에 대해 Hi-Z Occlusion culling test. 가려지면 이 또한 목록에서 제거.
- 다음번 프레임에서의 카메라 위치를 예측(이전 프레임과의 위치와 방향을 이용)
- Raster스레드에 예측되는 카메라 방향과 위치 지형지물의 삼각형 데이터 전달.
- 렌더링될 오브젝트의 목록과 함께 메인스레드 리턴
[Raster스레드]
- Raster스레드는 전달받은 지형지물을 depth buffer에 S/W Rasterize.
- 512×512 depth buffer를 1×1크기까지 down sampling – 소요시간은 O(1)
- 전달받은 leaf들을 depth buffer에 대해 Hi-Z Occlusion culling test. 가려는 leaf인경우 bit table에 표시
- S/W Hi-Z Occlusion Culling결과를 메인 스레드의 결과 큐에 push
- wait상태로 진입
일단 구현을 해봤다. 예전에 만들어둔 코드를 살려냈는데 이 와중에 예전에 만든 S/W Hi-z culling코드에 문제가 있었음을 발견했다. 어쩐지 그 당시에 삑사리가 많이 났었다.
- Hi-Z culling을 수행할때 occuldee로 사용할 오브젝트의 스크린 스페이스 상에서의 4개의 귀퉁이 점을 찾아서 각각의 점들에 대해서 depth값을 샘플링한다.
- 그리고 4개의 depth중에서 가장 먼 값을 찾아서 occludee의 depth와 비교한다.
- depth를 샘플링 할때는 보간이 없어야 한다. 보간을 하게 되면 그 시점에서 비교할 depth값이 변형되기 때문이다.
- 예전 코드는 여기서 보간을 해서 샘플링하고 있었다. 텍스처 샘플링의 아주 일반적인 룰을 따르고 있었는데 여기선 보간하면 안된다. 이 문제를 수정하고 나니 잘 작동한다.
우선 복셀맵에 대해서 테스트 해봤다. 단순 프레임레이트로만 보면 이전 방식보다 뚜렷하게 더 빠르지는 않다. 다만 오브젝트를 더 정밀하게 많이 걸러준다. 이전 방식에선 S/W로 Z-test하는 비용이 커서 오브젝트 단위로는 culling을 하지 않고 leaf단위로만 수행했다. S/W Hi-Z방식에선 culling비용이 엄청 적으므로 leaf단위뿐만 아니라 오브젝트 단위로도 culling이 가능하다.

S/W Hi-Z Occlusion Culling On

S/W Hi-Z Occlusion Culling Off
