2008年12月12日金曜日

bool型のマーシャリングのワナ

マネージコードでのbool(=System.Boolean)型の中身は
4バイトの整数(false=0, true=o以外)で、
慨定のマーシャリングの動作では4バイト整数としてアンマネージコードに渡される。

が、

OpenGLでのGLBoolean型は、調べてみると、
「1バイト符号無し整数」。
つまり、マーシャリング時に型の不一致が起こってバグる。
解決方法は、

(1)
[MarshalAs( UnmanagedType.U1 )] という属性を付加して、
boolが1バイト符号付き整数(true=1, false=0)に変換されるように指定する。

(2)
bool型の代わりにbyte型を使い、
true = 1, false = 0へ置き換える。

・・・のはずなんだけど、
(1)の方法で、bool[]型に、
[MarshalAs( UnmanagedType.LPArray, ArraySubType = UnmanagedType.I1 )]
という属性をつけてやってみたら、
どうもうまくいかない。
ArraySubType = UnmanagedType.U1でもダメだった。
1バイト整数に変換されず、4バイトのままアンマネージコードに渡されてるっぽい。
(2)の方法でbool[]の代わりにbyte[]を使うとうまくいく。
なんか属性の指定方法が間違ってるのだろうか?

2008年12月10日水曜日

どう使うかが、難しい。

一応、バッファオブジェクトのクラス化はできた。
まぁ、ここまではそれほど難しくもない。
問題は、これをどう使うか。

前々から、GLSharpのMeshクラスを
もっとマシなものに作り直したいな、とは思っていて、
頂点配列(glVertexPointerとか)やバッファオブジェクトを使って
うまいことやってみたいな、とは考えているのだけれど、
さて、どうしたものか・・・。

2008年12月3日水曜日

バッファオブジェクト

何となく気が向いたので、
バッファオブジェクトに手をつけてみることにした。
で、GLSharpへの実装も視野に入れて
赤本片手に、バッファオブジェクトを扱うクラスを試作中。

・・・といっても、まだバッファオブジェクトの扱い方すら
よくわかってないけど。

んん、OpenTKの
glBufferData、glSubBufferData関数のインポートが・・・

GL.BufferData( BufferTarget target, IntPtr size, IntPtr data, BufferUsageHint usage );
GL.BufferData( BufferTarget target, IntPtr size, object data, BufferUsageHint usage );
GL.BufferSubData( BufferTarget target, IntPtr offset, IntPtr size, IntPtr data );
GL.BufferSubData( BufferTarget target, IntPtr offset, IntPtr size, object data );

IntPtr型でサイズやオフセットを指定しろと?
いや、それはちと違うんでない?

2008年11月27日木曜日

線形代数

ベクトルとか、行列とか、
どうしたって3Dグラフィクスやるには避けて通れないわけで。
が、しかし、いつぞやに受けた
線形代数の講義は今ひとつよくわからんかったし、
いまでは内容もほとんど記憶にない。

で、改めて勉強しなおしておこうかなー、と思って、
線形代数の教科書じゃよくわからんし、
なんかいい参考書がないかと思って探していたのだけれど、
こんなのを見つけた。



なんというか、こう、地に足の付いた感じのする説明が非常にわかりやすくて、いい。
常に行列の幾何学的な意味を意識して書かれているので
具体的なイメージがつかみやすい。
あんまり「数学」っぽい雰囲気もなくて読みやすい。
面倒な証明のたぐいはかなり控えめに書いてあって読み飛ばせるし、
行列を実際に道具として使う事を念頭に書かれている。

Amazonのレビューでもなかなか好評のようだし、
行列を使いたいから勉強したい(しなおしたい)、と思っているなら、
この本はかなりおすすめかも。

ただし、タイトルには
「プログラミングのための」とあるけれど、
あんまりソースコードとかは出てこない。
あくまでも"プログラミングのための"線形代数が中心であり、
"行列計算をプログラミングする本"ではない。

2008年9月27日土曜日

とりあえずできたが、

とりあえず、GetGlyphOutlineでグリフイメージを取得してテクスチャにして、四角いポリゴンに貼るところまではできた。

で、右の画像。
左側が、文字テクスチャで、
フォントサイズが100Point、
5階調のアンチエイリアス、
サイズは128*128pixel、
α値のみのテクスチャ。

比較として、右側は、
同じくGetGlyphOutlineで
アウトラインフォントをポリゴン化したもの。
曲線部分は3つの直線に分割して近似、
ポリゴン数106、頂点数104。
右下の小さいのは、これのワイヤーフレーム表示。

ここまで大きく表示すると、
さすがにフォントサイズ100でも、荒さが目立つ。
それに対して、ポリゴン化したやつは、近似が荒いわりには意外ときれいに見える。
メモリのサイズとしてはポリゴン化のほうが省メモリっぽいが、
速度的には、やっぱりテクスチャのほうが速いのだろうか。

んー、
128*128pixのαチャンネルのみだから、メモリサイズは約16.4kB、
半角文字ならサイズ半分なんだろうけど、そこはひとまずおいといて、
256文字だと合計で約4.2MB・・・。
まぁこのくらいなら、今時ならそれほど問題にならないサイズだろうか。

2008年9月21日日曜日

GL_BITMAPなテクスチャは不可

先日の件の
GetGlyphOutlineで取得したグリフのビットマップ(アンチエイリアス無しのやつ)を
そのまま使ってテクスチャにしてみようと思って
いろいろいじってみていたのだが、

glTexImage2D( GL_TEXTURE2D, 0, GL_ALPHA, 0, width, height, GL_ALPHA, GL_BITMAP, texels );

ってかんじで、ビットマップなテクスチャを作成しようとしたら、
エラー(GL_INVALID_ENUM)になった。
どうやら、最後から2番目の引数"type"にGL_BITMAPを指定できないらしい。

公式ホームページの関数リファレンスのページをみると、
引数"type"に指定可能な定数のリストに、GL_BITMAPが含まれているのだが、
関数リファレンスのページの最後のほうの「ERROR」項目に、
"GL_INVALID_ENUM is generated if type is GL_BITMAP and format is not GL_COLOR_INDEX."
と書いてある。

・・・つまり、
引数"type"にGL_BITMAPとGL_COLOR_INDEXは渡せるけど、
"format"がGL_COLOR_INDEXでないとエラーになるぞ、と。

ビットマップなテクスチャならメモリサイズも小さくて済むかなー、
とか目論んでいたのだが、無理なようだ。

2008年9月12日金曜日

OpenGLでアンチエイリアスが掛かった文字を描く

Windows環境において
OpenGLでテキスト描画をしようと思うなら
"wglUseFontBitmaps" でビットマップの文字を描くという方法があるわけだが、
この方法だと、描画される文字はビットマップ(文字通りの0か1のみのbitのマップ)ゆえに、
アンチエイリアスが掛からない。

そこで、
アンチエイリアスが掛かった文字を描くにはどうしたらいいのか? 
と。

以前、文字をポリゴン化したメッシュを作成しようとしたときに、
アウトラインフォントの輪郭線を取得するのに
"GetGlyphOutline"
というwin32 API を使ったのだが、
たしか、この関数って、アンチエイリアスの掛かったグリフのビットマップなんかも
これで取得できるんだったよなー、
というのを思い出したので、
その辺りをググって調べつつ、やってみた。

おおざっぱな方針としては、
まず、アンチエイリアスの掛かったグリフのイメージを取得して、
それをDrawPixelsで描画、をディスプレイリスト化。
という感じ。

・・・これだと、
glBitmapみたいにglColor*で
テキストの色を変えることができないけど、
まぁ、しかたがないか。


<手順その1>
まずは、バッファを渡すところにnullを渡してやると、
必要なバッファのサイズを返してくれるので、
そうして取得したサイズぶんだけbyte配列(バッファ)を作成。

IntPtr prevObj = win32.SelectObject( hDC, font.ToHfont() );

uint bufferSize = win32.GetGlyphOutline( hDC, (uint)character, format, out metrics, 0, IntPtr.Zero, ref matrix );

byte[] buffer = new byte[bufferSize];


<その2>
さっきのバッファを渡してもう一度GetGlyphOutlineを呼び出せば、
バッファにアンチエイリアスの掛かったグリフのイメージが書き込まれる。


GCHandle gchBuffer = GCHandle.Alloc( buffer, System.Runtime.InteropServices.GCHandleType.Pinned );

uint ret = win32.GetGlyphOutline( hDC, (uint)character, format, out metrics, bufferSize, gchBuffer.AddrOfPinnedObject(), ref matrix );

gchBuffer.Free();


<その3>
バッファに書き込まれた値は0~255にはなっていなくて、
formatフラグでい指定した階調の値そのままが入ってる。
つまり、17階調なら0~16の値が入ってるので、
255/16とかでスケーリングする必要がある。
(注:MSDNには、GGO_GRAY8_BITMAP(65階調)を指定すると
0~255の値が入るって書いてあるけど、それは嘘。
実際にやってみると、0~64の値が入ってた。)

さらに、グリフイメージの左上から、という並びで書き込まれているので、
このままでは上下反転した文字になってしまうから、
これもまた、ひっくり返しておかないといけない。

あと、グリフイメージの幅は「4バイト境界」になってて、
metrics.blackBoxXと一致してないので、そこも要注意。
(これ、MSDNのリファレンスに書いてなくて、結構悩まされた・・・。)
つまり、バッファに入ってるイメージの幅は、
metrics.blackBoxXが収まるのに必要十分な4の整数倍、ということ。

<その4>
さて、グリフイメージが取得できたので、
ここからはOpenGLの部分。

glDrawPixelを使うわけだが、
文字が描かれていない部分はα値を0にして透明にするのが賢明であろうから、
フォーマットとして使用できそうなのは、
GL_RGBA、GL_LUMINANCE_ALPHA、GL_ALPA
の3つだろう。
GL_RGBAなら色を指定できるが1pixel辺り32bitのメモリが必要、
GL_LUMINANCE_ALPHAだと、1pixelあたり16bitだが色が白のみになる。
GL_ALPHAだと1pixelあたり8bitだけど色は黒になる。
どれにするかは、用途によりけり、といったところだろうか。
普通はRGBAだけど、白か黒の時はLUMINANCE_ALPHAやALPHAにする、
とかが、いいのだろうか。

後は、glDrawPixelをディスプレイリストにしてしまえばできあがり。

くれぐれも、毎フレームごとにglDrawPixelを呼び出したりしないこと。
これをやってしまうと、
いちいちプロセッサメモリからビデオメモリにピクセルデータを転送することになり、
非常に遅くなる。
それに対して、ディスプレイリスト化すると、
イメージデータはビデオメモリにコピーされるので、転送する手間が無くなり、
割と高速に描画されるようになる。

ちなみに、
"glPixelTransfer"関数と"glPixelZoom"関数を
glDrawPixelの手前で使えば、
<その3>でやる値のスケーリングや上下反転の操作を
ピクセル転送処理として行うことができる。
が、そのぶん、パフォーマンスが悪くなる。

2008年9月4日木曜日

ほぼ日手帳

ここ3~4年、毎年買ってるのだが、
今年もいよいよ販売が開始されたので、
早速注文してきた。
今年は、ファブリックカバーの
マキノ・ヘリンボーン。

しかしまぁ、すごいね。ほぼ日手帳。
毎年、ユーザーの意見を取り入れて着実に改良を加えて、
やたら細かい部分までこだわって作ってるのがいいね。
あと、なぜか「マンガ」とか
「この手帳についていないもの」のページがあるところが、
いかにも、ほぼ日らしい。

2008年8月3日日曜日

System.Collections.Generic.List<T>を継承するクラスを作ってはいけない

http://msdn.microsoft.com/ja-jp/library/ms182142.aspx

リストにアイテムが追加されたときにイベントを起こしたいとか、
そういうクラスを作成したい場合は、
System.Collections.ObjectModel.Collection<T>
から派生させろ、ということらしい。

・・・なるほどね。

2008年7月19日土曜日

これは読んどけ : Code Complete

たまには話題を変えて、
本のことでも。

Code Complete 第2版 上 (日経BPソフトプレス)


ソフトウェアコンストラクションについて書かれている。
プログラミングをそれなりやろうと思うのであれば、
これは一度は読んでおくべき良書だと思う。

この「ソフトウェアコンストラクション」という
聞き慣れない言葉が意味するところは、
いわゆる「プログラミング」とか「コーディング」ということらしい。
つまりはコーディングのテクニックや
ノウハウ的なことについて書かれている本である。
もうちょっと具体的にいうと、
クラスを作成するときはこういう事を考えて作れだとか、
変数名はこういう付け方をするべきだとか、
そいう事。
このコンストラクションについて
ここまで詳しく丁寧に述べられている本は
そうは無いだろう。

ちょっとでかくて価格が高めの本ではあるが、
文章はとても読みやすくて理解しやすいし、
内容も充実していて読み応えがある。

とにかく、ひと言、
これは読んでおけ
と言いたい本だ。

2008年7月11日金曜日

GLSharp更新しました。

とりあえず、いろいろ作ってたのが一段落したので、
GLSharpの新しいのをHPのほうにアップしました。

ここでは書いてなかった気がするが、
アニメーションの基礎部分なんかも実装しました。
まだ、幾分改良の余地はありそうですが。

フレームレート制御ややこしいな、と思ってたのだが、
YaneSDKになんかそんなのがあったな、と思いだして、
FPSTimerクラスをいいように使わせていただきました。
なかなか使い勝手が良いです。
ただ、そのままだと、時間計測にSDLを使ってるので、
FPSTimerのためだけにSDLを組み込むのも面倒なので
(っていうか、SDLの使い方知らんので)
System.Diagnostics.Stopwatchで時間を計測するように改造しました。

2008年6月18日水曜日

放置してたフルスクリーンモードの件、

前に、フルスクリーンモードにすると
なぜかわからないが描画速度が極端に遅くなってしまう、
と、書いていたが、
一応解決策が見つかった。
・・・たぶん。

これまた、なぜかわからんが、
フルスクリーンモードの時に
wglMakeCurrent関数を呼び出すと、
極端に描画が遅くなるっぽい。
(glGetErrorでエラーをチェックしても、エラーは出てない。)
普通はこの関数を呼び出してレンダリングコンテキストを設定してから
フレームの描画を始めると思うのだが、
たまたまこれを呼び出すのを忘れたまま実行したら、
描画速度が落ちずにスムーズに動いた。

とにかく、
フルスクリーンモードの時は、
毎フレームごとの描画時に
wglMakeCurrent
を呼びだしてはいかんらしい。
・・・なぜかはわからないが。

2008年6月9日月曜日

VisualStudioのデザイナに対応させてみた

OpenGLで描画するためのコントロールを用意して、
初期化時のピクセルフォーマットの指定をデザイナ画面で設定できるようにしてみた。
ちゃんとアンチエイリアスの指定とかも(ハードウェアでサポートされていれば)できます。



自分で作成したクラスなどをデザイナで扱えるようにするためには、
プロパティやクラスにデザイナ用の機能を提供する属性をくっつけていけばいいらしい。
参考資料はこちら。
http://www.microsoft.com/japan/msdn/net/general/vsnetpropbrow.aspx

PixelFormatDescripterクラスなどに
けっこう手を加える必要があったが、
なかなかおもしろいものができたかも。

2008年6月1日日曜日

気が変わったので

GLSharpに、
アンチエイリアス等のOpenGLのエクステンションを使用した
レンダリングコンテキストの作成機能を実装してみた。
サイトへのアップはもう少しかかります。


// * class Form1 : Form の中。

RenderingContext context;
protected override void OnHandleCreated( EventArgs e )
{
base.OnHandleCreated( e );

PixelFormatDescriptor pfd = new PixelFormatDescriptor();
PixelFormatAttributes pfa = new PixelFormatAttributes();
pfa.SampleBuffers = true; //←ここと
pfa.Samples = 4; //←ここで、マルチサンプリング(=アンチエイリアス)を設定。
IntPtr hdc = win32.GetDC( this.Handle );

context = new RenderingContext( hdc, pfd, pfa, null );
context.MakeCurrent();
gl.LoadAll();
glu.LoadAll();

if( context.Format.Attributes != null && context.Format.Attributes.Samples != 0 )
gl.Enable( EnableCap.Multisample );
}


と、こんな感じで使えるようになった。
PixelFormatAttributes、ContextFormatというクラスを新たに作って、
RenderingContextクラスのコンストラクタを作り直した。
・・・とかまだ公開してないコードの説明書いても、
しょうがないのだけど、
まぁ、予告ということで。

2008年5月27日火曜日

アンチエイリアスも・・・

OpenTKのソースコードのWinGraphicsModeクラスに、
未実装のSetGraphicsModeARBなんてのがあるあたりをみると、
アンチエイリアスなどの拡張機能に対応した
レンダリングコンテキストの初期化を
実装するつもりがあるっぽい。
まぁ、まだバージョンも0.9.1で1未満だし、
今後実装されることを期待。

・・・ということで、
アンチエイリアスも
放置。

せっかくだから、
資料へのリンクだけでも、
どっかにまとめておきますか。


あ、
ホームページに資料ページを作ろうとしてたら
思い出したのだけど、
そういやホームページのほうの
NURBSのページを放置してたから、
そのへんをやってみようかな・・・?

2008年5月26日月曜日

今度はアンチエイリアス。

資料が見つからないので、
ひとまず、フルスクリーンは放置。
まぁ、特に必要に迫られているわけでもないので。

で、何となく、
今度はアンチエイリアスを調べてみた。

OpenTKではGLControlのコンストラクタの
引数に渡すGraphicsModeクラスのインスタンスを通して
アンチエイリアスのサンプリング数を指定できる。
が、
アンチエイリアスが有効なレンダリングコンテキストを作成する機能は
実装されてないので無視される。

で、自分で資料を探してみると、
NeHeのLesson46 Fullscreen AntiAliasing
http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=46
ってのを見つけた。

コードを書いて試してみたら、
wglChoosePixelFormatARB関数で
有効なピクセルフォーマットを取得するところまではうまくいった。
さて、GLSharpで実装するか、
OpenTKを改造するか・・・。

OpenTkのレンダリングコンテキストって、
ややこしくって、よくわかんないんだよなぁ・・・。

2008年5月24日土曜日

続・フルスクリーンモード・・・

OpenTKのソースコード眺めてみると、
OpenTK.Platform.Windows.WinDisplayDeviceDriver
(DisplayDeviceDriverのWindowsでの実装。)
というクラスの中で、
ChangeDisplaySettings関数使ってるし。
前にも書いたディスプレイの解像度変更は
これを使って実装しているようだ。

ってことは、わざわざ自分で
ChangeDisplaySettings関数を
使う必要はないのか。
それどころか、
DisplayDeviceクラスを通して
知らずして使っていたということか・・・。

このあいだ書いた、
描画速度が極端に遅くなる 、という症状は、
DisplayDeviceクラスを使って
ディスプレイの解像度を変更したときではなく、
どうも
.NET TIPS Windowsアプリケーションをフルスクリーンで表示するには?

に書いてある方法でフルスクリーンにしたときに起こるようだ。
つまり、
別の方法でフルスクリーンにしろ、
ということか。

2008年5月22日木曜日

フルスクリーンモード ただいま調査中・・・。

そういや、
CSGLにフルスクリーンモードの機能がある
とかいうのを聞いた覚えがあったので、
CSGLのソースコードを眺めていたら、
「ChangeDisplaySettings」
というWin32 APIにたどり着いたのだが、
どうもこれっぽい。

試しに
「ChangeDisplaySettings フルスクリーン」
でググってみるとMSDNとかそれっぽいのが
すぐに出てきた。

C#でWin32 APIをいじるときは、
http://www.pinvoke.net/index.aspx
ここが便利。
英語だが、ソースコードやサンプルコードなど
必要なものはたいてい揃っていると思います。

さて、
これでフルスクリーンモードを実装するのに
必要なものは揃った・・・か?

2008年5月17日土曜日

OpenTKでフルスクリーンモード

OpenTK、結構いい感じになってるっぽい。

Ver. 0.9.1になって追加された
「DispreyDevice」というクラスがあるのだが、
これを使うと、その名の通り、
ディスプレイデバイスを簡単に扱える。
具体的には、
ディスプレイの解像度やリフレッシュレートなどを
たったの1~2行で変更したり、元に戻したりできる(!)。

DisplayDevice[] disp = DisplayDevice.AvailableDisplays;

でディスプレイデバイスをリストアップして、
例えば、解像度640*480ピクセル、色32bit、リフレッシュレート60Hzなら、
disp[0].ChangeResolution(disp[0].SelectResolution(640, 480, 32, 60.0f) );

元にもどすなら、
disp[0].RestoreResolution();

これだけでOK。

これに加えて、
http://www.atmarkit.co.jp/fdotnet/dotnettips/199fullscreen/fullscreen.html
を参考にしてフルスクリーンモードの切り替えを実装すると・・・
あっという間に解像度を指定した
フルスクリーンモード切り替えができあがり。


・・・と思ったんだけど。
なんだか極端に描画速度が遅くなるようなので、
ダメっぽい。
もうちょっと、ちゃんと調べてみます。

2008年5月14日水曜日

OpenTK ver.0.9.1

OpenTKがバーションアップしたらしい。
ネームスペースの変更とか、
いろいろあるようだが・・・

ver.0.9.0同様、
やっぱりGLクラスにGetBooleanv関数が無い。
代わりに「GL.NV.GetBoolea」とかいうのがある。
なんでなおらんのだろうか・・・。

まぁ、まだ落としてきたばっかりだし、
GLSharpも新しいバージョンに対応させたいし、
新機能なども幾つか実装されたようなので
ぼちぼち、じっくり見てみますかな。

2008年5月13日火曜日

再開しました。

ども。
一歩二歩散歩というモノです。

ひとまず、
場所を変えてブログを再開しました、
というご報告まで。