Communitygithub.com

pisakhov/x-motion-sumi

墨絵のような映像表現を実現するためのテーマ(紙、炭素インク、水、霧、息、一点の朱)。

x-motion-sumi とは?

このスキルは、映像作品を墨絵(水墨画)のように見せるためのアートディレクションと技術レシピを提供します。紙やインクの物理的な挙動(にじみ、かすれ、乾き、吸収)をシミュレートし、静寂と一瞬の動きのコントラストを強調します。対応プラットフォームは特定されていませんが、WebGLやWeb Audio APIを用いた実装を想定しており、x-motionスキルと組み合わせて使用します。インクの吸収をBeer-Lambert則でモデル化し、ブラシストロークは極座標フィールド、水中のインクは逆移流で表現するなど、高度なシェーダー技術を駆使します。また、音響面でも、素材としての息や水、楽器としての笛や撥弦を、最小限かつ精密に配置する方法を定義しています。ピクセル単位での決定論的なレンダリングを重視し、乱数の管理やミキシングのビット精度にも言及している点が特徴です。

対応~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/pisakhov/x-motion/tree/HEAD/skills/x-motion-sumi

お気に入りのAIに質問する

このエージェントスキルを事前に読み込んだ状態で新しいチャットを開きます。

ドキュメント

X Motion: Sumi

インク、紙、息。このテーマは、水墨画の物質的な論理を時間軸に変換します:多くのグレーに薄められた炭素インク、温かみのある繊維紙、にじむ濡れたエッジ、かすれる乾いた筆、そして主要な素材としての余白。これは x-motion を拡張します。最初にそのメソッド(概要、論文、ストーリーボード、批評ループ)を実行し、アートディレクションとレシピにこのファイルを使用してください。

ビジュアルテーゼ

インクが本来の振る舞い(拡散、にじみ、かすれ)をしながら、静かな紙の広いフィールド上で、一つのジェスチャーを必然的に感じさせるように。そうすることで、視聴者は決定的な瞬間の前後の静けさを感じることができる。

  • 一拍につき一つのジェスチャー。 一滴、一筆、一稜、一羽。決して二つの焦点イベントを同時に起こさない。
  • 余白は重みを持つ。 フレームの60から80パーセントは、手つかずの紙、霧、または水のままにする。空のスペースは構成されたものであり、余り物ではない。
  • イラストではなく、物質の真実。 外観は、インクが光を吸収し紙がインクを吸収する様子をシミュレートすることで得られる。輪郭線やベタ塗りからは生まれない。
  • 敬意と独創性。 書道、疑似文字、実在または架空の文字、衣装、寺院や提灯の小道具、太陽と鶴のエンブレムは使用しない。印は抽象的な幾何学模様とする。

パレット

リニアライトで作業すること。以下はsRGBの16進数アンカーである。

役割16進数備考
紙(明部)#f1e9d8暖かい和紙。決して純白ではない
紙(陰影)#e0d3baビネット、光の折り目、まだら模様
紙(深部)#cbbb9cフレーム端または霧の影の中でのみ使用
インク、最濃#14120f暖かいカーボン。決して #000 ではない
インク、中淡#5d5b57暖かい紙に対してわずかに冷たい
インク、淡い#a9a69f遠くの山、拡散した雲
朱色#c8372d唯一の色彩。最大でも一つのマーク、最大でもピクセルの約1.5パーセント
霞の中の朱色#d7735f大気によって紙の色に混ざった朱色

インクは塗る色ではなく、吸収である。Beer-Lambert則で合成する: rgb = paper * exp(-density * absorb)。ここで absorb は青成分がわずかに大きい(例:vec3(1.0, 1.04, 1.1))。これにより、淡いウォッシュはかすかに冷たく、濃いインクは暖かく黒く見える。

光と素材

  • 紙 は片側から柔らかく照らされる:低周波のまだら模様(プラスマイナス3パーセント)、目に見えるコウゾ繊維(細長い半透明の繊維、幅1~2ピクセル)、そして乾いたインクが引っかかる微細な歯(表面の粗さ)。一度ベイクして固定する。
  • 濡れたインク は、顔料が端に移動した暗い縁、繊維への羽状のにじみ、そして乾くにつれて消えるかすかな光沢を持つ。
  • 乾いたインク は、平行な筆の筋に分かれ、紙の低い部分をスキップする(歯をしきい値処理する)。
  • 水中のインク は、バックライトで照らされたボリュームである:密度の高いコア、エッジから見える層流シート(細い暗い線)、そしてその周りの柔らかなヴェール。
  • 大気 は紙の色である。距離と霧は値を紙の方向に持ち上げ、決して青や白にはしない。

構成

  • 被写体を非対称の三分割に配置し、一つの大きな静かな領域(上部または片側)をカウンターウェイトとして残す。
  • 明確な値の階層を保つ:一つの黒に近い色、いくつかの中間グレー、残りは紙の色。
  • フレームの端は静かに保つ。 #e0d3ba に向かうソフトなビネットで視界を内部に留める。
  • 朱色のアクセントは、主要な塊から遠く、小さく、低く、遅く配置する。それはジェスチャーに応答するものであり、競合するものではない。
  • サムネイルを確認する:一つのジェスチャーのシルエットは、幅320ピクセルで判読可能でなければならない。

動きの語彙とタイミング

動詞特性典型的なカーブ
呼吸(ホールド)光か霧以外は動かないジェスチャーの前に0.8~1.5秒の静止
落ちる/打つ決定的、速い、オーバーシュートなしease-in、0.3~0.6秒
描く筆は加速してから離れるゆっくり押し、速い中間、軽い持ち上げ
開花/拡散速く、その後指数関数的に減速1 - exp(-t / tau)、tau 1~2秒
にじむ/落ち着くイベント後の微小な成長接触からの時間の sqrt、上限あり
漂流カメラまたは霧、一定で遅いリニアまたは非常に長いイーズ、毎秒フレームの2パーセント未満
溶解コントラストが低下、エッジが広がる最後に2~3秒、決して黒へのカットではない
  • リズムは呼吸である:静寂、一つの決定的なイベント、長い沈静。その沈静に新しいイベントを詰め込まない。
  • カメラはジェスチャーを追うためだけに動く(インクと共に沈む、霧の中を滑る)。プッシュインは8パーセント未満に抑える。
  • シードとノイズフィールドはゆっくりと進化させる:フィールドオフセットは毎秒0.05単位未満。それ以上だと沸騰しているように見える。

サウンドパレットとミックス

サウンドは映像と同じくらいまばらである。静寂は合図である。

  • 素材: 息(バンドパスされたノイズ、ゆっくりとしたフィルタースイープ)、水(水滴のための上昇するサイン波チャープ、こもった泡)、紙と筆(バンドパスされたノイズ、筆が乾くにつれて増加するまばらな粒子のクリック音)、風(二つの無相関のノイズベッド、シードされた突風カーブ付き)。
  • 楽器: 息の混じったエンドブロウンフルート(サイン波に少しの第2倍音と第3倍音、ピッチは下からスライドアップ、息ノイズは音符にバンドロック)、撥弦(Karplus-Strongでバッファにレンダリング、オプションで撥弦後のピッチスライド)、低いゴング(非倍音の部分音とビートペア、低い部分音が最も長く鳴る)。
  • ハーモニー: 開放的なインターバル(4度、5度、9度)の数音。既成の「東洋的」な五音階リフは避け、単音を語らせる。
  • 空間: 一つの長い合成リバーブ(3~5秒、暗くなるテール、20~40msのプリディレイ)。楽器はウェット、テクスチャーはドライ。
  • 同期: 水滴の音は衝撃に合わせる。息はジェスチャーより0.3~0.8秒先行させる。ゴングは印に合わせる。旋律音は視覚イベントより0.1~0.3秒遅らせる。
  • ターゲット: 約-18 LUFS(統合)、トゥルーピークは-1.5 dBFS以下、テールは最終フレームで-60 dBFS未満、クリック音なし(すべてのゲイン変更は少なくとも5msかけてランプする)。

テクニックレシピ

1. ノイズと紙を一度ベイクする。 タイル可能な256x256 RGBA16Fテクスチャをアップロードする:RGはカールフィールド(周期勾配ノイズの回転勾配)を保持し、BAは二つの独立したノイズを保持する。紙をシード付きJS(グレイン+Canvas 2D繊維ストローク)でRGBA8テクスチャにミップマップ付きでベイクし、フレームごとにfbmを計算する代わりに毎フレームサンプリングする。

2. インク合成(リニアライト)。

vec3 lin(vec3 c){ return pow(c, vec3(2.2)); }
vec3 inkOver(vec3 paperLin, float density){
  return paperLin * exp(-density * vec3(3.4, 3.55, 3.75));
}

3. 逆移流による水中インク。 シミュレーションは行わない。インクをいくつかの解析的なプリミティブ(滴、茎、渦輪、花びらローブ)で記述し、その位置は時間の閉じた関数とし、ワープした座標で評価する。ワープはカールフィールドに沿った数ステップであり、振幅は衝撃からの時間とともに増大し、プリミティブをカールする巻きひげに引き伸ばす。

vec2 q = p;
for (int i = 0; i < 4; i++) {
  vec2 c = texture(uNoise, q * freq[i] + drift * t).rg;   // カールフィールド
  q += amp[i] * growth * c;
}
float dens = primitives(q, t);              // ガウシアンコア+広いヴェール
dens += sheet(q) * veil;                     // リッジノイズ:エッジから見えるシート
  • ワープは低周波に保つ(ベーススケールはフレーム高あたり1~3サイクル、弱い第3オクターブ)。高周波ワープはインクをうごめく虫にしてしまう。
  • 巻き上がりは、局所的なスワール(ガウシアンで減衰する角度で各渦コアの周りに q を回転させる)から生じる。ノイズからではない。
  • 花びら、ローブ、ヴェールは柔らかい縁(半径の0.2~0.3倍の幅のガウシアン)を必要とする。形状内部の細く暗い等高線は、地形図のように読める。
  • シルエットを判断し、詳細を判断しない:放射状の5つのローブ形状を正面から見ると、クラゲやヒトデのように見える。花のように見える前に傾ける(カメラが約30度見下ろすように3Dローブを投影する)。
  • 細く、鋭く、不透明なままの軌跡は、紐や茎のように見える。それを航跡として扱う:広げ、柔らかくし、カールさせ、約1秒以内に(先端から先に)フェードアウトさせる。そうすることで、形状が単独で成立する。
  • インクのコアは暖色係数(赤より青が多い、例えば vec3(3.2, 3.6, 4.2))で、薄まったエッジはニュートラルな係数で吸収する。そうすると、コアはフラットなグレーではなく、墨黒として読める。

4. 極座標フィールドとしての筆ストローク。 円相(えんそう)には r(theta) = R + wobble(theta) を使用する。ピクセルごとに角度を取り、ストローク進行 u とストロークを横切る符号付き距離 d / width(u) に変換する。u < brushHead(t) の場所を描画する。濡れ、幅、乾きは u の関数であり、にじみは t - timeTheBrushPassed(u) の関数である。

float lanes = texture(uNoise, vec2(across * 5.0, u * 0.35)).b;   // 筆の筋
float dryInk = step(dryness(u), lanes * 0.6 + tooth * 0.5);         // 紙の低い部分をスキップ
float rim = smoothstep(0.75, 1.0, abs(across)) * wet(u);            // エッジの暗色化

5. 風景のためのレイヤード深度。 各尾根は、深度 z における1D高さプロファイルである。ピクセルごとに、ワールドxに投影し、高さをルックアップし、その高さのパースペクティブ下でのスクリーンyと比較する。インクは尾根線で最も強く、下に向かって霧の中に減衰する。遠くから近くへ合成し、1 - exp(-depth * k) で紙の色に向かって混ぜる。フォグシートは尾根間の谷間に位置し、時間とともにドリフトする。カメラが尾根を通過しようとしているときは、その尾根をフェードアウトさせる。

  • 透過率を用いて近くから遠くへ合成する(acc += T * a * layer; T *= 1.0 - a;)。T がほぼゼロになったらブレークする。これは正確であり、下のフレームが1~2層で停止するためコストが半分になる。
  • 尾根線は解析的な1D勾配ノイズ(ハッシュと5次フェード)で計算し、テクスチャルックアップは使用しない。バイリニアテクセルは長い尾根線にキンクとして現れる。
  • 大気は、最も遠い範囲がもう1つのグレーではなく薄い幽霊として読めるように、急峻なカーブ(exp(-0.45 * depth))を必要とする。

6. 小さなシルエット(鳥、印)は、バウンディングボックステスト内のSDFカプセルとボックスとして、1.5ピクセルのアンチエイリアスエッジで表現する。翼は物理ではなく位相関数でアニメーション化する。

7. オーディオビルディングブロック。

// Karplus-Strong 撥弦、オプションのグライド付き、決定論的にバッファにレンダリング
for (let n = 0; n < len; n++) {
  const d = sr / freqAt(n) - 0.5, r = w - d;
  const a = read(r), b = read(r - 1);
  y = exc[n] + g * 0.5 * (a + b); write(y); out[n] = y;
}

リバーブインパルス:シードされたステレオノイズに exp(-t / decay) を乗じ、カットオフが時間とともに低下する1ポールローパスフィルタを通す。マスターチェーン:穏やかなコンプレッサー(レシオ2~3)、次に高速なハイレシオコンプレッサー(リミッターとして)、次に測定されたメイクアップゲイン、そして最後に線形マスターフェード(リバーブテールが消えるように、終了の約0.15秒前にゼロに達する)。

  • ビット単位の正確なミキシング。 Chromiumはノードの入力を不特定の順序で合計する。浮動小数点加算は2つのオペランドに対してのみ同じビットを返すことができ、コンプレッサーは1e-7の丸め誤差を呼び出し間で可聴レベルの差に増幅する。ゲインノードをチェーンすることで、すべてのミックスポイントに入力を最大2つに制限する:
function summer(ctx, target) {        // add(node) を返す。各ミックスポイントは最大2つの入力を持つ
  let open = target, count = 0;
  return node => { if (count === 1) { const j = ctx.createGain(); j.connect(open); open = j; count = 0; } node.connect(open); count++; };
}
  • DynamicsCompressorNode は独自の自動メイクアップゲインを適用するため、しきい値を下げるとミックスが大きくなる。代わりに出力ゲインでラウドネスを設定する。
  • ステムのバランスを確認する:メロディなしで一度レンダリングする。ベッドだけがフルミックスから2dB以内にある場合、メロディは埋もれている。

決定論的レンダリングチェックリスト

  • renderFrame(t) は全てのユニフォームを t のみから設定する。累積、Date、performance.now、または Math.random は使用しない。
  • 全てのランダム性は、シードされたPRNG(mulberry32)またはロード時にベイクされたテクスチャハッシュから得る。
  • 紙、ノイズ、IRはロード時に同期的に構築され、その後変更されない。
  • 後方シークは同一のピクセルを生成する。遅いフレームの後に t = 0 をテストする。
  • フレームコストはソフトウェアWebGLで1920x1080で測定する。1.5秒未満に抑えること。小さなディテールにはバウンディングボックスを使用し、ワープループは4ステップに保つ。
  • グレインは静的で、2パーセント未満。フレーム全体のフルスクリーンノイズは行わない。
  • ラインとフィラメントは、少なくとも1.5ピクセルのソフトなフォールオフを持つこと。
  • オーディオはシードされたノイズバッファを使用し、すべてのミックスポイントは最大2つの入力を持つ。2回の呼び出しはバイト単位で同一のバッファを返さなければならない。
  • プレビューモードは requestAnimationFrame でループする。レンダーモードはそれ自体では何も描画しない。

避けるべきこと

  • 純白の紙、純黒のインク、唯一の朱色のマークを超える彩度の高い色。
  • 文字のようなストローク、文字、疑似書道、署名、既成の文化的小道具。
  • 複数のジェスチャーの競合、雑然とした背景、常に同じ速度で動くインク。
  • グロー、レンズフレア、色収差、ボケ。インクはマットである。
  • 拡散の代わりに使われる均一なブラー。本当の拡散は、鋭いコアと同時に柔らかいヴェールを持つ。
  • 風鈴、カットごとのベル、持続的なパッド、シューという音、既成の五音階リフ。
  • 黒へのカットで終わらないこと。イメージを落ち着かせるか、紙またはグレーに溶解させること。

Individual skills in this repo

This repo contains 3 individual skills — each has its own dedicated page.

関連スキル