React Canvas에서 Liquid Glass Lens 효과를 만들었습니다
Source: Dev.to
소개
최근 react-canvas에서 액체 유리 렌즈 후처리 효과를 구현했습니다. 목표는 세 가지였습니다:
- 렌즈 중심에서 명확한 확대 느낌 제공
- 가장자리 근처에 강한 유리‑같은 왜곡과 미세한 색분산 구현
- 기존 CanvasKit 파이프라인을 완전히 재사용하여 두 번째 렌더링 스택을 없애기
GitHub 저장소:
Live demo:
렌더링 전략
가장 직관적인 방법인 별도의 캔버스나 WebGL 레이어를 추가하면 동기화 비용이 크게 증가합니다(스크롤, 줌, 카메라 상태를 일치시켜야 함). 또한 인터랙션 피킹 충돌이 발생할 수 있습니다.
대신 동일한 CanvasKit 파이프라인 내에서 전체 화면 후처리 방식을 선택했습니다:
- 전체 씬을 오프스크린 서피스로 렌더링합니다.
- 오프스크린 이미지에 SkSL
RuntimeEffect를 적용합니다. - 처리된 결과를 메인 캔버스에 한 번에 그립니다.
장점
- 씬 로직을 완전히 재사용하므로 이중 렌더 트리가 필요 없습니다.
- 효과 제어가 셰이더 코드와 유니폼에 집중됩니다.
- 기존 렌더링 아키텍처와 높은 호환성을 유지합니다.
렌즈 구성 요소
현재 액체 유리 렌즈는 네 부분으로 구성됩니다:
- 중심 확대 – 렌즈 내부의 선명한 확대.
- 테두리 왜곡 – 외곽 링 근처에 집중된 왜곡.
- 색분산 (RGB 오프셋) – 가장자리에서 미세한 색 프린지를 추가해 유리 느낌을 강화.
- 테두리 하이라이트 + 그림자 – 렌즈에 물리적인 존재감을 부여.
핵심 최적화
중심은 왜곡 없이 확대되어야 하며, 왜곡은 외곽 약 20 % 영역에 집중되어야 합니다.
정규화된 반경 t를 도입하고 다음과 같이 사용합니다:
// GLSL / SkSL snippet
float rim = smoothstep(0.8, 1.0, t); // edge weight
// Near‑zero distortion weight at the center (t < 0.8)
// Gradually increasing distortion and dispersion as t → 1
이 접근법은 전체 영역을 흔들기보다 실제 렌즈의 가장자리 굴절을 모방합니다.
렌더링 루프
렌즈는 순수하게 유니폼에 의해 구동되는 후처리 효과입니다. 매 프레임마다 강제로 다시 그리면 간단하지만 불필요하게 비용이 많이 듭니다.
- 포인터가 움직일 때
requestCanvasRepaint(canvas)로 렌더링을 활성화합니다. shouldContinueRepaint은 렌즈가 목표 지점을 아직 따라가고 있을 때만 계속됩니다.- 렌즈가 정착하면 연속 재그리기가 자동으로 중단됩니다.
결과: 움직일 때는 부드러운 모션, 멈춰 있을 때는 불필요한 회전이 없습니다.
흔히 겪는 함정
- 좌표 매핑:
getBoundingClientRect()와 백‑스토어 스케일을 이용해 포인터 좌표를 변환합니다. - 엣지 샘플링: 셰이더 UV 샘플링을
clamp(0..1)로 제한해 검은 가장자리나 범위 초과 아티팩트를 방지합니다.
이러한 사소한 디테일이 최종 완성도에 직접적인 영향을 줍니다.
정리
- 어디에 효과를 적용하지 않을지 결정하는 것이 무엇에 적용할지 결정하는 만큼 중요합니다.
- 렌즈 효과에서는 안정적인 중심과 표현력 있는 가장자리가 전체 영역 왜곡보다 자연스럽습니다.
- 아키텍처가 올바르게 잡히면(통합된 후처리 파이프라인) 반복적인 튜닝 비용이 크게 줄어듭니다.
Canvas/Skia 씬에서 유리, 깊이‑필드, 컬러‑그레이딩 효과를 만들고 있다면 “오프스크린 씬 + RuntimeEffect” 방식을 시작점으로 삼으세요. 향후 확장에도 훨씬 더 잘 스케일됩니다.