앞서 Rasterize코드의 버그를 수정했고 거의 완벽하게 CPU로 Raster/Test작업을 수행한다. 시간이 걸린다는 문제만 빼면 culling결과는 매우 훌륭하다.
다만 시간이 걸린다는 문제는 결코 무시할 수 없다. CPU로 삼각형을 픽셀 단위로 찍는 작업은 간단한 작업이 아니다. 아무래도 매 프레임마다 SW Occlusion Culling을 수행하면 Raster/Test비용이 렌더링 비용보다 더 커진다.
그래서 카메라 위치와 방향의 변화가 일정 수치 미만인 경우 앞 프레임의 cullig정보를 사용하게 했다. 물론 이렇게 하면 평균 프레임은 확실히 올라간다. 하지만 culling할때의 프레임은 무조건 떨어지기 때문에 화면이 몇 프레임마다 미세하게 멈칫거리는(평균 프레임은 높지만) 현상을 느낄수 있다. 어차피 쾌적한 게임환경을 제공하는게 목적인데 수치상 평균 프레임만 높게 나오는건 의미가 없다
Voxel Object culling때처럼 Raster/Test시간을 제한하기도 해봤다. 이 경우는 culling성능이 훅 떨어져서 너무 아쉽다.
멀티스레드로 SW Occlusion Culling을 수행할 경우 시간당 처리량은 높아지지만 스레드간 동기화와 스레드가 깨어나는 시간차 때문에 응답성이 크게 떨어진다.
멀티 스레드를 사용하려면 스레드의 awake/wait상태가 빈번해서는 안된다.
그래서 아예 SW Occlusion Culling을 백그라운드에서 돌리고 메인루프에선 얘가 완료하기를 기다리지 않는 방식으로 처리하는건 어떨까 생각해봤다.
현재 테스트중인 마을맵에서 거의 제약없이 SW Occlusion Culling의 Raster/Test를 수행하면 7ms – 9ms 정도가 나온다.
게임에 60프레임으로 돌아간다고 할때 한 프레임 간격은 16ms이다. 8ms 간격이면 100FPS-120FPS가 나온다. 백그라운드로 최대 120FPS정도로 SW Occlusion Culling을 수행할 수 있다.
120FPS로 게임이 돈다고 하면 대개의 경우 한 두 프레임 사이에 카메라의 위치와 방향이 크게 변하지는 않는다.
따라서 백그라운드에서(다른 스레드로) 120FPS으로 SW Occlusion Culling을 돌린다면 그 결과를 메인 루프에서 그대로 사용해도(Culling결과가 한프레임이나 두프레임 전의 결과라도) 거의 무방할것이다.
100% 문제가 되지 않는 것은 아니고 프레임 사이에 카메라 위치/방향이 유의미하게 변할수도 있다. 어차피 SW Occlusion Culling으로 걸러내는 대상이 오브젝트가 아닌 다수의 오브젝트가 모여있는 leaf이므로 leaf의 aabb를 x,y,z각 축당 1m씩만 늘려서 테스트시키면 오차는 거의 발생하지 않을 것이다.
그래서 다음과 같이 처리했다.
1. 메인루프에서 Find함수를 호출하면 현재 카메라의 위치/방향을 기준으로 KD-Tree를 순회하며 보여지는 leaf를 탐색.
2. 탐색한 leaf들을 백그라운드 SW Occlusion Culling스레드에 전달.(여기서 처리된 결과물은 다음 프레임이나 다다음 프레임에 쓰일 수 있다.)
3. 이전에 요청해둔 SW Occlusion Culling결과가 있는지 확인.
4. 이전에 요청해둔 SW Occlusion Culling결과가 있을 경우, 그 당시의 카메라의 위치/방향을 현재 프레임의 카메라 위치/방향과 비교.
5. 유의미한 변화가 아니라면 방금 찾아낸 leaf목록 대신 앞 프레임의 SW Occlusion Culling으로 한번 걸러낸 leaf목록을 선택.
6. 이 leaf목록들의 오브젝트들을 렌더링.
7. 다음 프레임에 이 과정을 반복….
일단 잘 돌아간다. SW Occlusion Culling을 켰을때 culling효과는 극대화시키면서 SW Occlusion Culling 자체로 인한 성능 저하는 거의 없다. 일단 오차 문제가 크게 두드러지는것 같지는 않은데 좀더 테스트를 해봐야 알것 같다.