브라우저에서 Liquid Glass 구현: CSS와 SVG를 활용한 빛의 굴절

8 hours ago 2

  • Apple이 2025년 WWDC에서 공개한 Liquid Glass 효과를 웹에서 CSS와 SVG, 물리 기반 굴절 계산으로 재현하는 방법 소개임
  • 굴절 현상의 원리와 Snell의 법칙을 활용한 유리 표면 묘사와 굴절 시뮬레이션 과정을 설명함
  • SVG displacement map 이용해 렌더링에 적합한 변위 벡터 필드를 생성하고, 실제 UI 컴포넌트에 적용 가능함을 시연함
  • Chrome에 한해 SVG filter를 backdrop-filter로 활용 가능하며, 다양한 UI 요소(돋보기, 스위치, 플레이어 등) 에 적용하는 예시를 제시함
  • 실시간 효과 구현이 가능하지만 크로스 브라우저 문제 및 퍼포먼스 제한이 있고, 향후 오픈소스 계획이 있음을 언급함

소개

Apple은 2025년 6월 WWDC에서 Liquid Glass 효과를 최초로 선보였음. 이 효과는 인터페이스 요소가 곡선의 굴절 유리처럼 보이도록 해 실제 유리 표면과 유사한 시각적 경험을 제공함. 이 글에서는 웹 환경에서 CSS, SVG displacement map, 물리 기반 굴절 계산을 통해 유사한 효과를 만드는 실습을 다룸. 완전한 구현은 목표가 아니고, 굴절과 반사 하이라이트 등 주요 특징을 재현한 확장 가능한 개념 증명을 목표로 함. 빛이 서로 다른 재질을 통과할 때 어떻게 굴절되는지 기초 원리로부터 시작해 단계별로 효과를 구축함. 마지막에 제공되는 인터랙티브 데모는 현재 Chrome에서만 정상 동작함.

굴절 현상의 이해

굴절이란 빛이 한 재질에서 다른 재질로 이동할 때 진행 방향이 바뀌는 현상임. 각 재질마다 빛의 속도가 다르기 때문에 발생하며, Snell의 법칙으로 입사각과 굴절각의 관계가 수식으로 표현됨:

  • n1 * sin(θ1) = n2 * sin(θ2)
    • n1: 첫 번째 매질의 굴절률
    • θ1: 입사각
    • n2: 두 번째 매질의 굴절률
    • θ2: 굴절각

인터랙티브 다이어그램에서 확인할 수 있는 것:

  • 두 매질의 굴절률이 같으면 빛은 굴절 없이 직진함
  • 두 번째 매질이 더 높은 굴절률일 때 빛은 법선 방향으로 굴절함
  • 두 번째 매질이 더 낮은 굴절률이면 빛이 멀어지며, 경우에 따라 내부 전반사가 발생할 수 있음
  • 입사광이 표면에 수직이면 굴절률과 무관하게 직진함

이 프로젝트의 한계

복잡도를 제한하고 알고리듬을 단순화하기 위해 다음과 같은 조건을 설정함:

  • 외부 매질 굴절률은 1 (공기)
  • 내부 유리 재질은 1.5 사용 (일반적인 유리)
  • 굴절 이벤트는 한 번만 고려 (출구에서의 굴절 무시)
  • 입사광은 항상 배경면에 수직
  • 모든 오브젝트는 2D 도형, 원 형태만 사용 (직사각형 등 확장 가능하지만 계산 필요)
  • 오브젝트와 배경면 사이에 틈이 없음
  • 이 조건을 통하면 Snell의 법칙으로 모든 광선을 단순 계산 가능

유리 표면 만들기

Liquid Glass 효과 구현을 위해선 가상 유리의 단면(렌즈 또는 곡면 판넬)을 수학적 함수로 정의해야 함.

표면 함수

유리 표면은 surface function으로 정의되며, 가장자리에서 평평한 내부까지의 두께를 나타냄

  • 함수 입력값 0: 외곽, 1: 베젤 끝 (평면 시작)
  • 각 점의 두께에서 도함수를 통해 표면의 법선 벡터 구함
const height = f(distanceFromSide); const delta = 0.001; const y1 = f(distanceFromSide - delta); const y2 = f(distanceFromSide + delta); const derivative = (y2 - y1) / (2 * delta); const normal = { x: -derivative, y: 1 };

주요 표면 함수 유형

  • Convex Circle: y = sqrt((1 - (1 - x))^2)
    • 간단한 원호 형태, 내부로 갈수록 급격하게 평평해져 굴절 가장자리가 뚜렷함
  • Convex Squircle: y = ((1 - (1 - x))^4)^(1/4)
    • Apple에서 즐겨 사용하는 형태, 곡선과 평면의 경계가 부드러워 확장 시에도 효과가 자연스러움
  • Concave: y = 1 - Convex(x)
    • 오목(볼록 반대) 형태, 빛이 오브젝트 경계 바깥으로 굴절되어 바깥 샘플링이 필요함
  • Lip: y = mix(Convex(x), Concave(x), Smootherstep(x))
    • 가장자리에 돌출된 립, 중앙에 얕은 굴곡이 있는 복합 구조

이 네 가지 함수만으로도 표면 형상에 따른 굴절 차이를 효과적으로 비교 가능함

시뮬레이션

각 표면 함수에 따라 광선의 굴절 경로를 시뮬레이션하여 실제 효과 차이를 시각화함.

  • 볼록(Convex)은 빛의 경로를 내부로 묶고, 오목(Concave)은 외부로 밀어냄
  • Apple의 Liquid Glass는 대부분 볼록 형태를 선호 (Switch 등 예외)
  • 배경 화살표는 굴절량(변위)을 마그니튜드로 색을 입혀 표시함
  • 좌우 경계에서 같은 거리에선 동일한 변위를 가져 효율적 재사용 가능

변위 벡터 필드 생성

유리 표면 전체에서 각 위치별로 빛의 변위 방향과 크기를 나타내는 벡터 필드를 구축함

  • 원형에선 경계 기준으로 항상 법선 방향으로 이동

변위 크기 미리 계산하기

  • 변위 크기는 경계로부터의 거리마다 대칭이므로 반지름 단위로 미리 값들을 계산해 배열로 저장
  • 2D에서 한 번만(127개 레이 시뮬레이션) 계산 후, z축을 중심으로 회전해 전체 필드 생성

벡터 정규화

벡터를 displacement map에 쓰기 위해 정규화(최대 1.0 스케일) 수행

  • 최대 변위값을 기준으로 나머지 벡터 크기를 나눠 정규화
const maximumDisplacement = Math.max(...displacementMagnitudes); displacementVector_normalized = { angle: normalAtBorder, magnitude: magnitude / maximumDisplacement, };

SVG displacement map에서 실제 픽셀 단위 변환 시 scale로 다시 최대 변위값을 곱해 원래 크기 복구

SVG Displacement Map

수학적 굴절 계산 결과를 실질적으로 브라우저 렌더링에 적용하기 위해 SVG displacement map을 사용함

  • <feDisplacementMap />의 각 채널(RGBA, 8비트)은 각각 변위의 X, Y축을 담당할 수 있음
  • 각 채널이 0~255의 값을 갖고, 128이 중성(변위 없음)을 의미함
  • displacement map은 반드시 이미지로 변환 필요
<svg colorInterpolationFilters="sRGB"> <filter id={id}> <feImage href={displacementMapDataUrl} x={0} y={0} width={width} height={height} result="displacement_map" /> <feDisplacementMap in="SourceGraphic" in2="displacement_map" scale={scale} xChannelSelector="R" yChannelSelector="G" /> </filter> </svg>

Scale

Red(X)와 Green(Y) 채널의 값을 [−1, 1] 범위로 맵핑

  • scale 속성으로 픽셀 단위 변위 최대치 곱해 실제 렌더링 구현
  • scale을 애니메이션해 효과 강도 조정 가능

벡터 → Red/Green 값 변환

  • 변위 벡터(각도, 크기)를 x, y 직교좌표로 변환 후, 128(중성) 기준으로 0~255로 맵핑
const x = Math.cos(angle) * magnitude; const y = Math.sin(angle) * magnitude; const result = { r: 128 + x * 127, g: 128 + y * 127, b: 128, a: 255, };

완성된 이미지는 SVG filter displacement map으로 활용 가능

Playground

인터렉티브 Playground에서는 표면 형태, 베젤 두께, 유리 두께, effect scale 등을 실시간 변경하며 굴절 필드, displacement map, 최종 렌더링 변화를 체험할 수 있음

Specular Highlight

마지막으로 specular highlight(유리 표면의 밝은 엣지 하이라이트)를 추가함

  • Apple의 구현은 rim light(가장자리 라이트) 방식으로, 표면 법선과 광원 각도에 따라 밝기 강도가 달라짐

굴절과 Specular Highlight 결합

최종 SVG filter에선 displacement mapspecular highlight 이미지를 각각 <feImage />로 불러오고 <feBlend />로 합쳐 최종 효과를 생성함

  • filter 파라미터를 조정해 다양한 비주얼을 연출할 수 있음

SVG filter를 backdrop-filter로 사용

  • 실질적으로 UI 컴포넌트에 Liquid Glass 효과를 입히려면, Chrome의 backdrop-filter: url(#...) 지원이 필요함
  • backdrop-filter 이미지 크기가 자동으로 맞춰지지 않으니, element 크기에 맞는 displacement map 준비 필수
.glass-panel { backdrop-filter: url(#liquidGlassFilterId); }

실제 UI 컴포넌트 적용

계산한 refraction과 displacement map을 바탕으로 현실적인 UI 컴포넌트에 적용 예시 구현

  • Chrome만 SVG filter를 backdrop-filter로 처리할 수 있음
  • 예시들은 진짜 프로덕션 컴포넌트가 아니라, 다양한 UI에 Liquid Glass 효과가 적용되는 모습을 시연하는 목적

Magnifying Glass

  • 양쪽에 refraction 및 zoom, 두 개의 displacement map을 사용
  • 그림자, 스케일 조정으로 인터렉티브한 효과 부여
  • 드래그로 렌즈 변형, 굴절 경로를 관찰 가능
  • 부드러운 specular highlight 추가

Searchbox

  • 표준 입력창 형태에 Liquid Glass 효과 적용

Switch

  • lip bezel 사용해 외부는 볼록, 내부는 오목
  • 중앙 슬라이더만 확대/축소된 상태, 가장자리는 내부 이미지 굴절

Slider

  • convex bezel을 사용, 현재값을 유리를 통해 그대로 보여주고, 양쪽은 배경 굴절 적용

Music Player

  • Apple Music의 Liquid Glass panel 스타일을 모방

  • convex bezel, subtle specular highlight로 입체감 부여

  • iTunes Search API를 활용해 앨범 아트와 곡명 등 정보를 로드

  • (리스트 형태의 곡명 및 앨범 정보 제공)

결론

이 프로토타입은 Apple의 Liquid Glass 개념을 실시간 굴절 효과와 간단한 하이라이트로 단순화해 표현함. Chromium 기반 브라우저(또는 Electron)에서만 실질적으로 동작 가능하며, 타 브라우저에서는 blur 레이어로 대체가 가능함.
이는 실험적인 구현이며, shape/size 변화마다 displacement map 재생성이 매우 비효율적임(필터 scale 등 일부 파라미터 만 애니메이션 가능).
추후 오픈소스 공개를 검토 중이며, 최적화 및 코드 정리에 관심이 있음을 밝힘.

Read Entire Article