bashで3Dプログラミング

Posted on

今日はbashと基本的なシェルコマンドだけで3Dレンダリング(ワイヤフレーム)をしてみました!

Github: ledyba / Bash3D

bashプログラミングテクニック

今回つかったbashのプログラミングテクニックについて、記録のついでに少しまとめます。シェルスクリプトの基本的な所は書きません。

あと、3Dプログラミングに関しては本に書いてある事以上のことが書けそうにないので、(今は)書きません。

bashのデータ構造

bashでそこそこ本格的にプログラミングする事を考えた上でまっさきに「辛そう…」ってなるのはデータ構造ですよね!今回のソフトでは、ベクトルや行列を綺麗に扱えないと困ってしまうでしょう。まずはそこを見ます。

bashの「値」は基本的に全てただの文字列ですが、「リスト構造」にだけはネイティブで対応しています。こんな感じ。

$ lst=(1 2 3)
$ echo ${lst[0]}
1
$ echo ${lst[1]}
2
$ echo ${lst[2]}
3
$ echo ${lst[3]}
#空行

データにアクセスする時に、必ず${}でくくらないといけないのが少し面倒です。ちなみに添字を付けないとリスト全体…と思いたいところですが、最初の要素が出てきます。

$ echo $lst
1

全体を表示するには、${lst[@]}とします。

$ echo ${lst[@]}
1 2 3

リストを定義するときに使ったカッコが出てこないので、リストをコピーしたい場合はそのカッコを補わないといけません。

$ lst2=(${lst[@]})
$ echo ${lst2[@]}
1 2 3

慣れればなんとかなるレベルですが、面倒です。

さらに気を付けないといけないのは、”(1 2 3)”という文字列を返す関数を$(command)で受けて変数に代入してもリストになりません。

$ function fun() { echo \(1 2 3\); }
$ lst=$(fun)
$ echo $lst
(1 2 3)

ただの文字列として代入されます。どうしてもしたい場合は、evalを使わないといけません。

#文字列展開が起こってから実行されるので、lst=(1 2 3)というコマンドが実行されることになる
$ eval "lst=$(fun)"
$ echo ${lst[1]}
2
$ echo ${lst[@]}
1 2 3

通常の用途ならそれで済むのですが、今回は出来る限り高速に処理したいので、こうやってコマンドが沢山必要になるのは避けたいところです。

ところで。

後に言うダイナミックスコープとevalを組み合わせると連想配列みたいなことも出来るのですが、今回のプログラムでは基本的にこのリストでデータ構造を管理しています。

インデックスを指定したアクセスをするためにコストが高いevalを行わなくてよいこと、ベクトルと行列が主役なのでリストで十分な事が理由です。

$ matrix=(MAT 1 0 0 0 0 1 .....)
$ vector=(VEC 0 0 0 1)

最初の要素に型の名前を入れて適宜assertすることで、動的型検査みたいなことをしています。

変数のスコープ

名前空間は基本的にひとつしかありません。関数のネストは一切関係なく上書きされていきます。

$ val=0
$ function fun() { val=1; }
$ fun
$ echo $val
1 #関数内の代入だが、グローバル領域が上書きされてしまった
$ function fun2() { val2=2;echo $val2; }
$ fun2
2
$ echo $val2
2 #オフサイドルールではなく、存在しない変数はグローバルに作られるみたい

一種のダイナミックスコープみたいなものだと理解しておけば大体書けます。

呼び出し元の変数がうっかり書き換えられてしまう可能性があるのですごく不便ですが、逆に利用することも出来ます。

「関数の引数に変数名を渡すと、evalと組み合わせることで指定した変数に結果を格納する関数」が作れます。こんな感じ:

$function setOne() { eval "$1=1"; }
$ setOne val1
$ echo $val1
1
$ setOne val2
$ echo $val2
1

これを使って「最初の引数は入力、最後の引数は出力」と規約を決めて関数を設計しています。たとえば行列の計算はこちらにあります。見た目がアセンブラみたいになりますネ。

「こんなスコープ再帰が出来ないのでは?」という気がしますが、案外なんとかなります。というのも、$(command)はサブシェルとかいうので実行されるらしくて、さらにサブシェルは別の変数空間で動作するようです。つまり、このスコープの問題は起こりません。…たぶん。よく分からない(◞‸◟ )

 #再帰関数といえば階乗である
function kaijo() {
    arg=$1
    next=$(expr $arg - 1)
    if [ $arg -gt 1 ]; then
        result=`kaijo $next`
        echo $(expr $arg \* $result)
    else
        echo 1
    fi
}

kaijo 10 --> 3628800

ついでにevalとこのスコープの仕様を使うと、擬似的な連想配列を作れますが、値のコピーが難しいこともあって使いませんでした。

$ val_key1=123
$ val_key2=456
$ function showKey1() { echo $(eval "echo \$$1_key1"); };
$ showKey1 val
123

固定小数点とexprコマンド

シェルスクリプトで計算をするには、exprコマンドを使う必要があります。このコマンドは整数の計算にしか対応していません。

$ expr 1 \* 2 \* 3
6
$ expr 1 \* 2 \* 3.2
expr: non-numeric argument

3Dソフトでそれでは困るので、整数だけで小数を計算します。それにはどうするかというと、扱う小数を適当に1000倍とかして、それを小数に見立てます。つまり、1000は1、1001は1.001、3140は3.140だと思うことにします。その場合、計算する時にちょっとした手間が必要になります。

#3.14 + 3.14
$ expr 3140 + 3140 #足し算・引き算はそのまま足し算で大丈夫です。
6280
#3.14 / 3.14
$ expr 3140 \* 1000 / 3140 #割り算するときは、倍率で掛けてから割ります。この世界では「1.0」は1000であることに注意
1000
#3.14 * 3.14
$ expr 3140 \* 3140 / 1000 #掛け算するときは、掛け算してから倍率で割ります。
9859

倍率を1000じゃなくてもっと高くすれば、もっと小数演算の結果が正確になります。

私の環境だけかもしれませんが、exprはどんな大きな数でもちゃんと扱ってくれるようなので、ガンガン倍率を上げていけば誤差がまったく気にならなくなります。これはすごい楽…(楽すぎた)

なお、sin/cosは(テイラー展開を自分で展開しない限り)計算できないようだったので、0~360度について事前に求めておいて、これを先ほどのリスト構造にいれて使っています。平方根も使いたかったんですが、どうやっても無理そうなので諦めました。

表示:オフスクリーンレンダリング

もちろんターミナルにはピクセルがないので、ターミナル上に表示する文字で代用します。普通に表示するなら、tput cup (line) (col)を使うとターミナル上の任意の位置に移動できるので、これを使いたいところです。

# 斜め45度に線を引いてみた
for1;do
    for2;do
        tput cup y x
        echo -n \*
    done
done

が、この方法では線を引く様子が目で分かるぐらい時間がかかります。これで最初レンダリングしたみたのですが、レンダリング中に線を引いている様子がバッチリ見えてしまい、あんまり回転アニメーションしてるように見えませんでした。

ほんもののゲームでもそういう、更新途中の絵が表示されてしまってちらつく問題があります。ほんもののゲームでは、そういう時は、書いてもすぐには表示されない「裏の画面」を用意しておいて、そちらに時間を掛けてレンダリングして、最後に表示される「表の画面」と入れ替える「オフスクリーンレンダリング」と「ダブルバッファリング」をしているのでした。私も同様にそのテクニックを使いました。

具体的には、こんな感じ。

SCR_BUFF=$(head -c $size < /dev/zero | tr '\0' '_')

今回の「ピクセル」は文字ですから、「裏の画面」はただの文字列になります。
画面のサイズを取得しておいて、そのサイズ分何もない文字列を意味する「_」をtrを使って入れておきます。空白でいいじゃないかと思うかもしれませんが、シェルスクリプトでは空白はかなり重要な意味を持っていて面倒なのでこうしています。

「ドットを打つ」時は、対応する文字列中のインデックスの文字を*に書き換えます。

SCR_BUFF=$(echo $SCR_BUFF | sed s/./*/<置き換える文字の位置>)

zshだとstr[12]=\*ってやると文字列を更新できるのですが、bashでは使えなかったので、sedを使って書き換えています。この処理が非常に重く、今回のレンダリングエンジン(?)の処理時間の殆どはこのドットを打つ処理に費やされています。上手い方法を知ってたら教えてください!

最後に表の画面に更新する方法ですが、単純にechoするだけです。この時、何も入っていない所には_が入っていたので、空白に直します。

$ tput cup 0 0
$ echo -n $SCR_BUFF | tr '_' ' '

ちゃんと幅に合わせて改行されるので、うまく表示できます。

高速化

インライン展開 → プロセスforkを抑える

当初綺麗に関数を使って書いた所、行列の掛け算に1秒近く掛かってしまい、さすがにレンダリングは出来ないのかな…と諦めかけたのですが、行列やベクトルの掛け算や足し算を全てインライン展開したところ、座標計算に関しては十二分すぎる程の速度が出せるようになりました。前述した通り、今回遅いのはオフスクリーンにピクセル(文字)を書き込む処理が異常に時間が掛かっているからです。

やはりプロセスの起動はものすごい重い処理のようで、可読性をそこまで損なわない範囲でプロセスの呼び出しを一箇所にまとめています。

感想

線を引くのにえげつない時間が掛かるのを見た時、初めて触ったBASICの処理系を思い出しました。懐かしい。

ところで、なんで3Dライブラリを作るとOpenGL風の名前にしちゃうんだろ。

ニコ動に「次はシェーディングだな!」というコメントがついているのですが、テクスチャの表示をやってみたいです!7色くらいしか使えないので、たいへんそう…。影は「暗い色」が基本的に無いのでかなりつらい気がします。

  1. x=0;x<10;++x []
  2. y=0;y<10;++y []

3Dをゼロからレンダリングするテスト

Posted on

C++から動画を直接出力する自作ライブラリ「Dolly」の上で、フルスクラッチで3Dのレンダリングを行なっています。

とりあえずは、ワイヤーフレームの正方形をぐるぐる。

テクスチャもなんとか実装しましたが、三角形の継ぎ目が出ているのが気になります。でもどこが悪いのかわからん…

このサンプルのソースコードはこちらです:

https://github.com/ledyba/Dolly/blob/master/src/sample/super_px.cpp

東北地方太平洋沖地震の地震波を音にしてみた

Posted on

地球物理をやる学科に居るので、地震も学んでいるのですが、ふと思いついたので地震計の観測した波形を音に見立ててWAVファイルにしてみました。

気象庁の強震波形(平成23年(2011年)東北地方太平洋沖地震)のページから、福島県郡山市朝日の、震度6のデータです。なんだか、雷みたいな音がしますね!

ソースコードはこちらです。PythonでCSV読んでWAVに書いているだけですが、一応使い方のサンプルとしては使えるのではないでしょうか。

二重振り子のシミュレーション

Posted on

C++から動画を直接出力できる自作ライブラリ「Dolly」を用いて、二重振り子のシミュレーション動画を作ってみました。

10分耐久・たのしい二重振り子

ソースコードはこちらにあります:

二重振り子はどこにいる?

さらに、振り子の通ったところをずっと重ねるように描いた動画も作りました。

すごいランダムな動きに見えますが、振り子のある場所はやっぱり下側が多い?

自作ライブラリでマンデルブローを描画した

Posted on

C++から動画を直接出力する自作ライブラリDollyを使って、マンデルブローを拡大する動画を作りました。

この部分のソースはこちらにあります。

紙ジェクションマッピングしてみた

Posted on

あけましておめでとうございます!今年もよろしくおねがいします。去年はあまりいろいろ出来なかったので、今年は少しずつでも、何かできればなと思っています。せっかく転学部までしたので物理をちゃんと身につけたい1ですし、大学再受験も本腰いれてやってきたいです。

さて、新年早速何かアウトプットしよう、ということで、去年作って放置していた「紙ジェクションマッピング」というネタを投稿します。

紙ジェクションマッピングしてみた

プロジェクションマッピングってご存知でしょうか。

20140102_07
東京駅が動き出す!? この冬見られる話題のプロジェクションマッピングまとめ

こういう感じで立体物に映像を投影するアートで、最近いろいろなところで行われているようです。

これっぽい事がしたいのですが、うちにはプロジェクタのような高いものはありません。ので、代わりに1枚50円で印刷できるコンビニのプリンタで何か似たようなことが出来ないかと模索した結果、こんな感じになりました。

仕組みは非常に単純で、ある角度から眺めると立体的に見えるように紙に印刷してあります。

20140102_01作り方ですが、まずこのマーカー画像をセブンネットプリントで印刷して(宣伝)、

20140102_03こんな感じで撮影します。このとき、カメラのファインダから眺めた時に台形が真四角に見えるようにするのがポイントです。

20140102_02この状態で撮った画像がこちら

20140102_04これを作ったソフトで変形させて適当にホワイトバランスを調整。

20140102_05これを紙に印刷して、マーカー画像を印刷した紙の代わりにこの紙をおきます。

20140102_01この状態で動画を撮ったのが、先ほどのニコ動の動画です。

20140102_06実態としては、台形に変形させただけの簡単画像加工をしただけということになりますw

今後の課題

今回は一番簡単な平面でしたが、もっといろいろなものに紙ジェクションしたいですね。たとえば、ティッシュの箱に貼り付けて、ある角度から見ると立体的に見える…とか。今位置合わせが非常にしんどいのですが、ARマーカーでなんとかできるといいんじゃないかなーとかも考えてます。3D力が問われる!!

ただ、結局プリンタの実力があまり高くないせいで、紙に写した方がそんなにリアルに見えないので限界かなという気がしています。っていうかそれがお蔵入りしてた原因です…(◞‸◟ )

  1. 物理とか数学って何やるにしても役に立つと思います []

クリスマスはNoiz2saをAndroidで!

Posted on

おひさしぶりです。21日のニコニコ学会βは良かったですね。特に良かったのは研究100連発で、本職の人のすごさを見ると、私も何かつくりたいな〜って気分になります。私はボランティア(登壇者へのカンペ出し)として参加しました。凄い人を間近に見れるという特典付き(?)の嬉しいけど責任重大なお仕事です。イベント運営って大変ですね…。あと野糞の人は本気でマッドだ(褒め言葉)。

Noiz2saをAndroidに移植してみた!

Noiz2saはABA Gamesさんの開発した、アブストラクトな感じのシューティングゲームです。私はこのゲームがSTGに入り始めたきっかけだけあって、大好きです。

去年はWebに移植したNoiz2saを公開しましたが、今年はAndroidに移植したものを公開します。

クリスマス、そして年末年始といえばシューティングゲーム!ぜひお楽しみ下さい。敵の弾を避けて撃つ、指一本で遊べるシンプルなゲームです。

USBのゲームパッドをつなぐと、PC版と同様な操作で遊ぶことも可能です。

ダウンロードはこちらから

バッドノウハウFAQ

移植時のバッドノウハウを書きます

Q.リソースのファイルの拡張子がamrとかなのはなぜですか

このように置かないと、リソースが圧縮される時に圧縮されてしまい、AssetManagerからランダムリードすることが出来なくなってしまうからです。

あっコーナー分けた割に一個しか思い浮かばなかった…質問は随時受け付けています。

二条項BSDとのデュアルライセンス。さきゅばす2.0b4を公開しました

Posted on

久しぶりの更新です。。。。

久しぶりにさきゅばすのアップデートを行いました。またマイナー更新ですけど…。

2.0b4のダウンロードはこちらから!

リリースノート

デュアルライセンスになりました

今回のver 2.0b4のソースから、

  • GPL v3 or later
  • 2-clause BSD License

のデュアルライセンスになりました。派生物のライセンスはこのどちらか一方か、両方(デュアルライセンスのまま)を選ぶことができます。また、改造されたffmpegに関しては、勝手にBSDにすることはできないので、従来どおりGPL v3のままとなります。

また、GPL v3のライブラリがリンクされているので、配布バイナリも従来どおりGPL v3のままです。

ライブラリの更新とバグの修正を行いました

その他の更新はこんな感じです。

  • 2chのスレで見つかってたいくつかのバグを修正しました。
  • x264とかffmpeg本体のライブラリの更新を行いました。
  • ビルドシステムがcmakeからwafになりました。pythonで書きやすい。
    • このためにwafにバグ修正パッチまで投げました…(1.7.10でaccepted)
  • 未踏ソフトウェア事業で開発していたもののうち、基礎的な部分をライブラリ「しなもん」として切り出し、そちらを利用することにしました。
  • 上記の影響でlibxml2への依存が消えています。icuにも陽には依存しなくなりました(しなもんを通して陰に依存はしています)。

追記

2ちゃんで報告されてた「【第10回MMD杯本選】騒がしいゆーじょー【超遅刻カオス】 」の音ズレ問題ですが、たぶんacodecがaac(ffmpegの内蔵aacエンコーダ)が問題を引き起こしてるのではないかと思います1。外部ライブラリであるlibvo_aacencにすると解決するようです。

今までは変換レシピでaacを使うものがいくつかありましたが、今回のバージョンからlibvo_aacencだけにしました。

  1. スレに書こうかと思ったらまた規制掛かっててファッ!? []

2012年度未踏事業でスーパークリエイター認定されました

Posted on

1月まで独立行政法人 情報処理推進機構(IPA)未踏IT人材発掘・育成事業の支援で「プログラミング言語ど~なっつ」と、それを用いた「ファミコンを題材にした電子教材のような何か」を作っておりましたが、本事業でスーパークリエータに認定されました。わーいわーい(謎

5/29にはスーパークリエータ認定授与式があり、そこで再度プレゼンテーションを行います。でも平日だよ!

今後の展開なのですが、とりあえず時間操作ができるプログラミング言語、ど~なっつの改善をしたいな~と思っておりまして、

とかやりたいです。特に最後ですが、これが出来ればニワン語の/seekとかにも対応できるし、現状抽象構文木をそのまま評価している「ねこまた」をど~なっつVMへのトランスレータにすることが出来れば、リアルタイム再生も夢でないくらいには高速化できる…かも??

本業(学生)の方があるので支援期間中ほどの速度は出せませんが、ぼちぼち書けていければな~と思います。進学する学科、間違えちゃったかなあ…。