0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

ローレンツアトラクタ4万点を毎フレーム RK4 で動かして HDR ブルームで描く: Rust 製の数式ビジュアライズエンジンを作った

0
Posted at

galaxy デモ

galaxy デモをエンジン自身のフレームバッファから録画したもの。約3,000個のグリフが4本の渦巻き腕の上で、それぞれ円軌道を回っています。

何を作ったか

Proof Engine という、Rust と OpenGL で書いたリアルタイムのジェネラティブアート / 数式ビジュアライズ用エンジンを作りました。点やグリフ (文字) を微分方程式 (ローレンツアトラクタとほか6種のストレンジアトラクタ)、力場、物理で動かし、半精度浮動小数点 (half-float) の HDR パイプラインにブルームと ACES トーンマップをかけて描きます。

「方程式を書くと、それが何をするかを光る絵で見せてくれる」ものです。クリエイティブコーディングやジェネラティブアートが好きな人、ストレンジアトラクタが動くところをただ眺めたい人向けです。

Rust を入れなくても、Windows なら proof-lorenz.exe をダブルクリックするだけで動きます。必要なのは OpenGL 3.3 以上の GPU だけです。

数式が1フレームになるまで

数式がフレームになるまでの図

lorenz の例の1フレームは、次の4段階でできています。

  1. 数式: CPU 上で、40,000 個のローレンツ方程式の状態を RK4 で1ステップ進める
  2. グリフ: 各状態を1つのグリフインスタンスにする。位置は x と z から、色は速さから決め、全部を1回のインスタンス描画で出す
  3. 光: half-float の HDR バッファに描くので、重なった部分は 1.0 を超えて足し上がる。軌跡 (trail) は前フレームの 55% を残し、ブルームで光をにじませる
  4. フレーム: 露出、ACES トーンマップ、ビネットをかけ、最後に HUD の文字をくっきり上に描く

同じ実フレームを各段階のあとでキャプチャしたものがこちらです。

各段階のフレーム

左がグリフのパスだけ (ざらっとした点の蝶)、中央が軌跡とブルーム込み (ループが埋まって光る)、右がトーンマップとグレーディング込みです。HDR で「重なるほど明るくなる」ので、点の密度がそのまま光の強さとして見えるのがポイントです。

3ステップで動かす

1. プロジェクトを作ってエンジンを追加します。

cargo new lorenz-demo && cd lorenz-demo
cargo add proof-engine

2. src/main.rs をこれに置き換えます (リポジトリの examples/quickstart.rs と同じものです)。

use proof_engine::math::attractors::rk4_step;
use proof_engine::prelude::*;
use proof_engine::render::ui_layer::UiParticle;

fn main() {
    let mut engine = ProofEngine::new(EngineConfig::default());

    // 5,000 points in a line 2 units long, each 0.0004 from the next.
    let mut points: Vec<Vec3> = (0..5000)
        .map(|i| Vec3::new(1.0 + i as f32 * 4e-4, 1.0, 1.0))
        .collect();

    engine.run_ui(move |engine, dt| {
        // Advance every point along the Lorenz equations.
        for p in points.iter_mut() {
            *p = rk4_step(AttractorType::Lorenz, *p, dt);
        }
        // Draw them: x across, z up, centred in the window.
        let (w, h) = engine.render_size();
        let (cx, cy, s) = (w as f32 / 2.0, h as f32 / 2.0, h as f32 / 60.0);
        let color = Vec4::new(0.5, 1.2, 1.6, 1.0);
        let dots = points
            .iter()
            .map(|p| UiParticle::new(cx + p.x * s, cy - (p.z - 25.0) * s, 3.0, 3.0, '●', color))
            .collect();
        engine.ui.draw_particles(dots);
    });
}

3. cargo run --release で実行します。

5,000 個の点は、長さ2の線分の上に 0.0004 ずつずらして並べてあるだけです。最初の10秒ほどは1本の筋としてまとまって動きますが、そのあとカオスによって引き離され、ローレンツの蝶の両翼に広がります。初期値鋭敏性が目で見えるのが、このデモで一番見せたかったところです。

quickstart の4フレーム

1秒時点では小さな点、13.5秒では細い弧、16秒でループに分かれ、18.5秒で両翼を埋めています。

Linux では先に ALSA のヘッダが必要です (sudo apt install libasound2-dev pkg-config)。ビルドは必ず --release で。

再コンパイルせずに自分の方程式を試す

Rhai という小さなスクリプト言語で、力学系を数行で書けます。Thomas アトラクタならこれで全部です。

let b = 0.208186;
[sin(y) - b * x, sin(z) - b * y, sin(x) - b * z]

描画して、ファイルを保存するたびに描き直すにはこうします。

cargo run --release --example scripted_attractor -- examples/scripts/thomas.rhai thomas.png --dt 0.05 --map turbo --watch

タイプミスしたまま保存しても、どこが間違っているかを教えてくれて、直前にうまくいった絵はそのまま残ります。自分のプログラムの中では math::scripted::ScriptedSystem が同じことをフレームループ内で行います。

画面録画ソフトなしで GIF を撮る

エンジンが自分のフレームを読み戻して GIF を書き出します。ウィンドウは表示されません。

PROOF_HIDDEN=1 PROOF_FIXED_DT=30 PROOF_SHOT=lorenz.gif PROOF_SHOT_AT=300 \
PROOF_SHOT_COUNT=120 PROOF_SHOT_WIDTH=480 cargo run --release --example lorenz

拡張子を .png や .jpg にすれば1枚の画像になります。設定の一覧は docs/CAPTURE.md にあります。

全コアで回す

math::attractors::rk4_step_all(kind, &mut points, dt) はスライス全体を1ステップ進めます。parallel feature を有効にすると rayon で全コアを使います。i7-13700KF で 40,000 点のローレンツを1フレーム進めるのに、1コアで 1.21 ms、parallel で 0.10 ms でした (cargo bench --bench attractor_bench)。どちらでも結果はビット単位で同一です。

方程式は本当に正しいのか

絵がそれっぽく見えても、方程式が間違っていたら意味がありません。テストスイートでは、ローレンツ、レスラー、Thomas、Aizawa、Chen の各方程式を別途書き下したうえで、ode_solvers クレートの結果と突き合わせています。

ほかにできること

  • カラーマップ: math::color::preset_gradient("viridis") で viridis、magma、turbo、cubehelix、ColorBrewer など 38 種の標準カラーマップが使えます。Gradient::from_css("#000, deeppink 40%, gold") で CSS のグラデーション記法も受け付けます
  • 音をファイルに書き出す: audio::OfflineRenderer でサウンドカードなしにシンセを鳴らし、audio::wav::write_wav で保存します
  • スクリーンセーバー: 10種のストレンジアトラクタを順に流す Windows のスクリーンセーバー proof-screensaver.scr もあります

デモは全部で21個です。一覧と操作方法は docs/DEMOS.md にあります。

既存のクレートとの違い

  • nannou は汎用のクリエイティブコーディングフレームワークです (wgpu、描画 API、オーディオ、OSC)。一般的なスケッチならこちらが向いています
  • macroquad は Web でも動く小さなゲームライブラリです。2D ゲーム、特にブラウザ向けならこちら
  • Proof Engine はもっと狭い用途向けです。シーンは方程式で動くグリフとパーティクルで、HDR ブルームのパイプラインで描きます。アトラクタ、力場、カラーマップ、スクリプトで書く力学系、フレームキャプチャが最初から入っています

使っているクレート

ウィンドウと OpenGL は winit、glutin、glow、数学は glam、文字は ab_glyph、音声出力は cpal、カラーマップは colorgrad、スクリプトは rhai です。

おわりに

方程式を少し変えると絵がどう変わるかを、その場で見られるのがこのエンジンの一番の楽しみ方だと思っています。ほかに入れてほしいアトラクタや、Rhai で書いてみた面白い力学系があれば、ぜひコメントで教えてください。

MIT ライセンスです。

0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?