🖼️

Starfishの世界とこれから

2023/08/30に公開

概要

Starfishは、タイル状に敷き詰めて表示するのに都合がよいテクスチャパターンを生成したり、そのテクスチャをデスクトップの壁紙として設定する機能を持ったユーティリティソフトです。ランダムなパラメータに基づく複数の数式を重ね合わせたパターンは多様かつ高品質であり、単なるデスクトップ用の壁紙にとどまらず、SNSのアバターや芸術作品の素材などにも活用できる可能性を秘めています。

Starfishで生成できる画像の例
Starfishで生成できる画像の例

Starfishが生成できるテクスチャは、ランダムに選ばれた5種類の生成器から得られるビットマップを2~6回重ね合わせるというシンプルな仕組みで作られています。この記事では、StarfishのRust移植版であるjelatofishの実装にあたって調査した内容――テクスチャの多様性を支える生成器の詳細や合成手法――について紹介します。

Starfishとその派生ソフトウェア

最初のStarfishがMars Saxmanによってリリースされたのは、1999年のことです。当初はMac OS 8・9向けのユーティリティとしてリリースされ、その後は有志によってMac OS X向けの移植版Windows 98~XP向けの移植版などが提供されてきました。

また、X Window System向けのユーティリティとしてxstarfishに移植され、メンテナンスが続けられています。さらには、OSに依存しないJavaScript移植版も提供されており、この実装を用いたデモページからブラウザで気軽にテクスチャ生成を楽しめます。

実は私も趣味の範囲でjelatofishという名前のRust移植版を作っていて、さらに様々な環境でテクスチャを生成できるようになりました。Rustならwasm-bindgenなどのサポートで簡単にWebAssemblyを出力できるので、こちらもブラウザで動作するデモページを提供しています。ベースの画像から縦横にそれぞれ半周期ずらしたバージョンは、アバターとして使う際によく使う便利機能です。

jelatofishで生成できる画像の例
jelatofishで生成できる画像の例

なお、オリジナルのStarfishはGNU GPL v2でライセンスされており、いずれの派生ソフトウェアもGPL v2またはそれ以降のバージョンでライセンスされています。

Starfishのしくみ

Starfishでは、ランダムなパラメータに基づいてビットマップを生成する数種類の関数の重ね合わせでテクスチャを生成します。具体的な生成器の種類は後述するとして、本節ではビットマップからヒートマップ状の画像を生成して合成する仕組みについて紹介します。

生成器から得られるビットマップは、色を持たないグレースケールの2次元画像と同等です。このビットマップにランダムな2色[1]を割り当てて線形に変化させると、ヒートマップ状のカラフルな画像が得られます。

~ビットマップからカラフルな画像を得る例
ビットマップからカラフルな画像を得る例

このような画像を持つレイヤーを2~6枚ランダムに生成して、さらに特定の規則でピクセルごとに重ね合わせることで、最終的に得られるテクスチャの多様性を高めていきます。このとき重要になるのが、各画像のピクセルごとの強度を示すマスク画像と呼ばれるビットマップと、それらのピクセル強度を相互に合成する計算手法です。

生成器から得られたビットマップは、画像を鮮やかにする2つの色を得るのと同時に、他の画像との豊かな重ね合わせを実現するためにピクセルごとの強度を付与されます。このピクセル強度群は元の生成器と同じ大きさのビットマップであり、Starfishではマスク画像と呼ばれています。

このマスク画像の生成方法は4種類あります。まずは元のビットマップをそのままマスクと見なすもの、異なる生成器[2]から得られたビットマップをマスクとするもので2種類。そして元のビットマップの値を反転させるもの、前述の異なる生成器を反転させるもので4種類です。

~マスク画像の分類

そして、これらのマスク画像について、おおよそ次のようなアルゴリズムで合成を行います。

#!/usr/bin/python3
ALPHA_MAX = 1.0

def get_texture_strength(
    masks: list[float]  # list of float (0...ALPHA_MAX)
) -> list[float]:
    current = []
    for index in range(len(masks)):
        alpha = sum(masks[:index])
        if alpha >= ALPHA_MAX:
            break
        current = [
            *map(lambda x: x * alpha, current),
            1 - alpha
        ]
    return current

このアルゴリズムの特徴は、同じピクセル強度でもレイヤーの順番によって最終的なテクスチャに寄与する強度が変わったり、あるいは同じピクセル強度でも他のレイヤーの強度によって最終的な強度が変化するといった、予測不可能性を高めている点にあります。

例えば、ピクセル強度0.2のレイヤーが5枚重なった場合でも、均等に強度が分配されるわけではありません。全体的により下のレイヤーが弱くなる傾向がありますが、一方で最大のレイヤーは上から2番目のものになるという特異な曲線を描いています(グラフ左上)。レイヤー間の強度が異なる場合でも同様で、概ね下のレイヤーの優先度を下げつつ、ある程度ピクセル強度を反映した結果が得られます(グラフ右側3つ)。

ピクセル強度とレイヤーの関係
ピクセル強度とレイヤーの関係

このような合成手法によって、単に色を足し合わせて平均化するよりも、カラフルでメリハリがある魅力的なテクスチャを得られるのです。以下の画像例では、右上部分のレイヤーがいずれも低い値になっていますが、緑色と青色が均等に混ざるわけではなく、後に足した2枚目の青い背景色がより強く反映されています。また、それ以外の左側や右下の領域については、各レイヤーの前景色が強く出ているのが分かります。

~合成手法の違いによる出力の変化

Starfishを支える生成器

さて、Starfishにおけるテクスチャの合成方法について理解したところで、ここからは具体的な生成器の実装についてまとめていきます。現在Starfishに実装されている生成器は、Bubble・Coswave・Flatwave・Rangefrac・Spinflakeの5種類です。これらはいずれも以下の特徴を持ち、またお互いに異なる印象のパターンを出力できるように調整されています。

  • 外部から与えるパラメータを変化させることで、大きく異なるパターンを生成できる
  • 上辺と下辺、左辺と右辺がそれぞれなめらかに接続するパターンを生成できる

これらの性質を満たす生成器から得られたパターンを重ね合わせることで、デスクトップの壁紙のようなタイル表示に適した多様なテクスチャを得られるというわけです。

Bubble

Bubble

Bubbleは丸い泡のような塊で画面を埋めます。重力で細長く押し潰されているものや、他の塊と共に空間を充填するように膨らんでいるものなどを出力できます。指定できるパラメータは以下の通りです。

  • 泡オブジェクトの個数(8~32個)
  • 各泡オブジェクトの位置(XY座標についてそれぞれ0~1)
  • 各泡オブジェクトの大きさ(0~0.2)
  • 各泡オブジェクトの向き(0~90度)
  • 各泡オブジェクトの変形率(真円に対して0.25~4)

各泡オブジェクトは、中心から離れるほど値が低くなり、境界線とその外側は0になるというぼんやりした泡のような楕円形の像を描きます。泡が重なって表示されないように、各点から最も近い泡オブジェクトの像のみ描画することで、上記の例のような泡同士が接する表現を得られます。また、テクスチャ領域をはみ出した泡オブジェクトも折り返して描画しているため、タイル状に敷き詰めても不自然な像になりません。

このうち、各泡オブジェクトにおける位置を除くパラメータは、全ての泡オブジェクトで共有となる範囲からランダムに決定します。パラメータの範囲が狭ければ各泡オブジェクトは似たような形を描くので、試行の度に大きな変化が生まれやすくなるのです。

~Bubbleにおけるパラメータによる全体像の違い

Coswave

Coswave

Coswaveは、正弦波をベースにした波状のパターンで画面を埋めます。真円に近い形状や滑らかな波形を反映した退屈なテクスチャにならないよう、波形が途中で打ち切られたり歪んだりしたものを出力できるのが特徴です。また、タイル状に配置した際に周囲の領域と連続するように、左側・左上・上側の仮想的なテクスチャと混ぜ合わせています。

Coswaveで指定できるパラメータは以下の通りです。

  • 正弦波の始点(XY座標についてそれぞれ0~1)
  • 正弦波の周期(1~26)
  • 正弦波の変形率(-2.5~-0.5 or 0.5~2.5)
  • 正弦波の変形方向(0~180度)
  • 正弦波の範囲調整方法(4種類のpack methodから選択)
  • 正弦波の歪み率(0.5~2)
  • 正弦波の加速率(1/64の確率で1~3)

上記パラメータのうち、pack methodは以下の4種類から選ばれます。正弦波は-1~1の範囲で生成されるため、ピクセル値である0~1の範囲に収まるようにスケールを調整したり折り返したりする必要があるのです。

  • Scale: 0~1に収まるように均等にスケールします。
  • FlipSign: -1~0の範囲を正の値に折り返します。
  • Truncate: -1~0の範囲に1を加えて正の値に調整します。
  • Slope: 半周期(下り傾斜)のみ利用しつつ均等にスケールします。

~pack methodによる出力の変化

また、歪み率は1から離れるほど、かつ変形率の絶対値が1から離れるほど波形が描く形状を歪めます。逆正接関数の引数を調整しているので、下図の通り45度付近のみ周期が大きく変化するのが分かるでしょう。角度をずらして波形を取得する都合上、変形率が1に近い場合は形状はほとんど変化しません。

~歪み率の変化による出力の変化

そして、加速率が適用された場合は始点から離れるにつれて周期が小さくなります。このような大きな変化がごくたまに(約1.5%)生じることで、さらにテクスチャの不確実性を高めることができます。

加速率が適用された場合の出力例
加速率が適用された場合の出力例

Flatwave

Flatwave

Flatwaveは、複数の波形を重ね合わせた干渉縞のようなパターンを生成します。Coswaveと同様に正弦波をベースにした生成器ですが、Coswaveが1つの波紋から円形に近い出力を得られるのに対して、Flatwaveは2~4個の直線的な波形の組み合わせを利用するものです。Flatwaveで指定できるパラメータについては、階層的で複雑なので完全な列挙は省略します。

Flatwaveにおける波形はCoswaveを直線的に広げたものであり、最も単純なものでは以下のような縞模様を生成します。さらに、50%の確率でCoswaveと同様のパラメータを元に波形を歪ませることで、正弦波のグラフのようなパターンに変換できるのです。基本波形が備える範囲調整方法がどのように出力に影響するかについては、Coswaveの例を参照してください。

~Flatwaveの波形を歪める処理の出力例

また、これらの波形をテクスチャ上でどのように混ぜ合わせるかについて、以下の5種類の手法から1つを選択します。いずれも異なる印象のパターンを生成できるので、最終的なテクスチャに多様性を与えられるでしょう。

  • MostExtreme: 各波形のうち、0または1に最も近いものを採用します。白黒のフレーク状のパターンが得られます。
  • LeastExtreme: 各波形のうち、0.5に最も近いものを採用します。穏やかなパターンになりやすいです。
  • Max: 各波形の最大値を採用します。前景色が強くなりやすいです。
  • Min: 各波形の最小値を採用します。背景色が強くなりやすいです。
  • Average: 各波形の平均値を採用します。各波形が重なって見えます。

~Flatwaveで選択できる干渉手法による出力の変化

Rangefrac

Rangefrac

Rangefracは、山の稜線や針葉樹のシルエットのようなフラクタル状のパターンを生成します。ここまでは球面や正弦波をベースにした比較的滑らかな図形が得られる生成器を紹介してきましたが、こちらはテクスチャにより細かく複雑な印象を与えることができます。

Rangefracは独自の走査アルゴリズムに基づいて各点の値を逐次的に生成するので、前述したように画像全体の出力を決める分かりやすいパラメータはありません[3]。具体的には、周囲のピクセル値の範囲に収まるようにランダムな値を生成する "拡大" 手法[4]でビットマップを縦横2倍のサイズにする処理を、必要な回数だけ繰り返します。

Rangefracによって8回の仮想的な拡大処理を行った例
Rangefracによって8回の仮想的な拡大処理を行った例

このように、Rangefracは数学的な曲線に基づかないパターンであり、生成時のパラメータとして走査回数の指定が必要です。1回で2ピクセル、2回で4ピクセル、4回で16ピクセルのテクスチャが得られます。Starfishはデフォルトで一辺が256ピクセルのテクスチャを生成するので、それに合わせて8回(2^8=256)の走査を行っています。これを超えるサイズのテクスチャが必要な場合は、線形補間などで適宜拡大しなければなりません。

Spinflake

Spinflake

Spinflakeは、木目や水流を思わせる比較的変化が大きいパターンで画面を埋めます。正弦波から細かく複雑な図形を生成しているという点で、Rangefracとそれ以外の数学的な曲線ベースの生成器の中間的な性能を持っているといえます。

こちらもパラメータが少し複雑なので、解説は省略します。

Starfishのつかいかた

本来のStarfishは、日々のデスクトップの壁紙に彩りを与えるために作られたユーティリティソフトです。しかし、このように多様かつ高品質なテクスチャを生成できるエンジンは、さらに多くのシーンで活躍できる可能性を秘めています。

壁紙として

まずは、本来の目的である壁紙としての利用を試しましょう。各プラットフォームで動作するStarfishがあればそちらから、またはjelatofishのブラウザ版からデスクトップの壁紙を設定できます。

Windowsでタイル状に壁紙を設定したイメージ
Windowsでタイル状に壁紙を設定したイメージ

ここまで述べてきたとおり、Starfishで生成できるテクスチャは上下左右が滑らかに接続されるため、画面サイズや比率によらずデスクトップ全体が華やかなパターンで埋め尽くされます。毎日、毎時あるいは毎分新しいテクスチャを生成しても異なるパターンを楽しめるので、スタートアップやスケジューラで自動的に更新してもよいでしょう。

なお、jelatofishでは現状コマンドラインオプションを実装していません[5]が、簡単なインターフェースを書けばいつでも自由に組み込むことができます。

SNSのアバターとして

次に紹介するのは、テクスチャを敷き詰めずに1枚のサイズで活用する方法です。xstarfishでは直接壁紙を変更せずに画像ファイルを書き出すオプション -o が用意されていますし、jelatofishのブラウザ版や簡易なコマンドライン版も利用できます。

Starfishの華やかなテクスチャは、SNSなどのアバターとして設定するのにも向いています。ただし、1枚のサイズで切り抜く場合はどの領域を選ぶかによく注意しなければなりません。

~テクスチャを切り抜く周期と印象の変化

上記の例では、元のテクスチャのまま切り出したものに対して、テクスチャを4分割して入れ替えた――周期を半分ずらした――ものでは、ぼんやりと魚のように見える緑色の領域が生まれています。これは、タイル状に敷き詰める場合は全く問題になりませんが、1枚のみ使用する場合(特に丸く切り抜かれるケースで)では大きく印象が変わります。

jelatofishのブラウザ版では、このようなケースに対応するために、X軸とY軸およびその両方について半周期ずつずらしたバージョンを保存できるようにしています。同様の機能をコマンドライン版でも実装予定です。

その他いろいろな素材として

さらに、デスクトップの壁紙と同じように、本の表紙、ステッカー、Tシャツ、ハンカチ、クッション――様々な場所でテクスチャを敷き詰めた背景素材として利用することもできます。

Starfishテクスチャ(やさい)を用いたクッションのイメージ
Starfishテクスチャ(やさい)

Starfishテクスチャ(なみ)を用いたタオルハンカチのイメージ
Starfishテクスチャ(なみ)

Starfishテクスチャ(えび)を用いたTシャツのイメージ
Starfishテクスチャ(えび)

なお、Starfishがデフォルトで生成する一辺が256ピクセルのテクスチャは、300~350dpiでせいぜい2cm程度の範囲しか覆うことができません。より広く複雑なパターンが必要な場合は、テクスチャ自体のサイズを大きくする必要があります。jelatofishではまだ対応していませんが、xstarfishでは -g オプションで生成するテクスチャの大きさを指定できます[6]

まとめ

  • Starfishはデスクトップの壁紙としてタイル状に敷き詰めやすいテクスチャを生成するユーティリティソフトであり、いろいろな言語・環境に移植されています。
  • Starfishでは、多様かつ高品質なテクスチャを出力するために、5種類の生成器から得られる2~6つのパターンを独自のアルゴリズムで混合しています。
  • Starfishは本来デスクトップを彩るためのユーティリティですが、そのテクスチャはアバターや背景素材として利用できる多くの可能性を秘めています。

転載元の「Starfishの世界とこれから」はCC-BY 4.0(https://creativecommons.org/licenses/by/4.0/)でライセンスされているため、この記事についても同じライセンスが適用されます。

脚注
  1. Starfish内ではbackground / foregroundと呼ばれています。 ↩︎

  2. 異なるパラメータの同じ生成器を含む。 ↩︎

  3. 疑似乱数生成器のシードをパラメータとすることはできます。 ↩︎

  4. 周囲の値に収まる範囲から取り出す点で拡大処理に似ていますが、通常の線形補間やバイキュービック法とは異なる非決定的かつ限定的な操作です。何もないところから複雑なパターンを見出す点で、むしろ拡散モデルなどと似ているかもしれません。 ↩︎

  5. オプションなしで実行するとカレントディレクトリに image.png を保存します。 ↩︎

  6. ただし、Rangefracは一辺が256ピクセル程度の情報しか持っていないので、Rangefracを含んだ大きなテクスチャを生成すると像がぼやけます。 ↩︎

Discussion