INDEPENDENT STUDY / SEPTEMBER 2026
Persistent Scene
이 포트폴리오의 배경을 위해 만든 그래픽 실험입니다. Rust·wgpu와 WGSL로 입자의 위치와 속도를 계산합니다. 페이지를 옮겨도 canvas와 입자 상태를 유지하고, 본문과 목표 형상만 바꿉니다.
Pmndrs 예제의 은백색 입자와 깊이감, 《팬텀 가든》 스틸의 유기적인 형태와 여백을 참고했습니다. 곡면 함수와 움직임, 페이지 전환은 이 사이트에 맞춰 새로 구현했습니다.
DATA & COMPUTE
Surface Morphing
입자마다 고정된 ID를 부여하고, 해시값으로 표면 좌표와 레이어를 정합니다. 같은 ID를 리본, 셸, 매듭 등 열 가지 형태에 대응시켜 페이지 전환 중에도 각 입자가 이동할 위치를 유지합니다.
compute shader에서 각 입자의 현재 위치와 속도를 읽고, 목표 곡면으로 돌아가는 스프링 힘과 감쇠를 계산합니다. 표면의 흐름과 주변을 떠다니는 움직임에는 analytic curl field를 사용했습니다.
표면을 이루는 입자와 주변을 떠다니는 입자의 움직임을 따로 조절합니다. 고정 ID로 선택한 약 9%의 입자는 어떤 형태에서든 같은 강도로 움직입니다. Repeated cells의 중심은 고정하고, 표면 입자는 셀 안에서만 회전하도록 했습니다.
마우스의 최근 이동 방향과 속도로 주변 입자에 힘을 줍니다. Repeated cells는 셀의 위치를 유지하면서 입자의 크기와 밝기로 반응하고, 주변을 떠다니는 입자만 위치가 변합니다. 스크롤에 따라 회전과 레이어 간격을 바꾸고, 탭을 다시 열었을 때 움직임이 튀지 않도록 delta time에 상한을 뒀습니다.
Position + velocityGPU storage buffer · particle당 32 bytes각 입자는 자신의 위치와 속도만 갱신합니다. 이웃 입자를 읽지 않으므로 상태 버퍼를 두 개 번갈아 쓸 필요가 없습니다. compute pass가 끝나면 render pass가 같은 버퍼를 읽습니다.
RENDERING & LIFETIME
Rendering & Lifetime
입자의 깊이와 크기를 달리해 작은 점과 흐릿한 보케를 섞었습니다. 화면 전체에 DOF를 적용하는 대신 sprite의 크기와 투명도로 심도를 표현합니다. 반투명 입자가 겹칠 때의 렌더링 비용을 줄이기 위해 입자 수와 DPR에 상한을 뒀습니다.
페이지를 이동해도 canvas와 입자의 위치·속도는 유지합니다. 다른 탭으로 이동하면 렌더링을 멈추고, 모션 감소 설정을 켜면 움직임 없이 형상만 표시합니다.
WebGPU를 지원하지 않으면 WebGL2로 전환합니다. WebGL2에서는 vertex shader로 형태 전환과 마우스 반응을 계산하며, compute 시뮬레이션과 속도 시각화는 지원하지 않습니다. 둘 다 사용할 수 없으면 미리 렌더링한 이미지를 표시합니다.
SOURCE & REPRODUCTION
Source Code
WGSL, Rust, 브라우저 제어 코드와 빌드 방법을 공개했습니다.