ノイズ質感のSVG制作
(daniel.do)- Webイラストが平面的なベクターを超えてノイズ質感やライティング・陰影を使う方向へ広がるなか、それを軽量で再利用しやすいSVGとして実装する方法が重要になっている
- 核心となる目標は、単純な図形にテクスチャ付きの影効果を加え、SvelteやReactコンポーネントへ移して色や形を変えて使えるようにすること
- 基本構造は、
<defs>で図形を一度定義し、<use>で再利用したうえで、maskとlinearGradientにより色に縛られにくいグラデーションマスクを構成する - 粒子感は
<feTurbulence>ベースのSVGフィルターで作り、<feComposite>、<feColorMatrix>、<feBlend>を組み合わせて、選んだ塗り色と混ぜ合わせる - 結果は意図した質感に到達したものの、Safariのレンダリング差が残っており、ブラウザごとの表現や、より複雑なイラストへの適用には追加調整が必要
コードだけでSVGにノイズ質感を追加する
- 最近のWebデザインではベクターイラストが引き続き使われる一方で、きれいな平面図形よりも粗い粒子感の入ったライティング・陰影スタイルが目立つ
- こうしたスタイルはイラスト制作ツールからSVGとして書き出すこともできるが、テクスチャがラスターとして入ったり、ベクターのサイズが大きくなったりする可能性がある
- 目標は、一般的なSVG仕様だけでノイズのあるテクスチャ影効果を作り、コンポーネント化しやすい構造を保つこと
- 再利用性のため、図形や色を変えても同じ方式が動作するように構成する
基本図形とグラデーション
- 円や四角形のようなSVG基本図形は簡単に描けるが、再利用可能なコンポーネントでは固定色のグラデーションだけでは不十分
- 基本色に合わせて変化するグラデーションを作るためにマスク(mask) を使う
- SVGマスクは、黒と白によって見える領域や隠れる領域を制御する
- 方式はやや分かりにくい場合があるが、単純な例から始められる
- 同じ円図形を複数回使う構造なら、
<defs>内に<circle id="shape">を定義し、<use href="#shape">で参照する - この方式は繰り返しを減らし、図形定義を一か所に集約するDRYな構造
ノイズテクスチャの生成方法
- グラデーションの上に質感を載せるため、SVGのフィルター(filter) を使う
- ノイズ生成は
<feTurbulence>が担う<feTurbulence>は人工的なテクスチャを作るSVGフィルタープリミティブ- 望む雰囲気を得るには複数の値を調整する必要がある
- 最終フィルターは複数の段階を経て、質感を色と混ぜ合わせる
<feTurbulence type="fractalNoise" baseFrequency="19.5" numOctaves="10">でノイズを生成する<feComposite>でturbulenceの結果をSourceAlpha内に合成する<feColorMatrix type="luminanceToAlpha">で色の変化を抑える<feBlend mode="color-burn">で元のグラフィックと合成する
- フィルターは最終的な
<use>要素にfilter="url('#noise')"として適用する
最終的なSVG構成
- 最終例では、
<defs>内に図形、フィルター、マスク、グラデーションをまとめて定義する - グラデーションは
linearGradient id="fade"で構成される0%地点は黒色にstop-opacity="0.6"を使う65%と75%地点は白色を使い、透明度を段階的に調整する
- 最終図形には塗り色、マスク、フィルターが一緒に適用される
fill="hsl(337, 92%, 69%)"mask="url(#gradient)"filter="url('#noise')"
- 色を追加し、グラデーションをさらに微妙に調整することで、望む視覚効果に近い結果を得る
- 全体の結果はFull Demoで確認でき、実装コードはrepositoryで公開されている
結果と限界
- 追加の実験を経て満足できるイラストを作れたが、結果は完璧なレベルではない
- 特にSafariでは、他のブラウザとは異なるレンダリングになる
- 参考にした複雑なイラストに比べるとまだ単純だが、さらに磨き込めば、より多くの場面で使える可能性がある
- 2023年12月7日の追記によると、Hacker Newsへの投稿後、Jimmy Chionによる2021年のCSS-Tricks記事Grainy Gradientsを知った
- 同記事が到達した手法はこの方式と似ており、補足資料として併せて参考にできる
2件のコメント
やはり期待を裏切らないSafari! 2020年代のIEという名前がもったいないくらいですね
Hacker News のコメント
最近 SVG を触っていて、canvas では作りにくかった ドロップシャドウを入れようとしたのですが、Safari では SVG 内の CSS ドロップシャドウが動作せず、SVG ネイティブのドロップシャドウフィルターを使う必要があり、その結果も他のブラウザとは違って見えました
具体例は https://www.einarmagnus.net/pages/logo にあり、JavaScript で Safari を検出して SVG 版を有効にし、CSS 版を無効にするようにしています
「再帰的な陰陽」の背景は https://einarmagnus.net/blog/2020/01/22/finding-the-logo にあります
テクスチャももっと実験してみたいのですが、Safari であまりにも違って見えるのがもどかしいです。いつか SVG の解釈の仕方が収束するのか気になります
この話題については https://razrfalcon.github.io/notes-on-svg-parsing/ が読む価値があります
SVG ライブラリのバグ報告で「自分の SVG が正しくレンダリングされない」という表現をよく見ますが、実のところ正しくレンダリングされた SVG など存在しない、という内容です。テキスト、フィルター、アニメーションのような 高度な機能を使い始めると、レンダラーごとに簡単に壊れるという趣旨です
パラメータを少し変えるだけで scorpion、balance、spruce のようにかなり違うデザインが出てくるのが見事です
筆者が探していた用語は ディザリングに近いです。CSS-Tricks の “Grainy Gradients” も同じ手法を説明しています
https://css-tricks.com/grainy-gradients/
CSS で似たように処理する代替案もありますが、画像全体に適用される方式です
https://github.com/tomren1/dither-with-css
ディザリングは、滑らかな高色深度の画像を限られた色数に量子化するときにバンディングを減らすため、各ピクセルを上下どちらかの値から確率的に選ぶ手法です。目的の色に正確に対応する地点では、ノイズのように見えないこともあります
ここでは均一なノイズテクスチャが生じるだけで、実際にバンディングを減らしてはいません。美的目的なら、本物のディザリングよりこちらの方がよさそうです
ただしディザリングのように欠陥を隠し、体感上の鮮明さを高めて、画像品質がよく見える効果はあるかもしれません
筆者は「flat」「skeuomorphic」「italian style」のように視覚スタイルを呼ぶデザイン用語を探しているようです
幾何学的な形状と粒状感の組み合わせを見ると Memphis style が思い浮かびます
むしろヴィンテージの旅行ポスターアートを思い出します。例: https://www.graphicart-news.com/wp-content/uploads/2012/09/2...
[0] https://en.wikipedia.org/wiki/Stippling
これを作ったときは本当に見ておらず、一人で試していて同じ結論にたどり着きました
フィルム写真家や映画制作者たちは フィルムグレインをなくそうと苦心していたのに、デジタル写真家たちはまたそれを入れたがっている、という光景を思い浮かべます。隣の芝生は青いものです
何もかもがデジタル的に無菌なものばかりの中で、このページの例はかなり魅力的に見えます
ノイズ自体が問題なのではなく、いつも問題なのは多すぎるかどうかでした
実装ごとにどの機能が壊れるのかをまとめた資料はあるのだろうか? SVGでメンタルヘルスを守る方法が知りたい。
最近、複雑な学会ポスターをSVGで作るという失敗をしてしまい、今ではブラウザやプログラムごとに違うレンダリングになっている。
https://kxygk.github.io/imergination/
作っていて経験したことはこうだ。SVGを含めたりリンクしたりすることはできるが、その中にさらに別のSVGが入ると動作しない。たとえばポスター → 図表 → 複数のプロット/地図という構造なら、下位画像が変わったときに動的に更新されるSVGは事実上不可能。
Webブラウザは「セキュリティ」上の理由で、埋め込まれた/リンクされた画像を勝手に表示しないこともある。上のリンクのSVGを別タブで開くと正しく表示されない: https://raw.githubusercontent.com/wiki/kxygk/imergination/ag...
複雑なSVGをPDFに変換するのも、不一致なしに処理するのは難しく、メモリが底を突くこともある。SVG-to-PDF変換器ごとに結果が異なる。
Inkscapeのような複数の実装では、含まれたSVGを任意にラスターグラフィックのように扱うこともあり、いつそれが起きるのかは最後まで特定できなかった。PNGにレンダリングするとき、InkscapeがDPI設定を下位画像へ伝播しない問題もあった。
すばらしい。InkscapeはFiltersメニューのさまざまな効果を通じて、この種のノイズ追加をサポートしているようだ。Plain SVGとして書き出してコードを見ると、
feTurbulenceを使っていることが確認できる。比較のためにAffinity Designer 2のノイズ塗りつぶしも試してみたが、書き出されたSVGにはノイズがbase64 JPEG画像として埋め込まれていた。
Adobeライセンスを正当化しづらく、InkScapeもしっくりこない立場からすると、こうした変わった点さえ除けばDesignerは完璧な代替だった。
「ApeFest」のWebサイト[1]にも動的ノイズが入っていたのでCSSを見てみた。参加者の網膜をUVで焼いた、まさにあのイベントのサイトだ。
実際にはタイル状のPNGオーバーレイ[2]を使い、CSSアニメーションで「ランダム」っぽく揺らす方式。修正: 実際にはJSで動かしているようだ。かなりローテクだが、動きはする。
[1] https://apefest.com/
[2] https://framerusercontent.com/images/rR6HYXBrMmX4cRpXfXUOvpv...
こういうSVGフィルターを何度も使いたかったが、問題はエンジンごと、拡大/縮小率ごとに表現が大きく変わることにある。
そのため、不正確さがあまり重要でない微妙な効果にしか使えず、結局残念ながら継続的にラスターへ戻ることになる。
またピクセル単位で動作する性質のため、
feDisplacementMapのようなものはアンチエイリアシングを壊し、使いたくなる状況の半分くらいを台無しにしてしまう。Perlinノイズとその後継であるフラクタルノイズは、リアリティや有機的な感じを加えるうえで費用対効果がものすごく高い。
たとえばAladdin (1992)でCave of Wondersから脱出する場面に、とても効果的に使われているのが見られる。
これが本来どう見えるべきなのかは分からないが、自分の携帯ではノイズなしの滑らかなグラデーションだけに見える。
効果は気に入っているが、拡大/縮小率から独立させる方法はあるだろうか?
関連して、これもランダム性を加えているようだが、シードを与える方法があるのかも気になる。たとえば特定のサイズで、1つの正しいレンダリング結果を定義できるとよさそうだ。
seed属性があり、乱数関数のアルゴリズムも仕様で186行のCコードとして定義されているので[0]、理論上は実装ごとに同じ結果になるはずだ。ただしコードを見ると浮動小数点演算を使っていて、これは実装定義だと理解しているので、プラットフォームごとに異なる動作をする可能性はありそうだ。
[0]: https://drafts.fxtf.org/filter-effects/#elementdef-feturbule...