복셀 에디팅 팁

김포프 2013-12-02

다신 안 볼 줄 알았는데… -.- 요즘 들어 다시 복셀(voxel) 에디팅에 대해 잠시 본 바…. 이 기회에 제가 이짓하면서 배운 교훈들을 짧게(정말?) 공유해볼까 합니다. 저번에 썼던 '혼합 가능한 RGBM' 글'과는 달리 여기에 나열한 모든 정보/fact 들은 이미 온라인에 다 널려있는 내용입니다. 그냥 그 모든 내용들을 모아 제 의견을 약간 곁들인 게 전부입니다.

1. isosurface부터 이해하세요

복셀 데이터를 주무르는 가장 실용적인 방법은 각 복셀마다 iso 값을 대입하는 겁니다. 다른 방법도 시도해봤는데요(물론 제가 발명해낸 기괴한 방법도 한번 시도해봤죠 -.-) 대부분 별로더라고요. 단점들이 있어요. 결국엔 iso 값으로부터 메쉬를 다시 생성해내는 게 가장 실용적이고 메모리 친화적인 방법인 거 같습니다. iso surface extraction에 대해 설명하는 무수한 논문이 있으나 정작 isosurface가 뭔지를 쉽게 설명하는 자료는 흔치 않더라고요.

여기저기 뒤진 끝이 이 튜토리얼을 찾았습니다. 설명도 잘해놨고요. isosurface의 개념을 익히는 데는 최고인 듯싶습니다.(물론 영어입니다… 이 글도 영문 블로그에 썼다가 한글로 번역하는 놈이기 때문에.. 혹시라도 괜찮은 한글 자료 가지고 계신 분은 공유 좀 부탁 -.-a)

또한 3D Coat의 개발자가 매우 친절하게도 3D Coat에서 사용하는 복셀 데이터의 포맷을 공개해놨습니다. 이 데이터 구조를 들여다보는 것만으로도 복셀 에디팅에 대해 상당히 많은 걸 배울 수 있습니다.

복셀 편집에는 큰 관심이 없고 그 대신 임시 복셀 데이터로부터 절차적(procedurally)으로 거대한 지형(terrain)을 만드는 게 목적이시라면 VoxelFarm inc의 블로그를 읽어보세요. 이 분 정말 천재더라고요. (실제 한번 만나도 봤습니다. 몬트리올 살때 -.-)

2. 복셀 데이터는 "간접적"으로만 변경할 수 있습니다

볼륨 데이터를 표현하기 위해 iso 값을 사용하게 되면 복셀 데이터를 변경할 수 있는 유일한 방법은 iso 값을 변경하는 것입니다. 이 연산들은 간단한 수학인데요. 각 iso 값을 증감하거나 이웃 간의 값을 평균 내는 등입니다. 물론 distance 함수를 직접 계산해서 새로운 isosurface 모양을 꾸겨넣을 순 있지요. (이 distance 함수에 대해서는 위에 링크를 걸어둔 튜토리얼을 참고해 주세요.)

iso 값과 최종 메쉬의 모양과는 1:1 관계가 없습니다. 즉 "이 edge를 0.2미터 오른쪽으로 늘리고 싶어. 그러니 복셀 값을 0.752로 바꿔야지!"라는 말을 할 수가 없어요. 다시 말하면 최종 모양을 세밀하게 조절할 방법이 없습니다. 복셀의 밀도(density)를 아주 높이 올려도 (예: 1센티당 복셀 하나) 역시 마찬가지입니다. 이게 바로 제가 간접적으로만 복셀을 편집할 수 있다고 한 이유죠.

매우 뾰쪽한 edge를 가진 메쉬를 생성하는 새로운 알고리듬들도 있긴 합니다. 하지만 이 방법들은 복셀 편집을 위해 만들어진 게 아닙니다. (자세한 건 다음 섹션에서….)

3. 전통적인 마칭 큐브(marching cube)면 충분합니다

방금 말씀드렸듯이 iso데이터에서 메쉬를 생성하는 새로운 리서치들이 있습니다. 특히 Cubical Marching SquareEfficient Voxel Octree가 꽤 주목할만한 리서치들인데요.

CMS는 매우 날카로운 edge를 생성할 수 있습니다. 단, 한 가지 조건이 있습니다. 폴리곤 데이터로부터 iso값을 capture 해야만 하지요. 이 방법은 큐브 표면에서 각 폴리곤 edge가 만나는 거리를 인코딩하는 방법으로 작동하거든요. 근데 실시간으로 이 데이터를 편집하려면(예: C4 engine이나 3D Coat에서 하는 것처럼 꽤 힘들겠죠? 저도 edge의 교차점을 제대로 보존하면서 이 데이터를 편집할 실용적인 방법을 찾는데 실패했습니다.. ㅠ_ㅠ

EVO는 폴리곤 생성을 하는 기법이 아닙니다. 이건 CUDA에서 레이 트레이싱을 하면서 실시간으로 복셀을 화면에 그려주는 기법입니다. 실시간으로 돌릴 만큼 꽤 빨라요. (보고 꽤 감동받았습니다. 데모 한번 실행해보세요 ^.^) 그리고 복셀 데이터를 볼륨이 아닌 표면(2D 비슷한) 데이터로 저장했다는 게 꽤 주목할만합니다. 하지만 똑같은 문제점이 있지요. 이 데이터를 편집할 수 있는 쉬운 방법이 없어요 -_-

즉, 이 새로운 기법들은 모두 폴리곤 데이터를 복셀 데이터로 캡처 한 뒤 아무런 수정 없이 다시 보여주는데 유용한 방법이란 겁니다.

물론 전통적인 MC와 CMS를 사용해서 메쉬를 생성해내면 결과가 조금 달라집니다. 하지만 폴리곤 데이터를 완벽하게 재현해내는 게 목적이 아니라 복셀 데이터 편집이 목적이라면 그 차이점은 무시해도 될 수준입니다. 편집 툴에서 고치는 즉시 결과가 보이기만 하면(WYSIWYG) 어떤 방법을 써도 별 차인 없거든요. 하지만 MC가 CMS보다 최소 30% 이상은 빠릅니다. (둘 다 최적화를 하지 않은 C# 프로그램에서 테스트해봤습니다. 물론 당연히 멀티스레딩은 했고요)

4. 정점 버퍼를 최적화하세요

10 x 10 x 10 복셀에서 메쉬를 생성하면 삼각형이 1~2천 개 정도 나오는 건 예사입니다. 복셀 밀도가 높고 월드가 넓으면, 1500만 개 삼각형도 금방 씁니다. 이 엄청난 정점 데이터를 GPU로 보내려면 느리더라고요. 고사양 그래픽 카드와 저사양 그래픽 카드에서 모두 테스트해봤는데 둘 다 엄청 느렸습니다… -_-

그러니 간단한 정점 버퍼 최적화라도 해주세요. 삼각형들끼리 정점을 공유하고 인접 표면들끼리의 법선을 평균 내는 것 등이요. 이러면 정점 버퍼의 크기를 30~50% 정도 줄일 수 있습니다.

5. level-of-details을 쓰세요

정점들을 공유하는 것 만으론 충분치 않습니다. 1500만 개에서 30~50% 세이브해도 여전히 삼각형 수가 800~1000만 정도거든요. LOD 메쉬도 생성해주세요. 복셀 공간에서 메쉬를 생성하려면(아마 그러셔야 할걸요?) 매 n번째 복셀을 샘플링하는 방법으로 LOD 메쉬를 만들면 됩니다. (LOD1은 매 2번째, LOD2는 매 4번째 등으로요..)

CMS를 사용하면 LOD를 만드는 게 그리 어렵지 않습니다. 이 알고리듬은 교차점 정보를 가지고 있으므로 LOD사이에서 끊김 현상이 없거든요. 하지만 제가 권해드린 것처럼 MC를 사용하신다면 틈이 벌어집니다. 이 틈을 메꾸는걸 아직 시도해보진 않았습니다만.. 만약 그런다면 TransVoxel 알고리즘을 먼저 시도해볼 거 같습니다. Don Williams 아저씨도 이 방법을 써서 성공했다더군요.

높은 LOD 폴리곤으로부터 낮은 LOD들을 생성하는 방법도 있습니다. 이러면 복셀 데이터를 직접 사용하지 않아도 되지요. Voxel Farm이 이 방법을 씁니다. 이 방법의 장점이 하나 있는데요. 높은 LOD 메쉬를 재투영(reproject)해서 낮은 LOD 메쉬에 사용할 노말맵을 만들 수 있습니다. 하지만 메쉬 단순화(simplification)는 제 입맛에 안 맞더군요. 자세한 이유는 역시 다음 섹션에서… -_-

6. 메쉬 단순화(simplication)가 입맛에 안 맞을 수도 있습니다

정점 데이터를 최적화하는 다른 방법으로 메쉬 단순화 기법이 있습니다. Voxel Farm이 이 방법을 사용하고 결과도 그닥 나쁘지 않습니다. 하지만 전 별로 맘에 안 들었습니다. 합쳐질 삼각형과 그러지 않을 삼각형들을 제 맘대로 선택할 수 있는 방법이 매우 제약적이기 때문입니다. 이 삼각형들 선택은 어떤 오차계산(error calculation) 알고리듬을 선택하느냐에 따라 다른데요. 모든 경우에 제대로 작동하는 함수를 도무지 찾을 수가 없더라고요.

이 방법이 다른 분들의 용도에 안 맞는다는 말은 아닙니다. 그냥 저에겐 안 맞았습니다.

자.. 그럼 이 정도면 된 듯 하니… 즐겁게 복셀을 주무르세요~

포프