프로덕션 WebGPU 엔진 구축... 정신치료 실무를 위해?
Source: Dev.to
도전 과제: 노이즈가 흐름이 되는 순간
Therapy Warsaw의 디지털 존재감을 구축하면서 우리는 특이한 요구사항에 직면했습니다. 우리는 스톡 사진이나 정적인 일러스트를 원하지 않았습니다. 살아있는 느낌을 주는, 항상 변화하지만 주의를 끌지 않는 생성형 텍스처를 원했습니다.
시각적 은유는 간단했습니다: 복잡한 패턴이 명료함을 찾아가는 과정. 잡음의 필드가 서서히 스스로를 조직화하여 일관된 흐르는 선이 되는 모습.
기술 요구 사항
- 유기적이고 밀도 높음: 약 10,000개의 상호작용 입자.
- 성능 중요: 사용자가 스크롤할 때 모바일에서 60 FPS 유지.
- 탄력성: 10년 된 노트북(WebGL2)과 최신 장치(WebGPU) 모두에서 작동해야 함.
- 프레임워크 비사용: React도, Three.js도 사용하지 않음. 오직 제어된, 유동적인 로직만 사용.
아키텍처: 듀얼‑스택 WebGPU + WebGL2 엔진
메인 스레드 외 렌더링
웹에서 무거운 그래픽을 다룰 때 가장 중요한 규칙: 메인 스레드에서 벗어나라.
| 스레드 | 역할 |
|---|---|
| 메인 스레드 | DOM, 접근성, 라우팅, UI 상태 |
| 워커 스레드 | 물리 연산, 기하 생성, OffscreenCanvas를 통한 렌더링 |
물리 시뮬레이션에 지연이 발생하더라도 페이지 스크롤은 부드럽게 유지됩니다. 전용 메시징 시스템을 통해 시각적 “프리셋”(색상, 속도, 난류)을 동기화하며, 블로킹 없이 통신이 이루어집니다.
// main.js
const worker = new Worker(new URL('./worker.js', import.meta.url), { type: 'module' });
const offscreen = canvas.transferControlToOffscreen();
// Hand ownership to the worker
worker.postMessage({ type: 'init', canvas: offscreen }, [offscreen]);
WebGPU 구현
우리는 WebGPU로 시작했는데, Compute Shaders가 파티클 시스템에 자연스럽게 맞기 때문입니다.
Compute Passes
| Pass | Purpose |
|---|---|
| Map Pass | 노이즈 텍스처(불꽃, 밀도, 공허 맵)를 생성 |
| Flow Pass | 벡터 필드를 계산 |
| Life Pass | 파티클 수명을 업데이트하고 리셋을 처리 |
| Physics Pass | 흐름 벡터에 따라 파티클을 이동 |
핵심 성능 향상: CPU‑GPU 왕복을 피하는 것. 전체 시뮬레이션이 GPU에 머무릅니다.
Source:
WebGL2 Fallback Using Transform Feedback
WebGPU 지원이 점점 늘어나고 있지만 아직 보편적이지 않기 때문에 “멍청한” 대체 방안이 되지 않는 fallback이 필요했습니다.
- Transform Feedback은 WebGL2가 정점 셰이더에서 입자 위치를 업데이트하고 이를 버퍼에 다시 기록하도록 하여, 컴퓨트 셰이더를 흉내냅니다.
- 이 접근 방식은 CPU에 과도한 부담을 주지 않으면서 기능 동등성을 유지합니다.
부드러운 파라미터 전환: 임계 감쇠 스프링 시스템
사용자가 페이지를 이동할 때 시각화가 변형됩니다(색상이 변하고, 혼돈이 바뀌며, 속도가 조정됩니다). 단순한 선형 보간은 로봇처럼 보였기 때문에, 우리는 임계 감쇠 스프링 시스템을 구현했습니다:
function updateSpring(state, target, dt) {
const tension = 120;
const friction = 20;
const displacement = target - state.value;
const force = tension * displacement - friction * state.velocity;
state.velocity += force * dt;
state.value += state.velocity * dt;
}
매 프레임마다 약 20개의 스프링 구동 파라미터를 업데이트하고 이를 **Uniform Buffer Object (UBO)**에 업로드하여, 계산된 느낌이 아닌 물리적인 느낌의 전환을 구현합니다.
효율적인 트레일 렌더링
두꺼운 선을 렌더링하는 전통적인 방법은 세그먼트당 두 개의 삼각형(여섯 개 정점)을 생성하는 것이며, 긴 트레일에서는 비용이 많이 듭니다.
우리의 접근 방식
- 각 선의 헤드 위치만 저장합니다.
- 버텍스 셰이더 내부에서 루프(~60회 반복)를 실행하여 흐름 필드를 통해 경로를 역추적하고, 트레일을 실시간으로 재구성합니다.
장점: 대규모 대역폭 감소(선당 1점, 수천 개 정점이 아님).
단점: 정점당 ALU 비용 증가.
최신 GPU에서는 ALU가 저렴하고 대역폭이 비쌉니다. 이 트레이드오프를 통해 모바일 기기에서도 수천 개의 길고 부드러운 트레일을 렌더링할 수 있습니다.
결과
최종 사이트인 therapywarsaw.com은 살아있는 배경을 특징으로 합니다—작업의 특성을 반영하는 조용한 텍스처이며, 다양한 기기에서 성능을 유지합니다.
오픈 소스
엔진은 오픈 소스입니다:
github.com/23x2/generative-flow-field
셰이더 파이프라인이나 Transform Feedback 구현을 자유롭게 탐색해 보세요.