4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

Life is Tech ! KantoAdvent Calendar 2022

Day 20

Swiftでもクリエイティブコーディングしたい!

Last updated at Posted at 2022-12-19

はじめに

本記事はLife is Tech! Advent Calendarの20日目の記事です!

今回のタイトルは "Swiftでもクリエイティブコーディングをしたい!" ということで、自分が先週作ったswifty-creativesというSwift製のクリエイティブコーディング用のライブラリで遊びつつ、今回クリエイティブコーディング環境を自作するにあたって大変だったことなどをまとめていきます。

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

とくに明確な定義があるわけではないと思います。
ただ、AdobeのIllustratorやPhotoshopなどのGUIツールを使って何かクリエイティブなものを作ることとは区別して、何かしらプログラムを書いてクリエイティブなものを作ること、というのが個人的な認識です。
ProcessingとかOpenframeworksなどはクリエイティブコーディングのための環境として使われることが多いイメージです。

まあとりあえず遊んでみよう

とりあえず本題のswifty-creativesで遊んでみましょう!
こちらのgithubからダウンロードしてみてください!
https://github.com/yukiny0811/swifty-creatives

ExamplesフォルダにXcodeプロジェクトが入っていますので、よければ実行してみてください。Xcodeが入っていて、macOS Monterey以降またはiOS13以上&iPhone11以降の端末があれば実行できるはずです。

実行してみるとこんな画面が出てくると思います。
ExampleMacOSApp 2022年-12月-15日 15.11.02.gif

ではプログラムを見てみましょう。

MySketch.swift
final class MySketch: SketchBase {
    var boxes: [Box] = []
    var elapsed: Float = 0
    func setup() {
        for _ in 0..<100 {
            let box = Box(pos: f3.randomPoint(-1000...1000))
            box.setColor(
                Float.random(in: 0...1),
                Float.random(in: 0...0.5),
                Float.random(in: 0...0.5),
                Float.random(in: 0...1)
            )
            boxes.append(box)
        }
    }
    func update() {
        for b in boxes {
            b.setColor(
                sin(elapsed),
                b.getColor().y,
                b.getColor().z,
                b.getColor().w
            )
        }
        elapsed += 0.01
    }
    func cameraProcess(camera: SwiftyCreatives.MainCamera<some SwiftyCreatives.CameraConfigBase>) {
        camera.rotateAroundY(0.01)
    }
    func draw(encoder: MTLRenderCommandEncoder) {
        for b in boxes {
            b.draw(encoder)
        }
    }
}

この4つのメソッドが主な処理になります。

.swift
final class MySketch: SketchBase {
    func setup() {}
    func update() {}
    func cameraProcess(camera: SwiftyCreatives.MainCamera<some SwiftyCreatives.CameraConfigBase>) {}
    func draw(encoder: MTLRenderCommandEncoder) {}
}

setup():最初に一度だけ実行される。重めの処理はここで書きましょう。
update():毎フレーム実行される処理。
cameraProcess():カメラを操作するための処理。毎フレーム実行されます。
draw():毎フレーム実行される描画処理。

Processingなどをさわったことがある人ならば簡単にいろいろ遊べると思いますので、試してみてください。今のところTriangle・Rectangle・Box・Img・TextObjectの5種類の図形を描画することができます。

なぜSwift?

少し遊んでもらったところで、なぜProcessingやOpenframeworksがある中、Swiftでクリエイティブコーディング環境を自作しようと思ったかについて話します。

自分はプログラミング言語としてSwiftがとても好きで、普段の開発も8割程度はSwiftで書いています。ですがSwiftでクリエイティブコーディングやメディアアートっぽいことができるライブラリはほとんどなく、少しだけ存在するライブラリも機能面やパフォーマンス面で自分の求めているレベルのものがありませんでした。

また、iOSやmacOSアプリを作る際の画面を派手にポップにしたいという思いもあって作っています。ここで書いたスケッチのプログラムはUIKitのUIViewとして、またはSwiftUIのViewとして使うことができるので、簡単にアプリに組み込むことができます。

例えばこのようにListのセルの背景を派手にしてみたり、

Simulator - iPhone 14 Pro 2022年-12月-20日 4.51.23.gif

ちょっとしたローディングアニメーションとしていれたりすることも簡単にできます。

Simulator - iPhone 14 Pro 2022年-12月-20日 4.59.13.gif

ちなみにViewらへんのコードはこれだけ

.swift
ZStack {
    SketchView<MySketch, MainCameraConfig, MainDrawConfig>()
    Text("Hello, World!")
        .font(.largeTitle)
}
.background(.black)

さらに、Swift製であることの一番の利点はiOSのセンサー類などを全て組み込むことができる点にあります。マイクの音を拾ってみたり、カメラの画像を使ってみたり、加速度センサの値を使ってみたりなど、普通にiOSで書けるコードをそのまま使えるので学習コストは低くなります。

アプリ内で派手なグラフィックをしたいだけならWebViewを使ってp5jsを動かしたりすれば当然できるのですが、ユーザーとのインタラクション・パフォーマンスという観点では断然Swift製のライブラリを使った方が良いです。

SwiftyCreativesを作る上で大変だったこと

ここからは自分がSwiftyCreativesライブラリを作る際に考えたことを軽くまとめていく、という内容になっています。

図形の描画

クリエイティブコーデイング環境にはなんといっても図形が必要です。2Dなら三角形・円・長方形、3DならBoxなど。
2Dシーンで図形を描画するのは実はそんなに難しくないのですが、3Dになると途端に難しくなります。なぜなら最終的に僕らが見る画面は2Dなので、3Dオブジェクトが持つ3D座標を2D座標に変換しなければならないからです。
3Dの座標変換についてはたくさん記事がでているのでここでは説明しません。もし興味がありましたらこちらの記事とかは短くてわかりやすいのでおすすめです。

さて、図形に必要なパーツも考えてみましょう。
・それぞれの頂点のローカル座標
・シーン内でのワールド座標
・回転(rotation)
・大きさ(scale)
・色(color)またはテクスチャ(texture)

これらの情報を使ってオブジェクトを描画するのですが、描画するためにはさきほど軽く紹介した座標変換が必要になります。描画する3Dオブジェクトが1つとか2つなら全然問題ないのですが、10000個とか100000個とかを描画したい!となった時には普通にfor文でやっていると全く処理が追いつきません。

そこで登場するのがGPUです。
GPUはCPUとは違い並列処理が得意なので、100000個のオブジェクトを描画したい!と思った時に、CPU1つに全て任せるのではなく、並列化できる部分の処理をGPUに託します。具体的に言うと、座標変換と各ピクセルの色の計算はGPUに任せたいです。

ただ、GPUはハードウェアなので、直接いじるためには低レイヤーの知識がかなり必要になります。そこで登場するのがMetalです!MetalとはApple製デバイスのGPUへのアクセスを提供するAPIのことです。

今回自分はこのMetalを使い、図形を描画しました。

アルファブレンディング

普段自分たちが何気なく使っているRGBAのA
実はかなり複雑な処理で描画されています。

透明度をもつオブジェクトが複数あるシーンでは、それを正確に描画するためには一番後ろにあるオブジェクトから順番に描画しなければなりません(または前から後ろ)。ですが、GPUによって図形は並列に描画されているため、後ろにあるオブジェクトから順番に描画されるとは限りません。

そこで、今回はMetalが提供するRaster Order Groupsというものを用いて順番を気にしなくても透明度がちゃんと計算されるようにしています。詳しくはこちら

効率化

現在のswifty-creatives最新版はv1.4.1ですが、v1.0.0の初期バージョンと比べて4倍以上パフォーマンスが向上しています。
追記2023.02.10: 現在のswifty-creatives最新版はv1.9.0です。変更点が多いのでGithubも確認していただけると嬉しいです。
追記2023.02.28: 現在の最新版はv1.11.1です。変更点がかなり多いので再度記事を書きます。

まとめ

  • 自分の好きな言語でクリエイティブコーディングができるのは楽しい。
  • 3Dレンダリングは奥が深い
  • SwiftyCreatives ぜひ遊んでみて!
  • バグがありましたら教えていただけると助かります。PRも大歓迎です。
4
3
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
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?