OpenFrameworksでカーニングやらなんやら2

先日からHarfBuzzとずっと格闘してる。
難しいけどコーディングはDEEPなのですごくエキサイティングだ。

結論からいうと、openframeworksにおいてのカーニングについてはHarfBuzzとFreeTypeの相性が抜群なので、
ofTrueTypeFontUC にちょっと手を加えるだけで 日本語のプロポーショナル(palt)については対応出来た。

やりたい機能である、混合フォントについては
bool loadSubFont(string filename,float sizeRate=1.0f, float baseLineRate=1.0f ,int unicodeRangeStart=0x0000,int unicodeRangeEnd=0x0000);
みたいな感じでサブフォントをunicodeレンジに対して追加できるようにしてみた。
unicodeレンジを指定しない場合は後で読み込まれたフォントで上書きするイメージ。
最初に日本語フォントを呼んで LATENフォントを読めば 対応文字のみ LATENフォントで表示ができる。
ついでにいうとこのクラスはTextureつくっちゃうのでオンスレッドで動作できないので
(アウトライン)パスをスレッドでガンガン呼び出して使いたいのでofImageに習ってbUseTextureみたいなフラグもつけた。

で、次はレンダリング時の改行制御だ。これについては、1つ外側のレベルの話だ。
ICUとかを導入するのが筋っぽいんだけど大げさすぎるので簡単に実装してみた。
(というかパス通し疲れた・・・)

各文字要素(クラスタ)に対して、こんなかんじで改行可能かどうかをマーキングしていく。

breakable=__CharsNotEOL.find(src[i])==string::npos &&
(isCJK(src[i])||(i<len-1?isCJK(src[i+1]):true)||__CharsBreakable.find(src[i])!=string::npos) &&
(i<len-1? __CharsNotSOL.find(src[i+1])==string::npos :true);

日本語にしてみると、

・行末禁則文字でない
かつ(&&)
・CJK文字である または(||) 次の文字がCJK文字である または(||) 区切り文字である
かつ(&&)
・次の文字が行頭禁則文字ではない
場合は改行可能(笑)

概ねこんなかんじだと思う。
後は禁則文字セットなどを充実しておけばわりとちゃんと動きそう。

マーキングだけしたら、実際の改行判断はレンダリング部分でやる(次回)。
どんどん日本語限定仕様になっていく気がするな・・・。
ちなみにちょっと手を加えれば縦書きに対応できる。

と、いろいろ順調だったんだけど、なんかharfbuzzのclusterが
ちょいちょいおかしい値を返してきて壮大に位置がずれるのである・・・。
調べたらどうやら ligature だ。これは盲点だった。
ちゃんとunicode文字コードで用意されてるんですねー。
hb_feature_tでligaをoffってみたらちゃんと(っていうか若干重なって)表示された。
が、全く美しくないので次はligatureに対応しなくてはならない。

レンダリング freetypeに依存しすぎ&合成フォントしてるしで大改修が必要である・・・。
\(^o^)/オワ・・・

OpenFrameworksでカーニングやらなんやら

OpenGLの環境下でフォントのレンダリングについて色々調べている。

まずは標準のofTruetypeFontをつかってたんだけど、いろいろ問題がある・・・。
このクラスはFreeTypeをベースにしてる。余談だけど、グリフで遊ぶならOF007とかにバンドルされているFreeTypeはVersion2くらいなので、日本語の大部分のフォントで正しいアウトラインがとれないし、ビットマップレンダリングも崩れる事がある。これは最新のFreeTypeを入れれば解消される。

問題なのはカーニング。一見詰めは効いてるように見えるが・・・

・欧文フォントなら文字幅での詰めはあるが、カーニングpairが効かない。
・日本語フォントはpaltがとれないので等幅になる。

これらはGPOSに含まれるカーニング情報であり、FreeTypeのDOCに書いてあるとおり。
※ただしkernテーブルにカーニング情報がある場合はFreeTypeでもカーニングされるらしい。

しらべたところ、ICUやPangoというテキストレイアウトライブラリがあればできるっぽいとのとこで、
まだ試してないけどofxFontとかofxPangoというアドオンが見つかった。

とりあえず自分の用途にはちょっとあってなさそうだったので、Pangoコンパイルしてみた。

なんか大変だった・・・。

これからofで使ってみる。

追記:

どうやらharfbuzzを内部で使っていてこれだけでできそう。ややこしいな・・・

ポリゴンの三角形分割

openFramworksのofPath、最近初めて使っているのだが、想像以上にいたれりつくせりでよかった。パスと言っても直線だけでなくベジェ等色々サポートしていてofでやりにくいと感じていたベクターグラフィックが思ったより簡単に扱える。ofxSvgでSVGパースもできるし。

ofPathにはofPath::getTessellation();で一撃でmesh面を形成する機能がある。しかしこの3角系分割は、平面走査法(?)みたいな分割が行われるため、ベジェを多用した図形をテッセレートすると、短いレンジで縦方向に大きな分割が入りまくる。これだとテクスチャを貼ったりする場合の歪の原因になるし、あまりに多くの縦ラインの密集は面の色すらうっすらと変えてしまうほど。ポリゴンの変形がやりにくい状態。

三角形分割のライブラリはいろいろあったので5,6個試したところこれが一番良かった。
https://code.google.com/p/poly2tri/
穴あきポリゴンにも対応していて、C++ですんなり使えた。
パスの分割もblenderとかにある機能みたいな感じでめっちゃ綺麗に、理想的な分割をしてくれます。

Jonathan Shewchukのtriangleっていうライブラリも良かったけど、
http://www.cs.cmu.edu/~quake/triangle.html
パスを分割と言うよりはグラフィックそのものを面として分割する機能に特化した感じで、こちらは用途違いで使えそうだが、アルゴリズムの特性上パスの分割には向いてなかった。

とりあえずこれで、2Dで作った図形を3Dに押し出した。
ライブラリ無いんかいと思いながら書いた。

流れとしては、
1.パスの面をpoly2triで三角形分割(前面)
2.背面形成
3.アウトラインから側面形成
4.法線計算

ofPath -> ofMesh の変換ですね。
ベジェとかを使っているので、30度未満の角のみスムーズ法線で、綺麗な押し出しが完成しました。addon化しよっと。

OpenNI2+NITE2を試す

会社でのお仕事。PrimeSense x Openframeworks で作ってる。
体の動きにあわせて文字が動く。みたいな。

2013-02-07 15.49.02

これ系のセンサーについてはこの一年かなり使い倒したなー。Kinectも。感覚的には、OpenNIよりWindowsSDKのほうが扱いやすいし安定感はある。でも使えるのはやっぱOpenNIだ。スケルトンの認識数で有利だし、ハードがごつくない。IRカメラは開発元同じっぽい(?)けどASUSのXTIONはKinectよりもRGBが綺麗だと思う。(kinectはなぜか標準でジャギが目立つ)

このセンサー認識精度も優秀なんだけど、こういう展示作品にする場合、プレイヤーの後ろに人だかりができてしまって、プレイヤーまで認識精度が落ちる。というのは、認識のトリガーのようなものはあくまで動きなので、カメラ的に背景が動いてるとやっぱ認識が悪くなる。一回精度が落ち始めるとなかなか復活しない。なのでセンサーを定期的にリセットしたいのだけど、そういうAPIがなかった。センサーを完全にシャットダウンすることはできるけど、時間がかかりすぎる。
で、今回OPENNI2+NITE2 という構成に載せ替えてみた。ofxアドオンになかったので、主要処理はサンプルから移植。NITE2ではのUserTrackerをいったん破棄して再構築することでセンサーのリセットが可能になってた。この場合のリセットは2秒ぐらいかかる。

で、様子をみてるけどnite2.dllがたまにエラーを吐いて落ちる見たい。悩ましいなー。

Geometory Shader でB-スプライン曲線を描いてみる

3Dでスプライン曲線を描きたいというお題。

いいサンプルが見つからなかったので、とりあえずofx3DSplineをつくってみた。基本的には頂点配列を更新するとスプライン描画用頂点配列を更新してくれるクラス。でも、重かった。60fpsだと256頂点で20分割の補完が限界。これはリアルタイムに曲線をアニメーションさせ続けた場合の負荷。描画はVBOで高速にやってるのでCPUでやってるスプラインの計算がイケてないことは間違いない。他にもいろいろ計算させたいのでこれでは使い物にならない。

そこで、最近ハマってるシェーダーの登場。今回はあまり使ったことのないGeometryシェーダを使ってみる。ちなみにスプラインをやりたいのにB−スプライン曲線になってるのは力不足のため。本当にやりたいのは指定点を必ず通過するほうのスプライン曲線。こちらのほうがアニメーションでは制御しやすそうな気がしてる。あとこの分割ってところはTessellation シェーダーの役割のような気がしていて、実際できるみたいなんだけど、情報が少なかったのとATIとNvidiaで違うところがあるらしいので、今回はGeometryで。

スプラインの参考にしたのはこのドキュメント

以下、GLSL4でB-スプラインを描くジオメトリシェーダー。uniformで頂点の分割数を変更可能。


layout( lines_adjacency ) in;
layout( line_strip, max_vertices=200 ) out;
uniform int division;

vec4 w_bspline(float alpha){
 vec4 tmp;
 float a2=alpha*alpha;
 float a3=alpha*alpha*alpha;
 tmp.x = -a3 + 3.0*a2 - 3.0*alpha + 1.0;
 tmp.y = 3.0*a3 - 6.0*a2 + 4.0;
 tmp.z = -3.0*a3 + 3.0*a2 + 3.0*alpha + 1.0;
 tmp.w = a3;
 return tmp;
}

void main(void)
{
 int i;
 vec4 w;
 for (i=0; i<=division; i++)
 {
 w = w_bspline(float(i)/float(division));
 gl_Position.xyzw =
 w.x * gl_PositionIn[0].xyzw +
 w.y * gl_PositionIn[1].xyzw +
 w.z * gl_PositionIn[2].xyzw +
 w.w * gl_PositionIn[3].xyzw;
 EmitVertex();
 }
}

注意点はOPENGLのほうで、GL_LINE_STRIP_ADJACENCYを指定すること、これによってシェーダーが隣接含め4点を受け取り補完を行える。こんなかんじで。


 glEnableClientState(GL_VERTEX_ARRAY);
 glVertexPointer(3, GL_FLOAT, 0, mSplineVtx);
 glDrawArrays(GL_LINE_STRIP_ADJACENCY,0,mSplineVtxNum);
 glDisableClientState(GL_VERTEX_ARRAY);

ちなみにopenframeworksのofShaderはジオメトリシェーダーにもちゃんと対応してて、以下のように読み込めるようになってた。Tessellation はいまのところ未対応。


shader.load("myShader.vert","myShader.frag","myShader.geom");

以上でベジェ曲線の描画は上手く行ったんだけど、やっぱり重かったw。CPUでやるのとパフォーマンスそんなに変わってない。やはり若干滑らかさを犠牲にするしか無いかな。そもそもB-スプラインじゃだめだしw

参考:

Bulletでsoftbody

ソフトボディをやってみたかったので、とりあえ本買ってみた。

この本、泣けるぐらいSoftBodyの解説は少なかったわ( 3ページぐらい)w
けど、モデル(wavefront*.obj)のメッシュからsoftbody生成するサンプルがあった。まぁbulletライブラリのサンプルでもsoftbodyのサンプルいくつかあるんだけどね・・・。この本のサンプルはCode::Blocks+MinGWという構成だが、とりあえずVisualStudioで動かしてみた。

なんか表示は上手く行ってないっぽかったけど、上は柔軟体と鋼体のteapot。ぐにゃぐにゃしてる。(っぽい)
softbodyを使うためにはいつも使ってるbtDiscreteDynamicsWorld じゃなくて btSoftRigidDynamicsWorldを使う。当然RigidBodyも使えるワールドだが、softの計算もやるのでパフォーマンスをちょっと心配したけど、軽く試したところRigidについてはそんなに悪くなさそうだった。が、物体をすり抜けてしまうことが多い気がした。通常の物理演算が鋼体の衝突で実現されているのに対してこっちはMeshで頂点の当たり判定を細かくやってるイメージ。ただし、softbodyのオブジェクトの自分自身の当たり判定はほとんど突き抜けてしまっているので、判定が無いか、甘い印象。

で、いつもどおりopenframeworksで使ってみたかったので、ofxBulletで移植を始めたんだけど例のごとく先駆者がいらっしゃいました。

https://github.com/damiannz/ofxBullet/

このライブラリではassimpでロードしたメッシュをもとにsoftbodyを作るとことまでやってくれる。とてもいい。

手前のteapodがぐにゃんぐにゃんしてる。やっぱsoftbodyは突き抜けがおおい。このアドオンの調整も色々必要になりそう。あとはこれを動かしたいわけだけど、ノードがわかれば鋼体と同じ要領で特定の頂点に対して力を加えることができるようだ。

softbody ->addForce(const btVector3& forceVector,int node);

パフォーマンスは少し心配。teapotだと、最近のマシンでも10個も入れるとFPSが結構落ちてくる。あと、柔軟体をワールドに投下するタイミングでも、頂点が多いからか一瞬固まってしまうのも気になる。動きは60FPSでてれば結構気持ちがいい。なんか楽しいことはできそうだ。

Realtime slit scanの実装 (openFrameworks)

やってる途中でofxslitscanというopenFrameworks用のslitscanアドオンが見つかった。
http://jamesgeorge.org/ofxslitscan/
なんか想像してたのと違う実装方法があるっぽい。ひとまずソース読ませていただこう思う。

とりあえず、以前のエントリで書いた方法でリアルタイムのスリットスキャン実装をやってみた。
https://github.com/kentaroid/ofVideoSlitScan


うねうね動きます。

プログラムでは下記の変更ができる。
・スリットの数の調整
・スリット間のSmoothing強度(0~1)
・ミラーモード
・スキャン方向のきりかえ、(Up|Down)
・フルスクリーン表示

実際やってみてわかったのは、Smoothingのためにちょっとだけアルファ合成してもぜんぜんきれいにつながらないということ。もっと合成の方法を工夫しなければだめっぽいぞ・・・。

ofxBulletをWindowsで動かす

最近は勉強のためにofxBulletじゃなくて直bulletもやってるよー。

ちょっと前に会社のWorksでopenframework+ofxBullet+OpenNIみたいなインスタレーションを作った。その時にこの素敵addonがWindowsに対応していなくて困った。MacのSTBとかありえないし、LINUXはパフォーマンスが不安だし。とりあえずWindowsへの導入やってみたら、思ったより普通にできた。

せっかくなので、すでにありそうな気がするけど、改めて最新のbullet(2.81-rev2613)でビルドしなおして、サンプルのsln(vs2010)も作った。巷で話題のGitHubとかいうものを使ってみたかったのでこちらで公開しておきます。

https://github.com/kentaroid/ofxBullet

以下、簡単に手順をメモ。

1.bulletをwindowsでビルド
http://code.google.com/p/bullet/downloads/list から最新ソースをダウンロードしてVisualStudioでコンパイル。

2. NickHardeman/ofxBullet をfork
forkしてみたかったんです。ofxBulletのlibフォルダの所定の位置に生成された*.libとかsrcをコピー。これでライブラリの設定はほぼ完了。ただしMacとlinuxの bulletライブラリのリビジョンは違ってるので、ライブラリもコンパイルしなおさないと多分動かなくなる。その場合は元のやつ使ったら良いです。

3. サンプルのビルド
ofxBulletのサンプルの main.cpp,testApp.h,testApp.cpp を使ってopenFrameworksプロジェクトを作成。アドオンソースとかリンカにパスを通しまくる。debugも、releaseも。これが毎度のこと面倒である・・・。いい方法ないのかっていつも思うけど、最近ではライブラリ作った人に感謝しながらこの作業を行うことにしてる。(あなたのおかげでぼくはこんなたんじゅんなさぎょうでこのきのうをつかうことができますありがとうございます。) なんかマクロとかで上手くやってる人がいるらしいよ・・・。

4. ちょっとしたfix
addon、サンプルともに少しのエラーを吐く。
・キャスト方法を明示。 static_cast  -> const_cast
・ちょくちょく出てくる引数の btTransform を明示的に参照渡し。

なんだかMacでみた時と色(light)が違う気がするけど一応動いてる。ちなみに一箇所バグがあって、joint使うとアプリの終了時にAssertionされる。これは終了時に物体だけ破棄されるのにjointが開放されてないとかが原因だった気がする。前使った時は終了時にちゃんとjointでつながった物体をdeleteする前にjointをdeleteするみたいな処理をがっつり書いたら落ちなくなった。この辺も時間があれば見なおしておきます。

Realtime slit scanの実装

スリットスキャンの静止画が撮れるアプリをiPhoneで作ってみたけど、やっぱ動画じゃないと面白くなかった。今回はPCのカメラ画像でリアルタイムにやっみようと思う。

スリットスキャンてこんなやつ。


Slit Scan from Stephen Boehnemann on Vimeo.

以下は、プログラム作成のためのメモ。

スリットスキャンを実装する場合、時間差で過去フレームを再生するわけだから、おそらくスリット数分のフレームを保存して置かなければならない。スリットの数Nとした場合、最低限確保が必要なメモリは下記の通り。

sizeof(unsigned char)*N*3*width*height

仮に、ソースサイズが1024×768で200スリットとすると、

200*3*1024*768 =471,859,200byte=450Mbyte

最近のPCは8Gぐらい普通に積んでるから1Gぐらい使うようなアプリでもいけるのかな?

問題になりそうなのは結果画像の生成速度。各フレームのリード自体は画像一枚分だから、そこまで重くないのかもしれない。スリットの動き・形状もいろいろ考えられるけど、リアルタイム処理の場合は上から下、または下から上ならピクセル配列的にも高速に処理できそうだ。

スリット幅が1pxを超えると、おそらくフレーム間のズレが目立つため理想はN=heightなのかな。その場合メモリは1.68GByte、 スレッドの遅延は768/30fpsで20秒以上かかってしまうことになる。ちょっと遅すぎるしメモリも絶望的だなぁ。とりあえずスリットの幅を広げてフレーム間をアルファ合成する方法でやってみようと思う。あとはカメラが30のFPS出ないようであればフレーム間がガタガタになってしまいそう。

この辺りの落とし所は探りながら。相当パワーのあるマシンが必要になりそうだ。

実装は次回。