🎨

RustのNannouフレームワークに入門してみた!

2024/02/04に公開

はじめに

Nannou_Logo
RustのクリエイティブコーディングフレームワークであるNannouに入門してみました!
Rust初心者 && 記事投稿は初めてなので、温かい目で見てください...

対象としている読者

  • Rustの環境構築が終わっている方
  • Rustの学習にチャレンジしてみたい方
  • クリエイティブコーディングにチャレンジしてみたい方

そもそもクリエイティブコーディングとは?

クリエイティブコーディングを簡単に説明すると、
プログラミングを活用して、
アート作品・アニメーション・イラスト・音楽・実体を持つ
作品などを作ってみよう!! という活動(ジャンル)のことです。
こんなこと言ってますが、筆者もp5.jsを少し触ったことがあるだけの初心者です!(笑)

nannouフレームワークの概要

https://nannou.cc/

Nannouはオープンソースの下に開発されており、
シンプルで高速かつ信頼性の高いコードで、
アーティストが自分自身を表現する事を容易にすることを目的としたライブラリであり、
アート・アニメーション・音楽等を作成する為の沢山の機能が揃っています。

フレームワーク名の由来

公式サンプル作品

1 2 3

なぜRustを使用するのか?

Rustを使用することで、下記のメリットが得られます。

  1. 強力なメモリ安全性・型安全
  2. 依存関係を非常に簡単に処理できるパッケージマネージャ
  3. 洗練されたモジュールシステム
  4. 並行性と並列性のサポート
  5. C, C++と同等の高いパフォーマンスを発揮
  6. クロスコンパイルに対応している

特にクリエイティブコーディングにおいては、グラフィック描画などの、
計算量が多いため、このメリットはとても大きいです。

筆者の環境

筆者はWSL2を使用しているため、VcXsrvを使用して、GUIを表示しています。
場合により、VcXsrvが使用するポートのファイアウォールを
開放する必要があります。

VcXsrvの設定

  • Windows10 Home 64bit
  • WSL2
  • Ubuntu 22.04.3 LTS
  • Visual Studio Code 1.86.0
  • Rust 1.73.0
  • CPU: Intel Core i7-10700F
  • RAM: 32GB
  • GPU: RTX3060

早速コードを書いてみる。

筆者もRustにそこまで慣れているわけではないので、とりあえずNannouGuideを見ながら、
コーディングしていきます。

まずは、CargoでProjectを作成し、Nannouを導入し、
ガイドに則って空のウィンドウを作成します。

bash
$ cargo new nannou-sketch
$ cargo add nannou

[依存関係の追加]

Cargo.toml
[dependencies]
nannou = "0.19.0"

コードを見てみると、ソフトウェア設計パターンの一つである、
MVC(Model-View-Controller)のような設計がされていますね

このコードを実行してみると...

main.rs
// Nannouのアイテムを一括インポート。
use nannou::prelude::*;

/// アプリケーションのメインモデルを表します。
struct Model {}

fn main() {
    nannou::app(model).event(event).simple_window(view).run();
}

/// モデルを初期化します。
///
/// # Arguments
///
/// * `_app` - `App` 構造体への参照。
///
/// # Returns
///
/// 初期化された `Model` 構造体。
fn model(_app: &App) -> Model {
    Model {}
}

/// イベントがトリガーされたときにモデルを更新します。(イベントハンドラ)
///
/// # Arguments
///
/// * `_app` - `App` 構造体への参照。
/// * `_model` - `Model` 構造体への参照。
/// * `_event` - トリガーされたイベント。
fn event(_app: &App, _model: &mut Model, _event: Event) {}

/// アプリケーションの描画処理を行います。
///
/// # Arguments
///
/// * `_app` - `App` 構造体への参照。
/// * `_model` - `Model` 構造体への参照。
/// * `_frame` - ビューをレンダリングするフレーム。
fn view(_app: &App, _model: &Model, _frame: Frame) {}


空のウィンドウが表示されましたね。

SketchとAppの違い

新しいNannouプロジェクトを作成する場合、プログラムを開始するには
2つのオプションがあり、それがnannou::sketchとnannou::appです。

sketch app
シンプル 複雑
簡単な創作に向いている 本格的な創作に向いている

appではセットアップするための関数や構造体が、
sketchに比べて多いですが、結局のところ大きな違いはなく、
両者の切り替えも簡単にできるので、あまり気にしなくても大丈夫です。
これ以降の記事では、基本はnannou::appを使用します。

座標系

Nannouでは、他のクリエイティブコーディングライブラリに比べて、
座標の指定が少し特殊であり、
p5.js等ではウィンドウ座標の原点が左上から始まりますが、
nannouではウィンドウの中心が原点に設定されています。

これはnannouがデカルト座標を使用して、Windowを記述しているためです。
個人的には、p5.js等の座標系よりコードが書きやすい気がします(笑)。

x値は右に向かう程増加し、左に向かう程減少
y値は上方向に向かう程増加し、下方向に向かう程減少

座標系の例画像

下記はウィンドウの中心に正方形を表示する例です。

main.rs
fn view(app: &App, _model: &Model, frame: Frame) {
    // キャンバスを取得
    let draw = app.draw();

    // キャンバスの背景色を設定
    draw.background().color(WHITE);

    // 1辺が200の正方形を原点を中心として表示
    draw.rect().x_y(0.0, 0.0).w_h(200.0, 200.0).color(ORANGE);

    // フレームにレンダリング
    draw.to_frame(app, &frame).unwrap();
}

アニメーション

Nannouでは静止画だけでなく、アニメーションを作成する事も可能です。
下記は大きさが変化しながら画面内を移動する正円のアニメーション例です。

main.rs
fn view(app: &App, _model: &Model, frame: Frame) {
    // キャンバスを取得
    let draw = app.draw();

    // キャンバスの背景色を設定
    draw.background().color(WHITE);

    // Windowの幅を取得
    let boundary = app.window_rect();

    // 正弦波を作成
    let sine = app.time.sin();

    // 速度を1/2にした正弦波を作成
    let slowersine = (app.time / 2.0).sin();

    // 正弦波をWindowのx,yの最大値・最小値にマッピングする
    let x = map_range(sine, -1.0, 1.0, boundary.left(), boundary.right());
    let y = map_range(slowersine, -1.0, 1.0, boundary.bottom(), boundary.top());

    // 正円を作成
    draw.ellipse().w_h(x / 2.0, y / 2.0).color(WHITE).x_y(x, y);

    // フレームにレンダリング
    draw.to_frame(app, &frame).unwrap();
}

画像描画(テクスチャ)

まずプロジェクトのルートにassetsディレクトリを作成し、画像ファイルを配置します。

bash
$ mkdir assets
main.rs
use nannou::prelude::*;

struct Model {
    // テクスチャ構造体を追加
    texture: wgpu::Texture,
}

fn main() {
    // アプリケーションを実行
    nannou::app(model).run();
}

fn model(app: &App) -> Model {
    // 512*512のウィンドウを作成
    app.new_window().size(512, 512).view(view).build().unwrap();

    // nannouにassetsを読み込む
    let assets = app.assets_path().unwrap();

    // 画像のパスを読み込む
    let img_path = assets.join("images").join("universe").join("quasar1.jpg");

    // 指定したパスから、テクスチャを読み込む
    let texture = wgpu::Texture::from_path(app, img_path).unwrap();

    // textureフィールドを持つModelインスタンスを作成
    Model { texture }
}

fn view(app: &App, model: &Model, frame: Frame) {
    // フレームを初期化
    frame.clear(BLACK);

    // キャンバスを取得
    let draw = app.draw();

    // テクスチャを描画
    draw.texture(&model.texture);

    // フレームにレンダリング
    draw.to_frame(app, &frame).unwrap();
}

サウンドアート

実際に作品を作ってみる!

windowの端に衝突した際にボールの色と方向を反転させて、
波紋を発生させるクリエイティブコーディングを使用した作品を作ってみました!

ソースコード

https://github.com/SL9-1994/nannou-sketch

まとめ

初めてNannouフレームワークにチャレンジしてみましたが、
Rustの勉強にもなりますし、Rustを使用したクリエイティブコーディングに興味がある方は、
是非ともやってみてほしいです!

Nannou自体は、とても使いやすかったのですが、まだまだ情報が少ないので、もっとこのフレームワークを使って作品を作る方が増えると嬉しいです!
今度はNannou_Audioを使用した、音楽作成にも挑戦してみたいですね!

Discussion