브라우저 속 물 커스틱을 실시간 광선 계산으로 구현
ScottieFox라는 닉네임의 개발자가 CAUSTIC//VOLUME 프로젝트의 소스 코드를 공개했다. 바닥의 커스틱을 매 프레임 광선 단위로 계산하는 두 개의 브라우저용 물 장면이다. 수면에 깔린 조밀한 격자의 각 정점이 물속 아래로 광선을 하나씩 내려보내고, 이웃한 광선들이 바닥에서 모이는 자리에 밝은 선이 생긴다.
두 버전 모두 three.js 기반의 HTML 파일 하나로, 설치도 빌드도 필요 없다. 브라우저에서 열면 바로 동작한다. 코드는 MIT로 공개되어 있어 저작권 고지와 라이선스 전문을 유지한다면 읽고 수정하고 상업 프로젝트에 사용할 수 있다.
커스틱의 밝기는 어떻게 계산되나
밝기는 면적의 비율에서 나온다. 자신의 빛이 바닥의 더 좁은 영역에 떨어진 수면 조각일수록 빛을 더 강하게 모으며, 셰이더는 이 비율을 화면 공간 미분을 통해 얻는다. 겹치는 조각들은 서로 더해지고, 그렇게 특유의 일렁이는 빛줄기가 생겨난다. 수면 아래 유리벽을 통해 들어오는 햇살은 별도로 더해진다.
수면은 길이 1.2미터에서 12센티미터에 이르는 24개의 진행파로 구성되며, 비슷한 길이의 파동이 나란히 가지 않도록 방향을 황금각으로 흩어 놓았다. 그 덕분에 커스틱 그물망은 단일 방향 파도에서 나타날 법한 줄무늬가 아니라 세포 모양이 된다. 파고는 각 스케일이 빛을 대체로 동일하게 굴절시키도록 맞춰져 있는데, 바로 이것이 바닥에 가장 선명한 선을 만들어낸다.
수면 위 인터랙티브한 파문은 three.js의 GPUComputationRenderer를 기반으로 한 별도의 GPU 파동방정식 시뮬레이션이 계산한다. 사용자의 클릭, 바람의 돌풍, 오리가 남기는 항적을 수조 전체로 퍼뜨리고 그 모두를 유리에 반사시킨다. 물 재질은 하늘의 반사에 프레넬 공식을, 깊은 곳을 들여다보는 시선의 굴절에 스넬의 법칙을 적용하며, 임계각을 넘어서면 전반사가 작동하고, 색은 부게르-람베르트 흡수가 만들어낸다. 빨간색이 파란색보다 빨리 사라지는 식이다.
하나는 스마트폰용 데모, 다른 하나는 최상급 그래픽카드용
CAUSTIC//LITE는 유리 수조와 수면 위의 고무 오리, 그리고 전환 가능한 광원(태양 또는 천장 등)으로 이루어져 있다. 클릭으로 파도를 일으켜 오리를 밀 수 있고, 슬라이더로 파고와 난류를 조절할 수 있다. 프로그램 전체는 주석을 포함해 약 990줄이며, 읽힐 것을 염두에 두고 작성되었다. 페이지는 스스로 프레임레이트를 감시하며 렌더 해상도를 조정하고, WebGL 2를 쓸 수 없거나 three.js가 로드되지 않으면 오리를 포함한 장면 전체가 일반 자바스크립트로 2D 캔버스에 트레이싱된다.
풀 버전은 18개 모듈로 구성된 약 7500줄 규모이며 강력한 하드웨어를 전제로 한다. EXT_color_buffer_float 확장을 지원하는 WebGL 2가 필요하다. 여기에는 다음이 추가되어 있다.
- 인터랙티브 잔물결 시뮬레이션(iWave) 위에 얹은 FFT 해양 스펙트럼, 거품과 물보라;
- 물속으로 빛기둥을 통과시키는 커스틱 볼륨, 그리고 색분산을 포함한 커스틱 포톤 트레이싱;
- 장난감 전함, 해적선, 고무 오리, 편지가 든 병, 궤짝, 닻, 암포라, 잠수 헬멧에 적용된 물리 기반 부력;
- 무리를 짓는 흰동가리와 양쥐돔, 촉수를 흔들며 맥동하는 해파리, 두 줄의 빨판을 가진 문어, 옆으로 걷는 게;
- 3차원 잉크 시뮬레이션과 층을 이루며 가라앉는 색색의 모래 — 드래그로 물을 저으면 둘 다 물살에 휩쓸린다;
- 각자의 색을 가진 최대 세 개의 조명과, 물 중간 높이에서 굴절되어 벽면에 반사되는 레이저;
- 시간적 안티앨리어싱, 블룸, 피사계 심도를 포함한 Low부터 Insane까지의 품질 프리셋.
수중 시점은 따로 눈여겨볼 만하다. 트레이서는 전반사는 물론 스넬의 창까지 처리한다. 잔잔한 수면을 아래에서 올려다볼 때 보이는 바로 그 하늘의 원이다.
어디서 받고 무엇을 할 수 있나
CAUSTIC//VOLUME 프로젝트는 GitHub에 통째로 올라와 있다. 저장소를 ZIP 아카이브로 내려받아 lite/index.html이나 sandbox/index.html을 로컬에서 열면 된다. 서버는 필요 없고, three.js r186 버전은 import map을 통해 jsDelivr CDN에서 불러온 뒤 브라우저에 캐시된다. 샌드박스는 Node 18에서 명령 하나로 모듈에서 다시 빌드되며 추가 의존성은 없다. 선택적인 테스트는 Playwright를 통해 헤드리스 Chromium에서 두 장면을 모두 돌리고 셰이더 오류가 나면 실패한다.
저자는 기여자들에게 두 가지 조건을 지켜 달라고 당부한다. 각 버전은 설치가 필요 없는 three.js 기반 단일 HTML 파일로 남을 것, 그리고 라이트 버전은 1000줄을 넘지 않으며 동작하는 CPU 폴백을 반드시 유지할 것.
이런 데모를 왜 다시 브라우저에서 만드나
실시간에서 커스틱은 대개 값싼 방식으로 표현된다. 미리 계산된 애니메이션 텍스처를 바닥에 투사하는 식인데, 이 수법은 three.js의 전형적인 예제에서도 쓰인다. 대형 엔진에서 물리적으로 정확한 계산은 보통 레이 트레이싱과 함께 등장하거나, 아티스트가 들여다볼 수 없는 비공개 구현 안에 남아 있다. 이번 공개의 가치는 아마도 읽을 수 있는 구현이 통째로, 라이선스 제약 없이 공개되었다는 바로 그 점에 있을 것이다.
기법 자체가 새로운 것은 아니다. 수면의 원래 조각과 굴절된 조각의 면적을 비교하는 방식은 이미 Evan Wallace가 자신의 WebGL 데모에서 사용했고, 그 데모는 지금까지도 여러 플랫폼으로 이식되고 있다. 그런 이식판들의 링크는 80 Level도 소개한 바 있다. 여기서 새로운 것은 규모와 접근성이다. 같은 원리가 부력과 잉크, 모래, 빛기둥까지 갖춘 본격적인 샌드박스로 확장되었으면서도, 더블클릭으로 열리는 파일로 남아 있다.
이 프로젝트가 하필 지금 등장한 것은 자연스러워 보인다. WebGL 2는 Safari를 포함한 모든 최신 브라우저의 공통분모가 되었고, 부동소수점 타깃도 더 이상 이색적인 것이 아니어서 브라우저에서의 본격적인 시뮬레이션이 눈에 띄게 수월해졌다. 문서에 달린 단서도 시사적이다. Chrome이 141 버전부터 정상 동작하는 GPU가 없는 기기에서 소프트웨어 WebGL로 폴백하지 않게 되었다는 것인데, 라이트 버전이 CPU 폴백을 강조하는 이유가 여기에 있다. three.js의 물 표현을 둘러싸고는 상업적인 영역을 포함해 전반적으로 활기가 느껴지며, 알고리즘을 상세히 해설한 무료 프로젝트는 브라우저 탭 안에서 달성 가능한 수준의 기준선을 제시한다. 단일 파일 오픈 테크노 데모라는 형식이 자리 잡는다면, 예전에는 대략적인 편법의 형태로 프로젝트에서 프로젝트로 전해지던 효과들의 레퍼런스 구현이 커뮤니티에 차츰 축적될 것이다.