Three.js vs VTK.js: 왜 사용하는가?
1. Three.js와 VTK.js를 사용하는 가장 큰 이유
Three.js
- 웹 기반 3D 그래픽 구현이 쉬움
- 조명, 그림자, 애니메이션, 카메라 컨트롤 등 다양한 기능 제공
- 경량 라이브러리로 웹에서 빠른 로딩 가능
- 필요한 기능만 import하여 최적화에 용이
VTK.js
- 대용량 의료 데이터를 웹에서 3D로 렌더링하기에 최적화된 라이브러리
- WASM(WebAssembly) 활용 가능 → 빠른 속도 (C++ 백엔드 기반)
- 다양한 3D 포맷 지원 (obj, stl, ply, mtl 등)
2. Three.js vs VTK.js 비교
| 항목 | Three.js | VTK.js |
|---|
| 렌더링 방식 | 일반 3D 객체 (Mesh 기반) | 볼륨 렌더링 (Voxel 기반) |
| DICOM 지원 | 직접 지원 X | 지원 |
| 성능 | 일반 3D에 최적화 | 대용량 의료 데이터에 최적화 |
| 3D 메쉬 지원 | STL, OBJ, PLY 가능 | STL, OBJ, PLY 가능 |
| WebAssembly | 없음 | WASM 활용 가능 (빠른 속도) |
3. vtk.js가 대용량 데이터에 특화된 이유
- 필요한 데이터만 부분적으로 로딩하여 메모리 사용 최적화
- On-Demand 처리: 필요한 순간에 연산하여 렌더링 성능 개선
- 일부 연산을 WebAssembly 기반으로 실행
- 예: Ray Casting, isosurface 추출 등
vtk.js
- DICOM(.dcm)을 3D 볼륨으로 변환 가능
- vtkVolumeMapper를 통해 바로 볼륨 렌더링 가능
Three.js
- 직접 변환 불가
- 슬라이스를 쌓아서 볼륨 생성해야 함
- GLSL 셰이더 직접 작성
- 컬러 매핑, Raycasting 수동 구현 필요
- 성능 낮음
5. Mesh vs Volume 렌더링 방식
| 포맷 | 구조 | 렌더링 방식 |
|---|
| STL, OBJ, PLY | 표면 정보 (Mesh) | 삼각형 기반 |
| DICOM | 2D 슬라이스 → 3D | Voxel 기반 |
Three.js는 Mesh 렌더링에는 강하나, 볼륨 렌더링 불가
vtk.js는 둘 다 지원 (특히 의료 데이터에 적합)
6. Three.js와 VTK.js를 한 화면에 렌더링하는 방법
방법 1: 각기 다른 <canvas> 사용
- 문제: 이벤트 충돌, OrbitControls vs VTK 카메라 시스템 다름
방법 2: WebGL 컨텍스트 공유
- 문제: 셰이더, 프레임버퍼 충돌
- WebGL 내부 상태 관리 복잡
- 인터랙션 통합 어려움
결론
- 같은 씬(Scene)에서 병합은 매우 어려움
- VTK.js 단독 사용 권장
7. vtk.js 단독 사용 시 고려할 점
| 기능 | Three.js | VTK.js |
|---|
| Mesh 로드 (STL 등) | 지원 | 지원 |
| 이동/회전/스케일 | 가능 | 가능 |
| 텍스처 매핑 | 지원 | 제한적 |
| 고급 쉐이더(PBR 등) | 다양함 | 제한적 |
| 애니메이션 | 지원 | 제한적 |
| PBR, GPU 기반 렌더링 | 지원 | 미지원 |
※ CT/MRI는 표면 텍스처가 필요 없기 때문에 제한은 큰 문제가 아님
8. WebAssembly + Web Worker 활용
- 문제: DICOM은 대용량 (보통 700MB 이상)
- 해결책: Web Worker + WebAssembly 조합
- 백그라운드에서 연산 처리 → 메인 스레드 UI 멈춤 없음
- C++ 기반 VTK를 WebAssembly로 포팅해야 성능 향상 가능
WebAssembly 장점
- JS보다 빠름 (바이트코드 직접 실행)
- 소스코드 노출 없음
9. 브라우저 스레드 구조 요약
| 스레드 | 역할 |
|---|
| 메인 스레드 | JS 실행, DOM 조작, 이벤트 루프 |
| 렌더링 스레드 | 스타일 계산, 페인트 등 |
| Web Worker | 백그라운드 연산 |
| I/O 스레드 | 네트워크 요청 처리 |
| WebSocket/WebRTC | 실시간 통신 처리 |
| GC 스레드 | 메모리 정리 (가비지 컬렉션) |
Web Worker 실행 흐름
- 브라우저에서 Web Worker API 호출
- 브라우저가 OS에게 새로운 스레드 생성 요청
- OS가 실제 CPU 스레드를 할당
- Web Worker 실행 → 메인 스레드와 postMessage()로 통신
- 브라우저가 결과를 받아 메인 스레드로 전달
참고 링크