2014年

昨年は下半期、アンビエントオクルージョンを目標にしていたんだけど、かなり時間かかったけど達成できたのでよかった・・・。アンビエントオクルージョンて1000回ぐらい言った気がする。響きがいいよ。

今年の目標は、実用的なシャドウマッピング。LiSPSMを軸に改良していこうと思っている。そして去年からやってるレンダリングシステムの完成。これらを使って、いい感じの作品を作りたい。iPhoneアプリの新作も作りたいなー。

スタンフォードバニーとかティーポッドばっかでいい感じのキャプが取れないからブログも書く気しないんだよなぁ・・・。今年はまず昨年度実装したレンダリングシステムをまとめてから、シャドウの実装にとりかかっていこうと思う。

がんばろ。

あ、あとアフターエフェクトつかえるようになりたいわ・・・。

CubeTexutureのシームレスなMipMap

くそハマった・・・。

IBL(image based ligting)における光源にCubeTextureのMipMapを使うとき。(もちろんIBLじゃなくても。)
CubeTextureはそのままではそれぞれのイメージで独立した画像としてMipMapを作成するため、高レベルのMipMapを使うとCubeが綺麗に繋がらない。もうこれがどうやったら回避できるのか、諦めてMipMap用のシェーダー書き始めたところで発見した・・・。

CubeTexutureのシームレスなMipMapは下記1行。

glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS);

IBL実装したら写実感がめっちゃアップした。

HarfBuzzのStaticライブラリを作る。winとmac。

これも結構ハマったのでメモ。Staticである理由は特に無いのだが、個人的にはこっちのほうが好き。(ライセンスによってはDynamic利用だけフリーとかが結構あるので注意。)

まずはgithubから最新のコードを取得。

https://github.com/behdad/harfbuzz

僕の開発環境はVisualStudioがメインなので、いろいろコンパイルを試みるが、エラーだらけでどうにもならない。ファイルを色々読んでみたらどうやらmakeして生成するヘッダが存在してる。ならばMACでやることにする・・・。

ドキュメントに従ってターミナルでやってみる・・・。
pkg-config と ragelが必要らしい。これはyumとかmacportsで入れとけばいい。
configureするときにfreetypeオプションをつけるとhb-ht関連が含まれるようだ。その場合freetypeも事前に入れとく。–enable-staticを指定すれStaticで作れる。

$ ./autogen.sh
$ ./configure --with-freetype=yes --with-glib=no --enable-static CXXFLAGS="-arch i386 -arch x86_64" CFLAGS="-arch i386 -arch x86_64" LDFLAGS="-arch i386 -arch x86_64"
$ ./make

少しこの辺りの補足を。XCodeではOSX Deployment targetでビルドアーキテクチャが選択されているようだがopenframeworksのデフォルトターゲットは10.6(i386)となっている。marvericksで指定する場合はx86_64となるが、今のところこの設定では一部でエラーが出る。こういったアーキテクチャの差異に対応すべくstaticライブラリはuniversal化しておく必要がある。./configureのオプションでFLAGS=”-arch i386 -arch x86_64″とするだけでuniversalライブラリを生成することができるようだ。依存するfreetypeなどもuniversalで用意しておく必要がある。出力されたものをfileコマンドで確認すると下記の通り。i386、x86_64が構成されていることが分かる。

$ file *.a
libharfbuzz.a: Mach-O universal binary with 2 architectures
libharfbuzz.a (for architecture i386):	current ar archive random library
libharfbuzz.a (for architecture x86_64):	current ar archive random library

make intallはしなくてもいい。makeした時点で src/.libsにharfbuzz.aが生成されている。macの方はこれを使う。ヘッダは下記がコピーされるのでincludeディレクトリに入れておく。

hb-blob.h, hb-face.h, hb-ot-layout.h, hb-shape-plan.h, hb.h,
hb-buffer.h, hb-font.h, hb-ot-tag.h hb-shape.h,
hb-common.h, hb-ft.h, hb-ot.h, hb-unicode.h,
hb-deprecated.h, hb-glib.h, hb-set.h, hb-version.h

次はWindowsへ移動。

macでmakeしたのでいろいろソースが書き換えられていて、vsでもコンパイルが通る。余談だが、ライブラリをビルドするVisualStudioのバージョンは高い方が良いようだ。下位バージョンでビルドしたライブラリを新しいバージョンで使うことはできないが、その逆、上位バージョンでビルドした場合は下位のVisualStudioで利用可能になってそう。(確かな情報ではなく、僕の経験した範囲では。Dynamicも事情は違ってそう)よって最近はライブラリのコンパイルを行う場合はVisualStudio2013を利用している。

話をもどして、VisualStudio2013でコンパイル手順。
まずは Win32プロジェクト-スタティックライブラリで新しいプロジェクトを作成する。SDLチェックオプションを有効にするとエラーになる部分が2箇所あるのでこのチェックは外しておく。プログラム全体の最適化を有効(/GL)にしている場合vs2010の時は利用側で警告がでていた。2012以降は出てないのでデフォルト(有効)で良さそう。

追加するソースについては、必要機能で良い。今回はこんな構成。glib,icu,test関連を省いている。(必要な場合は依存を解消してコンパイルすれば良い。)

スクリーンショット-2013-11-20-19.47

ソースフォルダと、ビルドしてエラー表示された部分にパスを通しておく。
コンパイルオプションについては、重要なポイントとしてプリプロセッサの定義が要る。

HAVE_OT
HAVE_UNISCRIBE

指定した機能を有効にしてコンパイルされる。

諸々調整を行い、うまく行けば libharfbuzz.libが作成される。(デバッグはlibharfbuzzd.libとする)
これを現在作成中のTypographyのライブラリに追加してみる。

そしたら、下記のエラーが。UNISCRIBEを追加したため、関連するDLLの読み込みが発生しているようだ。


エラー 6 error LNK2001: 外部シンボル "_ScriptShape@40" は未解決です。 D:\Dropbox\project files\openframeworks\addons\ofxTrueTypeFontUL2\example\libharfbuzz.lib(hb-uniscribe.obj) emptyExample
エラー 5 error LNK2001: 外部シンボル "_ScriptPlace@36" は未解決です。 D:\Dropbox\project files\openframeworks\addons\ofxTrueTypeFontUL2\example\libharfbuzz.lib(hb-uniscribe.obj) emptyExample
エラー 8 error LNK2001: 外部シンボル "_ScriptItemize@28" は未解決です。 D:\Dropbox\project files\openframeworks\addons\ofxTrueTypeFontUL2\example\libharfbuzz.lib(hb-uniscribe.obj) emptyExample
エラー 7 error LNK2001: 外部シンボル "_ScriptFreeCache@4" は未解決です。 D:\Dropbox\project files\openframeworks\addons\ofxTrueTypeFontUL2\example\libharfbuzz.lib(hb-uniscribe.obj) emptyExample
エラー 9 error LNK2001: 外部シンボル "__imp__UuidCreate@4" は未解決です。 D:\Dropbox\project files\openframeworks\addons\ofxTrueTypeFontUL2\example\libharfbuzz.lib(hb-uniscribe.obj) emptyExample

Rpcrt4.lib;usp10.lib;をリンクする必要があるらしい。
この場合こちらのプロジェクトの設定でリンクしてもいいのだが、libでやってしまうのがスマート。やり方は、ライブラリアンの追加の依存ファイルでlibを指定する。利用する側はほんの少し(笑)楽になる。

スクリーンショット 2013-11-20 19.40.53

以上が今回やったライブラリの作成手順。手探りでやってるので正しいのかはわからないけど。ようやくwindowsで開発していたものを両方で動かすための環境が整った。
VisualStudioは毎度こういうので疲れる・・・\(^o^)/

harfbuzzでAlabia語レンダリング

今回はRTLのレンダリングをやってみた。右寄せではなく、Right to Left、つまり右から左に書くarabia語などのレンダリングなど。(「はちにんこ」、みたいな。)

普通に hb_buffer_set_direction( buffer, HB_DIRECTION_LTR ); でやればいいだけなんだけど。この指定を行った場合、普通にイテレーションすると、文末から順番に表示されることになる。普段のLTRのロジックでレンダリングした場合は左寄せになる。改行もいれると、文末から文頭へと表示されて、一応RTLになってるんだけど、基準点によるレンダリングが難しい。(boundingboxを計算しているが2回計算することになり効率が悪い。)

そこで、レンダリングロジックでリバースイテレーションすると、右寄せになり、改行順も正しくなる。注意点はレンダリングとペンの位置の計算順。

LTRの場合は :  レンダリング( PenX+OffsetX ) -> ペン移動( PenX += AdvanceX )
の順で描画を行っていくが、リバースの場合はAdvanceXが示す値がそのキャラクター自身のPEN移動となる。
よってRTLの場合は :  ペン移動( PenX -= AdvanceX ) -> レンダリング( PenX+OffsetX )
このようにレンダリングすることで右寄せとなり、正確に表示された。

ここまででharfbuzzのLTRは完了。日本語や英語についてはリガチャも含めて綺麗に表示された。次にアラビア語を入れてみる。表示結果は以下のとおり。アラビア語でググって出てきた文章なのでなんて書いてあるかは知らない(過激な言葉だったらどうしよう)そして、これがぜんぜん正しいのかも分からないけど、元のタイポグラフィと比べるとおかしい。

スクリーンショット-2013-11-15-11a.46

テキストレイアウトでは色々必要とされる機能があってそれが各言語によって用意されている。これらはiso15924のタグで用意されていて、harfBuzzでもこのコードからスクリプトを適用することができる。
http://www.microsoft.com/typography/otspec/scripttags.htm
アラビア語を指定する場合は
hb_buffer_set_script(buffer,HB_SCRIPT_ARABIC);
これは、hb-common.hで定義されている。この指定を行うと、下記の通り。バラバラだった文字が組み合わされ、それっぽくなった。これは単純なリガチャではなく、合字のような機能だと思う。スクリプトは標準ではLatin用とかになってるのかなー。

スクリーンショット-2013-11-15-11.46

hb_buffer_guess_segment_propertiesみたいなのもあるんだけどうまくは行かなかった。
それで、今回は混合フォントのレンダリングクラスを書いているんだけど、フォント毎に”arab”とかiso15924を調べて指定するのはなんか面倒。なんとかフォントファイルからscript tagを調べられないかと、色々試行錯誤してみた。

まずはどこにタグがあるのかを調べる。
http://kanji-database.sourceforge.net/fonts/opentype.html
これによると
GSUB、GPOS、JSTFの第一層にある。まだ、頭のなかにopentypeの構造がぼんやりとしかイメージできていないので、どこやねんそれと思いながら試行錯誤してみた。結果、こんなかんじでよさげだなぁと。自信はないけど。

unsigned int cntGSUB = hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, NULL, NULL);
hb_tag_t* listGSub = NULL;
listGSub = (hb_tag_t*) malloc(cntGSUB * sizeof(hb_tag_t));
hb_ot_layout_table_get_script_tags(face, HB_OT_TAG_GSUB, 0, &cntGSUB, lstGSub);
hb_script_t script = hb_script_from_iso15924_tag(*lstGSub);
free(listGSub);

大概のフォントは複数のスクリプト値が入っている。秀英体とかだと6ことか入ってたりする。で、どれを初期値にするのかというのが問題になるんだけど、どうやら一番はじめのものがデフォルトレイアウトとして使えそう。アラビア語の traditional arabicフォントも一番目に HB_SCRIPT_ARABIC が入っていた。上の例はHB_OT_TAG_GSUBだけだけど、GPOSのみということもありえるのでJSTFも含めて調べておく必要がある。それで取れなければHB_OT_TAG_INVALIDとかにしておけば、上手いことやってくれそうな気がした。

リガチャー処理 (harfbuzz + freeType)

リガチャーが実装できた。

freeTypeありきで実装していたから色々勘違いがあったが、ようやく理解できた。手順をおさらいしておく。

簡単に言うとFreeType自体はあくまでFontFaceを弄るものだから、文字の組み合わせによって整形されるリガチャーの判断はできるわけがなく。文章を表示する場合harfbuzzでグリフのコードポイントを取得することになるのであるが、この時点でリガチャのコードポイントが返ってくる。よってFreeTypeにおいてはhb_glyph_info_t から取得したグリフ位置で表示を行えリガチャ表示が可能となる。

リガチャはデフォルトで有効になっているようだが、下記のように指定できる。
hb_feature_t feature= {HB_TAG(‘l’,’i’,’g’,’a’),1,0,static_cast<unsigned>(-1)};

リガチャーキャラクタは複数文字をひとまとめにして表示するため、hb_buffer_get_lengthが戻す値は実際の文字長よりも短くなる。カーニング値も2文字(or 3文字)分のアドバンスが確保される。リガチャの判断は次のhb_glyph_info_tポインタ のcluster値がジャンプするかどうかで行える。混合フォントを設計していたため、文字のポインタが取りづらく処理が複雑になったが、クラスタをもとにカウンティングを行うことで実現できた。

いざ自分でやってみると、普段何気なく見ているPC上のテキストがだいたいリガチャー効いててスゲーってなった。先は長い・・・。

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化しよっと。

OpenMPでプチ高速化

久しぶりにOpenMPをやってみたのでメモ。GPUシェーダ、CUDAとか使ってみてるけど、OpenMPが一番簡単にできる並列コンピューティングの環境だなと。でもCUDAは体感がすごかったけどOpenMPではイマイチ早くなった気がしないよ。最適化が不十分なんだろうけど。

使い方のメモ。*VS2010
構成プロパティのC/C++、[言語]のなかのOpenMPサポート有効にする

mpsetting

ヘッダインクルード

#include <omp.h>

ソース多分こんなかんじになった。
ピクセル内部で何かを探す処理。Y移動を並列化するやつを抜粋。

	int y;
	const size_t height= 1024*2;
	const size_t width = 1024*2;
	bool founded=false;
#ifdef _OPENMP
	omp_lock_t myLock;
	omp_init_lock(&myLock);
#pragma omp parallel for
#endif
	for (y=0;y<height;y++) {
		int x;
		float rf,gf,bf;
		bool flg;
		for (x=0;x<width&&!founded;x++) {
			//処理
			if(発見)founded= true;
		}
#ifdef _OPENMP
		omp_set_lock(&myLock);
#endif
		if(!founded){
			founded=true;

#ifdef _OPENMP
			printf(" [OMP:%i]", omp_get_thread_num());
#endif
		}
#ifdef _OPENMP
		omp_unset_lock(&myLock);
#endif
	}

ソースの改修が少ないまま並列化できるのがメリット。並列スレッド数は多分最近のPCでも8とかだからCPUコア数依存かな?。色々試したところ、上記のようにループサイズが小さいとあまり効果がない気がした。割と処理大きめに並列化したほうが良さげだと感じてる。今回の並列で検索するロジックはどこかで見つかった場合処理を終了させるため、見つからないスレッドが破棄され無意味になっている。それぞれで別検索を行わせて各スレッドに結果が出せるように組んだほうが良いと思った。何ゆってるかわからないけどそういうことだと思う。あと、

・ループ外側で定義する変数は共通化されるので定義する場所に注意。
・共用変数の変更はロックが使える。
・goto,breakは内部では使えるけど、脱出はできない。
 なんかそれ専用のコマンドがあるっぽい。
・Forの終了条件は確実性が求められる。 

丁寧にコーディングすればあまり間違いがおこならいので、ちょっとした並列化には効果ありそう。次回は色々ベンチマークとって効果的な書き方を検証していこうと思う。
タスクマネージャ。8コア全部が100%に。こうやって見ると仕事してる感は出てるよね。
でもやっぱGPGPUのがおもしろいなぁ。

mpsetting