Design

backdrop-filter では作れない「屈折するガラス」— iOS 26 Liquid Glass を Web で再現する判断軸

iOS 26 の Liquid Glass がガラスに見えるのは、半透明やぼかしではなく背景がレンズのように歪む屈折のためです。CSS の backdrop-filter は仕様上この屈折を作れません。WebGL シェーダーで屈折をどう実装するか、そして設計者として「いつ CSS をやめてシェーダーに切り替えるか」の判断軸を整理します。

2026年6月18日 12:05·11 min·SoraEndo
SoSoraEndo2026年6月18日 12:0511 min2,550

動画で読む

まず結論 — backdrop-filter は「ぼかせる」が「屈折できない」

結論から書きます。iOS 26 の Liquid Glass がガラスらしく見える核心は、半透明でもぼかしでもなく、背景がレンズのように歪んで見える「屈折」です。そして CSS の backdrop-filter(背景にフィルターをかけるプロパティ)は、仕様上この屈折を作れません。だから本物のガラス表現が欲しいなら、どこかで CSS を諦めて WebGL に切り替える判断が要ります。設計者として知っておきたいのは、その分かれ目がどこにあるか、です。

私は最初、Liquid Glass 風の半透明パネルを backdrop-filter: blur(20px) saturate(180%) で作ろうとしました。それっぽくはなります。でも横に並べて iOS の実物と見比べると、決定的に違う。実物はパネルの縁で背景の線がぐにゃりと曲がるのに、CSS 版は背景がただ均一にぼけているだけでした。曲がらないのです。この「曲がらなさ」がどこから来るのかを掘ると、CSS フィルターの設計思想そのものに行き当たります。

なぜ CSS backdrop-filter では屈折ができないのか

理由は単純で、CSS のフィルターは「各ピクセルをその場で加工する」ことしかできず、「背景の別の位置にあるピクセルを持ってくる」ことができないからです。屈折は後者を必要とします。だから仕様上の限界であって、パラメータの調整で越えられる壁ではありません。

blurbrightnesssaturate も、入力ピクセルの色を計算で変える関数です。ぼかしは近傍ピクセルの平均を取りますが、それでも「自分の位置の周り」を見るだけで、画面の遠くから色を引っ張ってはきません。一方で屈折(refraction、光がガラスを通って曲がる現象)は、「このピクセルには、本来あるべき場所から少しずれた背景が映る」という座標の移し替えです。レンズ越しに見た直線が曲がって見えるのは、この座標移動が起きているから。CSS フィルターの語彙には、この「座標をずらす」操作が存在しません。

CSS の新機能が増えても、この線引きは当面変わらない、と私は見ています。ネイティブ CSS で表現の幅が広がる流れ自体は歓迎していて、その手触りはネイティブ CSS の popover と anchor positioning を運用目線で読むでも書きました。ただ「背景を任意に歪ませる」のはフィルターの責務を大きく超えていて、ブラウザがこれを宣言的プロパティで提供する未来はまだ遠い。やるなら自分でピクセルを動かすしかありません。

WebGL でどう屈折を作るか — SDF・法線・ピクセルずらし

WebGL での屈折は、三段階で組み立てます。まずガラスの形を SDF で表し、次にその勾配から「縁の向き(法線)」を取り、最後に縁ほど強く背景のサンプル位置をずらす。中央は素通し、縁に近いほど大きく曲がる、という本物のレンズの挙動をこれで再現できます。

出発点は SDF(Signed Distance Function、符号付き距離関数)です。これは「ある点がガラス領域の縁からどれだけ内側/外側にあるか」を連続値で返す関数で、角丸矩形なら Inigo Quilez が公開している式がほぼ定番です。境界をピクセル単位の 0/1 で持たず連続値で持つので、アンチエイリアスにもそのまま使えます。

// 角丸矩形の符号付き距離(Inigo Quilez 式)
// p: 中心からの座標 / b: 半幅・半高 / r: 角丸半径
float sdRoundRect(vec2 p, vec2 b, float r) {
  vec2 q = abs(p) - b + r;
  return min(max(q.x, q.y), 0.0) + length(max(q, 0.0)) - r;
}

次に、この距離場を数値微分して勾配(gradient)を取ります。勾配は「距離が最も増える向き」、つまりガラスの縁から外を向いた法線(normal、面の向き)になります。屈折はこの法線方向に背景をずらせばよい。ずらし量は「縁にどれだけ近いか」で重み付けします。

// 距離場の勾配 = 外向き法線(eps は 1px 相当)
vec2 e = vec2(eps, 0.0);
vec2 grad = normalize(vec2(
  sdRoundRect(p + e.xy, b, r) - sdRoundRect(p - e.xy, b, r),
  sdRoundRect(p + e.yx, b, r) - sdRoundRect(p - e.yx, b, r)
));

// 縁ほど大きく曲げる係数。pow の指数で「曲がりの効き方」を調整
float curve = pow(1.0 - band, 1.6);
vec2  disp  = grad * curve * uRefraction * maxDisp;

// ずらした座標で背景テクスチャをサンプリング
vec4 bg = texture2D(uBackground, uv + disp);

pow(1.0 - band, 1.6)1.6 は、私が手で詰めた値です。1.0 だと縁の曲がりが直線的で安っぽく、2.5 まで上げると縁だけ急に歪んで「水滴」みたいになる。1.6 前後が一番ガラスの厚みに見えました。この手の数字に正解は無く、背景の絵柄とパネルサイズで最適点が動きます。

「ガラスらしさ」を足す細部 — 色収差・スペキュラ・DPR

屈折だけだと、まだ「歪んだ窓」止まりです。ガラスに見せる最後の一押しは、色収差・スペキュラハイライト・そして DPR(デバイスピクセル比)対応の三つでした。逆に言えば、この三つを外すと途端に CG っぽさが出ます。

色収差(chromatic aberration、レンズの縁で RGB がわずかにずれる現象)は、R・G・B を少しずつ違うずらし量でサンプリングするだけで出せます。縁にうっすら虹色が乗り、ガラスの「厚み」が一気に説得力を持ちます。スペキュラハイライト(鏡面反射の光沢)は、法線とライト方向の内積で縁に細い光の線を足す処理で、これがあると「つるっとした表面」に見えます。背景のぼかしは多点サンプリング(私は 12 方向)で取りますが、ここはタップ数を増やすほど綺麗になる代わりに重くなる、わかりやすいトレードオフです。

そして一番ハマったのが DPR でした。CSS px のまま計算したら、Retina 画面(dpr=2)でガラスの位置とサイズが半分にずれて、最初は「シェーダーが壊れた」と本気で悩みました。原因は単純で、WebGL は実ピクセルで動くので、座標もぼかし半径もすべて CSS px × dpr の実ピクセル値で渡さないと合いません。気付けば一行の話なのですが、気付くまでが長かった。視覚の「意図どおりか」を 1px 単位で詰める話はピクセルパーフェクトを意図の一致として捉えるに通じます。

設計者として、いつシェーダーに切り替えるか

私の判断軸はこうです。屈折が「体験の主役」になる一点だけシェーダーを使い、それ以外は素直に backdrop-filter で済ませる。全面ガラスにしたい誘惑はありますが、コストとアクセシビリティの両面で割に合わないことが多い。

シェーダーに踏み込む価値があるのは、ヒーローのカード一枚やオンボーディングの主役パネルなど、ユーザーの視線が必ず止まる場所です。逆に、リストの全カードを WebGL ガラスにするのは過剰で、背景画像の CORS(別ドメイン読み込み時の crossOrigin 設定)対応や GPU 負荷、低スペック端末でのフレーム落ちが効いてきます。さらに prefers-reduced-motion(動きを減らす設定)を有効にしているユーザーには、歪みやハイライトのアニメーションをおとなしくする逃げ道を必ず用意すべきです。見た目の派手さと、状態や環境をまたいだ破綻のなさは別物で、その視点は状態とテーマをまたいだコントラスト監査で書いたとおりです。

それでも、屈折が効く場所で効かせたときの効果は本物です。私は backdrop-filter のぼかしを 3 枚重ねて「なんか違う」と唸っていた金曜の夜を思い出すと、あれは方向ごと間違っていたと分かります。ぼかしは屈折の代用にはならない。次に Liquid Glass 風の表現を頼まれたら、私はまず「ここは本当に屈折が要る一点か」を確かめ、要る一点にだけシェーダーを置きます。道具を増やすのではなく、置きどころを選ぶ。それが設計者の仕事だと思っています。

よくある質問

CSS の backdrop-filter ではなぜ屈折表現ができないのですか?
backdrop-filter のフィルター関数は各ピクセルをその場で加工するだけで、背景の別の位置にあるピクセルを持ってくる『座標の移し替え』ができないためです。屈折は後者を必要とするので、ぼかしや明度調整をいくら組み合わせても再現できません。
WebGL で屈折ガラスを作る最小の手順は?
まず SDF(符号付き距離関数)でガラスの形を表し、その距離場を数値微分して勾配=法線を取り、縁ほど強く背景テクスチャのサンプル位置を法線方向にずらします。中央は素通し、縁ほど大きく曲がる本物のレンズ挙動をこれで再現できます。
屈折以外に『ガラスらしさ』へ効く要素は何ですか?
色収差(縁で RGB をわずかにずらす)、スペキュラハイライト(法線とライト方向の内積で縁に光沢を足す)、背景の多点ぼかしの三つです。加えて Retina で位置がずれないよう、座標を CSS px × dpr の実ピクセル値で渡す DPR 対応が必須です。
サイト全体を WebGL ガラスにするべきですか?
おすすめしません。GPU 負荷や背景画像の CORS 対応、低スペック端末のフレーム落ちが効いてきます。屈折が体験の主役になる一点(ヒーローカード等)だけシェーダーを使い、それ以外は backdrop-filter で済ませ、prefers-reduced-motion への配慮も用意するのが現実的です。

参考文献

  1. CSSのbackdrop-filterでは作れない屈折するガラスをWebGLで作る(Zenn / orectic)
  2. MDN — backdrop-filter
  3. Inigo Quilez — 2D distance functions

Reaction

Share

X (Twitter)