- 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 番目の媒質の屈折率
- θ2: 屈折角
インタラクティブな図で確認できること:
- 2 つの媒質の屈折率が同じなら、光は屈折せず直進する
- 2 番目の媒質の屈折率がより高い場合、光は法線方向へ屈折する
- 2 番目の媒質の屈折率がより低い場合、光は法線から遠ざかり、場合によっては全反射が発生する
- 入射光が表面に垂直であれば、屈折率に関係なく直進する
このプロジェクトの制約
複雑さを抑え、アルゴリズムを単純化するために次の条件を設定している:
- 外部媒質の屈折率は 1(空気)
- 内部のガラス材質には 1.5 を使用(一般的なガラス)
- 屈折イベントは 1 回のみ考慮(出口での屈折は無視)
- 入射光は常に背景面に垂直
- すべてのオブジェクトは 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))
- 端に張り出したリップと、中央の浅いくぼみを持つ複合構造
この 4 種類の関数だけでも、表面形状による屈折の違いを効果的に比較できる。
シミュレーション
各表面関数に応じて光線の屈折経路をシミュレーションし、実際の効果の違いを可視化する。
- 凸形(Convex)は光の経路を内側へ集め、凹形(Concave)は外側へ押し出す
- Apple の Liquid Glass はほとんどの場合、凸形を好む(Switch などは例外)
- 背景の矢印は屈折量(変位)を大きさで色分けして表示する
- 左右の境界で同じ距離にある点は同一の変位を持つため、効率的に再利用できる
変位ベクトル場の生成
ガラス表面全体について、各位置ごとの光の変位方向と大きさを表すベクトル場を構築する
- 円形では、境界を基準として常に法線方向へ移動する
変位量の事前計算
- 変位量は境界からの距離ごとに対称なので、半径単位であらかじめ値を計算して配列に保存
- 2D で 1 回だけ(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 map と specular 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、2 つの 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 を再生成するのは非常に非効率的である(filter の scale など一部のパラメータのみアニメーション可能)。
今後のオープンソース公開を検討しており、最適化やコード整理にも関心があると述べている。
まだコメントはありません。