
2026/06/14 11:14
Show HN: インクウォッシュ - 水彩スケッチングアプリと解説ガイド
RSS: https://news.ycombinator.com/rss
要約▶
Japanese Translation:
このテキストは、AI モデルによって完全に生成され、一切の手動コーディングを必要としない画期的な単一ファイル WebGL2 アートアプリケーション「Inkwash」を紹介する。デスクトップおよびモバイルデバイスの両方で動作し、湿り具合、速度、色の重なりなどの物理的屬性をモデル化して高度な GLSL シェーダーを用いてリアルなインクの流体ダイナミクスをシミュレートする。
このリアリズムを実現するため、システムは数学的に正確な圧縮不可能流体運動のために Jos Stam の Stable Fluids アルゴリズムを採用し、渦度拘束(vorticity confinement)で強化されている。視覚的信頼性はさらに、自然な光吸収効果を 위해 Beer–Lambert 法を組み合わせ、繊維ノイズや湿った光沢を含むプロシージャルなペーパーテクスチャとの併用によって向上している。ユーザーはコードを書かずにタッチジェスチャーやキーボードのショートカットという単純な操作を通じて、インクが 2〜18 秒で指数関数的に乾燥するという複雑な挙動や、下地を漂白するために白色のアクリル絵具(gouache)を適用するなどの制御を行う。驚くべきことに、このアプリは速度計算のために効率的な粗い格子ソルバーを活用することで、スマートフォン上でも 60 フレーム毎秒の高いパフォーマンスを維持している。このプロジェクトは、高度で高忠実度の物理シミュレーションが今や完全に AI 生成によって作成可能となり、深い技術知識や複雑なセットアップを必要とせず、すべての人にプロフェッショナルグレードのデジタルペイントを実現することを示している。
本文
01 インスピレーション:ナチュアジャーナリングと AI アシスタント
著者は「ナチュアジャーナリング」という活動を通じて、スクッチ速報に適したスタイルを確立しています。パイロット G2 ペンとウォーターブラシを組み合わせた使い方を考案し、同時に線画と陰影を描く手法を実現しました。これにより、最終結果への過度な執着が不要になり、ペンのUndo 機能がないという制約を受け入れ、「不可避免地な汚れや不整」を表現の幅として許容するようになりました。
プロジェクトの経緯
- 始まり: Anthropic の新モデル「Claude Fable 5」を試すことから始まりました。
- 発展: ブラウザ上で実際にその体験を再現し、Anthropic(注:原文は AI アシスタントとの対話を指唆)による生成デモやコンセプトの可視化に成功しました。
- 成果: コードに触れずに作成したにもかかわらず、技術的な知識があることでコード読解が可能で、単一 HTML ファイルという整然とした構造を実現しています。
- 免責事項: 記事の後半は AI の記述によって構成されていますが、著者は AI 生成に一定の評価を持ち、このアプローチの成功を認めています。
02 シミュレーションエンジン:三つの状態シート
キャンバスの下には、画素ではなく小さな浮動小数点テクスチャのスタック(重ねられた透明なシート)が配置されており、約一桁の WebGL2 フラグメントシェーダー で各フレームが処理されます。
5 つのフィールド定義
各シミュレーションレイヤーは以下の役割を持ちます:
| フィールド名 | レゾリューション (解像度) | 意味と機能 |
|---|---|---|
| Ink RGBA16F | Max 2048 px | 光学密度(光の吸収量)と Alpha(ホワイトガッシュ/不透明な白)。画面解像度に合わせるため、詳細な線画表現が可能。 |
| Fixed RGBA16F | Max 2048 px | 「決着したインク」。一旦移動しなくなった色素が保存される層(参照:セクション 07)。 |
| Wet R16F | Max 2048 px | 「水の量」。インクの拡散・滲み範囲を管理。 |
| Velocity RG16F | ~256 cells | 「流れの速度」。意図的に粗いグリッドで設定され、計算コストを削減しつつ滑らかな流体挙動を表現。 |
| Pressure R16F | ~256 cells | 「圧力(計算用メモリ)」。液体が不可圧縮であることを保つためのソルバー用空間。 |
パイプラインの仕組み
- ストロークエンジン: ガウシアンスプライトスタンプをフィールドに追加(参照:セクション 05)。
- シミュレーション: 水流と粘度の計算を進化させる(参照:セクション 03-04)。
- 表示シェーダー: インク密度を紙の色に変換して描画する(参照:セクション 08)。
重要な技術的トリック:
- コスト最適化: 滑らかな流体運動に最適化された粗い速度フィールド(256 セル)を使用し、高価な計算を行いながら低解像度で処理します。
- サンプリング: ぼやけた安価なフローフィールドをサンプリングし、その動きをシャープで高価なインクフィールドに反映させます。
03 動く水:半ラグランジアンアドベクション
流れのシミュレーションは、Jos Stam のアルゴリズム「安定流体(Stable Fluids, 1999)」に基づいています。この名前の由来は、流体を押し出すのではなく、「引き込む(逆方向に追跡する)」ことで安定性を保つ点にあります。
アルゴリズムの原理
- 幼稚なシミュレーション: 各流体パケットを速度に従って前方へ移動させると、グリッド境界を超えた際に「爆発」して不自然な挙動になります。
- Stam の逆方向追跡(半ラグランジアンアドベクション):
- 現在のセルから、1 時間ステップ前はどこにあったかを速度に従って逆方向に追跡します。
- その位置の古いフィールド値をサンプリングし(四点双一次補間)、現在地に持ち帰ります。
- 結果: オーバーシュートが発生せず、大きな時間ステップや低フレームレートでも爆発しません。
// GLSL による一連の操作(2 行で実装) vec2 coord = vUv - uDt * texture(uVelocity, vUv).xy * uTexel; // 逆方向の座標計算 vec2 vel = texture(uVelocity, coord).xy * uDissipation; // 速度と減衰を適用
シミュレーションの精製(2 パス追加)
アドベクションだけでは粘性のあるシロップ状になります。水として振る舞わせるために以下の処理を加えます:
- 圧力投影: 水を不可圧縮にします。
- 速度フィールドに発散(押し出す場所)や収束(引き裂かれる場所)があった場合、ソルバーが圧力場を計算し速度から圧力勾配を引き算します。
- これにより、スプレーではなく渦や巻き目などの自然な流路が形成されます(約 22 Jacobi 反復で緩和処理を行います)。
- 渦度拘束(Vorticity confinement):
- 数値的な不具合による「 mush(曖昧さ)」を防ぎます。ローパスフィルタ効果により小さな竜巻が消えるのを防ぎ、残存する旋回を測定して回転させる力を加えます。
04 決める紙:湿潤フィールドのゲート
単体で働く流体は永遠に漂うスモークのように振る舞います。インクワッシュのような「紙」らしさを出すためには、湿潤フィールド全体に対する許可システムが必要です。
三つの制限ゲート
すべての動作は小さなテクスチャ(湿り具合)を読み込んで制御されます:
- 速度の制限:
velocity *= smoothstep(0.005, 0.2, wet)- 速度は湿った紙のみに適用されます。乾いた地面には流れることは許されません。
- 色素の移動性(Earned Mobility):
で計算される可動性をインクに付与します。mob = smoothstep(0.02, 0.45, wet)- 適度な湿り気: インクが這います。
- 濡れすぎ: インクが流れます(水彩画)。
- 完全乾燥: パンチの美術館のように固定され、シェーダーコストを削減。
- 水の蒸発:
- 湿潤フィールドは時間とともに指数関数的に減少します(約 2 秒〜18 秒で設定可能)。
- 乾燥化とは「流体シミュレーションを絵画に変える」プロセスであり、洗浄が閉じる窓(一時的な状態)を意味します。
特徴: 水は移動する間に色素を持ち去りません。「水彩画」として認識されるテクスチャは、フローフィールドの最後の状態が凍結されたものです。
05 マークを作る:ストロークエンジン
ペンとフィールドの間には小さなストロークエンジンがあり、ガウシアンスプライト(
exp(-d²/r²))という一つのプリミティブで描画を表現します。
ストロークの特性
- インク: 加算的ブレンドを使用。密度が蓄積し、実際の釉薬のような深みが出ます。
- 水: MAX ブレンド(飽和)を使用。同じ場所にこすっても紙は濡れるだけで無限に膨らむことはありません。
ハンドデータの整形
ストロークの生成プロセスには以下の調整が行われます:
- 圧力と速度のマッピング:
- ペン:半径・密度はスタイラス圧力で増加、速さで減少します(素早いフリック=細く乾いたライン)。
- Force Touch(トラックパッド)やマウスでは、物理的な圧力をシミュレートするアルゴリズムを使用。
- カーソルの追従遅延:
- ブラシ位置はポインタへ指数関数的に緩和されます(
)。k = 1 - exp(-14·dt) - この数ミリ秒の遅れにより、ジッターが吸収され、角が丸くなり、ストロークに生命感が出ます。
- ブラシ位置はポインタへ指数関数的に緩和されます(
- 静止時のインク供給:
- ペンやブラシを留めると、インクはスポット状に溜まり(Bloom)、湿った紙上のノブのように振る舞います。
06 黒ではないもの:オンデマンドクロマトグラフィー
安価な黒インクに水滴を落とし、乾燥させる過程で現れる藍紫色の幽霊は、染料が分離する現象です。Inkwash はこの自然な物理法則を活用しています。
染色の分離(化学的プロセス)
- 黒インクは多成分の「染料カクテル」です。湿った紙上で、各染料は異なる速度で移動します。
- シアン系(赤光を吸収する):最も速く外部へ拡散。
- 藍色系(藍光を吸収する):ライン内に残りやすいため、暗いコアやクールなハローが形成されます。
// グローバル染色パラメータの調整 (C = 0 はランプブラック振る舞い) vec4 bleedAmt = clamp(uBleed * (0.25 + 1.3*brush) * mob * vec4(uChroma, 1.05), 0., 0.92); vec4 mixed = mix(advected, neighborhood, bleedAmt); // 近傍平均とのブレンド
ブラシの役割
- ブラシは単に濡らすだけでなく、染料を解きほぐす作用を持ちます。
- 「こすること」が滲みプロセスを加速させ、化学的な分離反応を促進します。
07 押して固定する:レイヤー化と白インク
単一フィールドでは「乾燥前の層」と「乾燥後の層」を重ねることはできません。そのため、移動中の
ink フィールドと定着した fixed フィールドの二重構造を採用します。
Fix(定着)プロセス
- 操作方法: 画面右下のボタンまたはキー
を押すと実行されます。[d] - 効果: 1.2 秒間かけて移動中のインクを固定層へ転移させ、速度フィールドは停止、湿潤フィールドは瞬間乾燥します。これにより「液体から絵画への変化」が完了します。
白インクとガッシュ論理
白インクは「破壊的な」と言えるほど不透明なガッシュのような挙動を模倣しています:
- 固定時: 白いガッシュのCoverage は、下の暗いインクの密度(色)を漂白(除去)します。
- 描画順序: 暗く描き、上から白を塗り、さらに上から暗く描くと「暗い場所」が認識されます。
- なぜなら、物理的には新しい白い地面(紙)の上に描いているためです。
// 白インクの漂白処理:隠れたインクを除去し、密度を変換 float c = (1.0 - exp(-2.2 * whiteAmount)) * uSettle; // カバー率の計算 vec3 T = exp(-density); // 現在の透過率 density = -log(clamp(T * (1.0 - c) + c, 1e-4, 1.0)); // 新密度の計算
08 紙を描く:物理法則に基づくレンダリング
これまでの工程は「密度の世界」での帳尻合わせでしたが、表示シェーダーでは絵画の物理法則が適用されます。
ビール=ランベルトの法則(Beer-Lambert)
光が色素を通過する際の指数関数的減衰を用います。これにより、単純なオーバーラップではなく、奥行きのある色重ねが可能になります。
// 透過率の計算 vec3 color = paper * exp(-density * uInkStrength);
表示パスでの付加要素
画像からサンプリングされたものではない、以下の効果を追加して「紙らしさ」を演出します:
- 繊維と歯: 二オクターブのノイズ(FBM)で紙の凹凸や織り目表現。
- 顆粒化: 色素がある部分にだけ適用されるノイズ。粒子が谷間に付着したような質感。
- 縁の濃化: 勾配が大きい場所(境界線)で吸収を強化し、乾燥水彩画のような輪郭強調を実現。
- 湿り輝き: 湿っている部分は少し暗く冷たい色調にし、乾燥していない状態を視覚的に表現。
09 それで描く方法:モードと入力
インストルメントは「ライン+洗浄」「湿った上での湿り」「暗い上での白」といった**3 つのIDIOM(idiomatic な表現)**を備えています。
代表的な技法
- クラシックなジャーナリング: まず線を描き、その後内部を洗浄で彩ります(青ハローのような陰影を作る)。
- ウェット-on-Wet(湿った上での湿り): まず清潔な水で紙を湿らせ、後にインクを落とします。インクは境界から外側へ Bloom して広がります。
- ホワイト・アール(White on Dark): 暗い背景に固定され、白インクを描いて星のような点を形成します。
入力マッピング
デバイスに応じて適切なメタファーが適用されます:
| デバイス | 操作内容 | 特徴 |
|---|---|---|
| iPad | Apple Pencil で線画、指で水ブラシ | 最もありったこと(真実の版)。ストロークは単一ポインタなのでパームレストを無視。 |
| Android タブレット | スタイルバレルボタン切換 | ペンとブラシを手動で切り替え可能。 |
| Mac/トラックパッド | Force Touch 圧力制御 | 物理的な圧力でライン幅を調整。 |
| キーボード | ショートカット | (ブラシ), (白インク), (固定), (クリア), (保存), (全画面)。 |
10 コロフォン
技術的特徴
- 単一 HTML ファイル: 千行未満、依存関係なし、ビルド不要。
- 要件: WebGL2 とハーフフローテクスチャ(
)が必要です。EXT_color_buffer_float - パフォーマンス: フルパイプライン(12 パスのシェーダー処理)でも、スマートフォンで快適な 60 fps が保証されています。
- マルチインスタンス化: 画面のデモは単一の WebGL コンテキストを共有し、各デモが独立したフィールドスタックを持っています。
開発背景と展望
- 最初の試みとしてスライム・モールド・シミュレーター「dotswarm」を作りましたが、今回さらに深い技術的対話とアイデアの絞り込みを通じて、「生涯で最も愛するソフトウェアの一つ」を完成させました。
- 流体シミュレーションは既知の技術ですが、このような奇跡的な個人ソフトウェアが広く利用可能なことを嬉しく思います。
メッセージ: 「アプリで描画し、ソースコードをご覧ください。存在を望むアイデアバックログを考え直してください。今作れる可能性があります!」
PS: テスト過程で行ったスケッチの一部(成功または失敗した形)を公開しています。Twitter/X の
@johnowhitaker でお知らせください。