Apple은 2025년 6월 WWDC에서 Liquid Glass 효과를 최초로 선보였음. 이 효과는 인터페이스 요소가 곡선의 굴절 유리처럼 보이도록 해 실제 유리 표면과 유사한 시각적 경험을 제공함. 이 글에서는 웹 환경에서 CSS, SVG displacement map, 물리 기반 굴절 계산을 통해 유사한 효과를 만드는 실습을 다룸. 완전한 구현은 목표가 아니고, 굴절과 반사 하이라이트 등 주요 특징을 재현한 확장 가능한 개념 증명을 목표로 함. 빛이 서로 다른 재질을 통과할 때 어떻게 굴절되는지 기초 원리로부터 시작해 단계별로 효과를 구축함. 마지막에 제공되는 인터랙티브 데모는 현재 Chrome에서만 정상 동작함. 굴절이란 빛이 한 재질에서 다른 재질로 이동할 때 진행 방향이 바뀌는 현상임. 각 재질마다 빛의 속도가 다르기 때문에 발생하며, Snell의 법칙으로 입사각과 굴절각의 관계가 수식으로 표현됨: 인터랙티브 다이어그램에서 확인할 수 있는 것: 복잡도를 제한하고 알고리듬을 단순화하기 위해 다음과 같은 조건을 설정함: Liquid Glass 효과 구현을 위해선 가상 유리의 단면(렌즈 또는 곡면 판넬)을 수학적 함수로 정의해야 함. 유리 표면은 surface function으로 정의되며, 가장자리에서 평평한 내부까지의 두께를 나타냄 이 네 가지 함수만으로도 표면 형상에 따른 굴절 차이를 효과적으로 비교 가능함 각 표면 함수에 따라 광선의 굴절 경로를 시뮬레이션하여 실제 효과 차이를 시각화함. 유리 표면 전체에서 각 위치별로 빛의 변위 방향과 크기를 나타내는 벡터 필드를 구축함 벡터를 displacement map에 쓰기 위해 정규화(최대 1.0 스케일) 수행 SVG displacement map에서 실제 픽셀 단위 변환 시 scale로 다시 최대 변위값을 곱해 원래 크기 복구 수학적 굴절 계산 결과를 실질적으로 브라우저 렌더링에 적용하기 위해 SVG displacement map을 사용함 Red(X)와 Green(Y) 채널의 값을 [−1, 1] 범위로 맵핑 완성된 이미지는 SVG filter displacement map으로 활용 가능 인터렉티브 Playground에서는 표면 형태, 베젤 두께, 유리 두께, effect scale 등을 실시간 변경하며 굴절 필드, displacement map, 최종 렌더링 변화를 체험할 수 있음 마지막으로 specular highlight(유리 표면의 밝은 엣지 하이라이트)를 추가함 최종 SVG filter에선 displacement map과 specular highlight 이미지를 각각 <feImage />로 불러오고 <feBlend />로 합쳐 최종 효과를 생성함 계산한 refraction과 displacement map을 바탕으로 현실적인 UI 컴포넌트에 적용 예시 구현 Apple Music의 Liquid Glass panel 스타일을 모방 convex bezel, subtle specular highlight로 입체감 부여 iTunes Search API를 활용해 앨범 아트와 곡명 등 정보를 로드 (리스트 형태의 곡명 및 앨범 정보 제공) 이 프로토타입은 Apple의 Liquid Glass 개념을 실시간 굴절 효과와 간단한 하이라이트로 단순화해 표현함. Chromium 기반 브라우저(또는 Electron)에서만 실질적으로 동작 가능하며, 타 브라우저에서는 blur 레이어로 대체가 가능함.
소개
굴절 현상의 이해
이 프로젝트의 한계
유리 표면 만들기
표면 함수
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 };
주요 표면 함수 유형
시뮬레이션
변위 벡터 필드 생성
변위 크기 미리 계산하기
벡터 정규화
const maximumDisplacement = Math.max(...displacementMagnitudes);
displacementVector_normalized = {
angle: normalAtBorder,
magnitude: magnitude / maximumDisplacement,
};
SVG 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/Green 값 변환
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,
};
Playground
Specular Highlight
굴절과 Specular Highlight 결합
SVG filter를 backdrop-filter로 사용
.glass-panel {
backdrop-filter: url(#liquidGlassFilterId);
}
실제 UI 컴포넌트 적용
Magnifying Glass
Searchbox
Switch
Slider
Music Player
결론
이는 실험적인 구현이며, shape/size 변화마다 displacement map 재생성이 매우 비효율적임(필터 scale 등 일부 파라미터 만 애니메이션 가능).
추후 오픈소스 공개를 검토 중이며, 최적화 및 코드 정리에 관심이 있음을 밝힘.