2Dのメタボール(Metaball)理解した

2Dのメタボール(metaball)について理解した。

メタボールっつーのは何かと言うとこれです。写経したやつだけど。
(PCで見てくれよな!)

See the Pen metaball canvas by bom_phage (@bom_phage) on CodePen.

メタボールというか、こういう滑らかにくっつく表現やりたいなーと思っていて、調べてたけどどうにもよくわからないでいた。特によく出てくるのはSVG filter使うやつ。liquid みたいな言葉で検索するとよく出てくるけど、とにかくパフォーマンスが悪い。ほんでSVG filterに対する知識があんまりないので、???みたいな気持ちになってた。

ちょっと間忘れてたけど、思い出してふとやってみるかということになって写経したところ、ばっちり理解した。

原理

上述のcodepenのやつだと若干実装が違うんだけど、ポイントは

  • 不透明度の円形グラデーションを複数用意する(中心は不透明度が高く、外側に行くにつれて透明)
  • ある値(閾値)以下の不透明度の場合は不透明度を0にする(透明にする)

の2つ。これをやるとメタボールみたいに境界がぬるっとした感じの描画になる。
例えば、中心から外側にrgba(0, 0, 0, 1) => rgba(0, 0, 0, 0)というグラデを作って、描画時に不透明度0.95以下の部分は rgba(0, 0, 0, 0)にしちゃう感じ。 そうすると不透明度1 〜 0.95の部分だけの円ができる。 これが複数個集まると、重なる部分は不透明度が足し算されて、円と円の間の部分に不透明度0.95を超える部分が出てきて境界が滑らかなる。 => メタボール!という感じ:sun_with_face:

まぁそれはそうだがそれはどんな風に実装すんねんっていうのを下に書いとく。
実際触ると一目瞭然だと思うし、canvascssfilterで実装したやつを用意している。

実装

canvas

canvasの場合は、表示用と描画用の二種類のcanvasが必要になる。描画用の方にradialGradientでぼかした円を描画して、その内容を表示用のカンバスにコピーする。そのときに不透明度が閾値より低い部分は透明にすることで、メタボールが描画できる。
この実装ではTHRESHOLDという定数が閾値(初期値210※1)になってるのでここを10とかにしてもらうとボケた円がでてくる。
※1 canvasの場合透明度の範囲も0〜255

See the Pen metaball canvas2 by bom_phage (@bom_phage) on CodePen.

◆前述の実装との違い

実装見てもらったら話は早いけど、描画用のcanvasからgetImageDataで全ピクセルの情報を取得している。その時、ピクセルのデータは1pxずつrgbaという順番で配列に格納されている。
前述の実装の場合はコピー時にその全データに対して、閾値より低い場合に0を与えている。これだとrgb閾値以上でないといけなくなってしまう。それだと色に限りがでてしまって困る。
そういうわけでaの値のみ評価するようにしたのが後述の方。そっちのがforが回る回数も少なくて済むし、いろんな色がいけるぞ。

CSS filter

cssでもやってること一緒。円にblurをかけて、描画領域のcontrastを上げることで、不透明度の低い(コントラストが低い)部分を飛ばしている感じです。でもCSSだとcontrastをあげる都合上、パキパキしたのしかできないし、色の自由度が著しく低いと思う(解決方法がわからん)。
しかもcssfilterめっちゃ重い!!

See the Pen metaball by bom_phage (@bom_phage) on CodePen.


はい

そういうわけで実装するならcanvasでやりましょう!原理がわかれば無限に応用効くと思うし、いろいろできそうでワクワクするぞ!!
3Dも理解できるように頑張るぞ!

See the Pen metaball particles by bom_phage (@bom_phage) on CodePen.