UpDate:--/--/--我が家のモニターが新しくなりました。憧れの19インチモニターです。17インチ:1280x1024x60Hz(フォント大)→19インチ:1280x1024x85Hz(フォント小)にレベルアップ。目にやさしくていい感じです。リフレッシュレートが高いと目にやさしいご様子。そんなわけで今回はリフレッシュレートのお話。実は私の周りには視覚系の研究をやってる人が結構います。彼らが実験をやる時に気にするのがVsyncとリフレッシュレートなんですね。Windowsでこの辺りをいじろうと思うとDirectXがお手軽で良さそうなんですが、なぜかDirectX7以前のバージョンではろくすっぽリフレッシュレートの設定が出来ません。SetDisplayModeなる関数の引数で指定できそうな感じなんですが、これで成功した話を聞いたことがありません。そもそもMSDNには『新しいモードのリフレッシュレート。リフレッシュレートを要求する。』と書かれているだけで、いったい何の数字を入れれば良いのかさっぱり分かりません。マイクロさんのいつもの癖で、実装予定な奴をさも出来るかのように書いてあるだけなんでしょう。かかわらず640x480x120Hz+Vsync待ちのプログラムの注文が来ちゃいました、なにやら昼飯一回分で請け負ってしまった様子。飯がかかっている以上ちょっとは真面目に取り組まなければなりません。しようかな〜、と思案することしばし『確かDirectX8ではリフレッシュレートの設定法が変わってたよな』ってなことに思い当たったんですよ。CreateDeviceするときの引数のD3DPRESENT_PARAMETERS構造体にFullScreen_RefreshRateInHzなるメンバーがいます。読んで字のごとくなんで120を入れて実行・・・、動きません。FALSE;と指定して、モニターを画面のプロパティーをいじってあらかじめ640x480x120Hzに合わせてから実行するとうまくいきました。DirectX側は自分でリフレッシュレートを切り替える気がなさそうな感じですね。Win32APIで解像度とリフレッシュレートを切り替えてからCreateDeviceすると良いような気がします。めんどくさいんで試してませんが・・・、飯一回でそこまでする義理も無いし(^^;一応サンプルソース。注意:このプログラムを使用する前に自分のモニタとグラフィックカードが指定するリフレッシュレートをサポートしているか確かめてください。不幸にもハードが煙を吐いても責任は負いかねます。UpDate:--/--/--つくづく私の脳ミソは抜けているのですが、先ほど就職活動中にその会社のトップとの面接を遅刻していくあたり、いい加減なにか重要なものが抜け落ちているとしか思えません。これ去年も同じことやったよなぁ・・・そんなわけで今回は抜けた脳ミソでDirectX8の抜きであるところのアルファブレンディングについてです。いっても面白いネタがあるわけでも無いんですけどね、まぁ忘れないうちに。DirectXも8になってD3DXライブラリが非常に重要な存在になってきました。事実上RMが無くなった今、穴埋めはこいつにしてもらわなけりゃならんわけです。ポリゴンオブジェクトの扱いがRM並に簡単になるわけも無かったんですが、それでも色々便利に拡張されています。初期化が異常に簡単になった今、D3DXは初期化ライブラリとしての役目を終えて、真に実用的な部分に注力されてる気がします。恩恵がでかいのがテクスチャの扱いじゃないかと思うわけです。BMP、TGA、PNG、JPG、DIB、PPM、DDSなど多種多様なファイルフォーマットに対応しつつ、D3DXCreateTextureFromFileExの引数にD3DPOOL_MANAGEDを指定しておけば、サーフェースロストの対応なんかや、VRAM管理なんかを勝手にやってくれる親切さっぷり。DirectX7以前でそのあたりに苦労した皆さんはご愁傷様って感じです。ついでに他の引数でカラーキーを指定できたりもするんで、抜き色合成もお手軽にできちゃったりします。こいつと合わせてD3DCreateSpriteでスプライトを作ってあげれば、2Dがらみの描画ルーチンは一瞬で完成ですよ、うひょー。おもってうはうはやっていたのですが、どうやらこのスプライト君α合成がうまく動かないっぽい。単純な半透明は問題ないんですが、レンダリングステートをいじって加算合成しようとしても普通の半透明にしかならない模様。爆発with加算合成で萌え萌えな人たちは悲しくなってしまうわけなんですね。そんなわけでD3DXスプライトがらみはポイして、2Dポリゴンで代用した方が良いようです。なにやらD3DXスプライトは遅いという噂もあるみたいですし。αブレンディングのやり方自体は以前のDirectXの方法と変わりありません。気をつけなければならないのが、不透明物体を先に描画しておいてあげること。これもいつもどおりです。Zバッファへの書き込みは不透明物体のときはON、透明物体のときはOFFにしときましょう。);透明物体->Render()透明物体の描画は奥にある奴から順にやってあげます。);抜き色を使うにはαテストもONにしておかないと駄目だったかも。)そもそもαテストってなんだったっけという気もしてきたので、こちら。こんなもんでしょうか、なんでこれしきのことに半日も悪戦苦闘していたのでしょうか(笑)半分はPhotoShopのPNGファイルの扱いが馬鹿だったことに起因するんですが・・・(日記参照)まぁ、うろ覚えな知識の再確認ができただけでも良かったということで。UpDate:--/--/--皆さんエロゲーやってますか〜、いえー。萌えてますか〜、いえー。ドットは正義ですか〜、いえー。Windowsなんてもんが無かった時代、グラフィッカーは己の魂の全てをかけてジャギー(斜めの直線のギザギザ)を手作業で消していました。要はギザギザ部分にそれと似た色を並べてぼかしただけなんですけどね。フルカラーなWindows時代になって、ここら辺の技術はかなり廃れてしまいました。例外的にエロゲーで背景とキャラクタを表示するときキャラクターの輪郭部分をきれいに表示する為に手作業でやっていた節がありますが(ToHeartとか)、最近ではアルファチャンネルを利用する方が主流です。表示されるフォントもいつのまにかジャギーの無いもの(アンチエイリアスフォント)になっています。考えてみるとノベルタイプのエロゲーでは自分の好きなフォントが選べたりするんで、あらかじめアンチエイリアスフォントを用意しているとは思えません。普通はそういった技法が使われるんでしょうが、Windowsにはアンチエイリアスフォントを作ってくれる親切なAPIがあります。その名もGetGlyphOutline、こいつを使うとフォントのアウトライン曲線またはビットマップを取得できます。曲線もらっても困るんで、今回はビットマップを頂くことにします。ここで取得できるフォントは引数の指定によって5〜65階調のアンチエイリアスがかかったものになります。ちなみにどの階調を選んでも、1ピクセルあたりの1バイトのデータになります。この際せこい事言わずにGGO_GRAY8_BITMAPを指定して最高階調の奴をゲットしましょう。このAPIでビットマップの配列を取得できるんですが、ドット単位でどうこうするのではなく、あらかじめ必要サイズのDIBを作っておいてそこに流し込むのがミソです。DIBは256色にしておくとぴったりのサイズになります。色もパレットをいじれば何色にでもできます。DIBに入れても上下が反転してしまう(DIBの癖)んで、このAPIがxy座標に2x2行列をかけることが出来るのを利用して、あらかじめフォント自体を上下逆にしておきます。DCにあらかじめフォントを設定しておかないとAPIがこけるので、CreateFont、SelectObjectを使って指定しておきましょう。デフォルト設定は無いんでしょうか...後はビットマップ配列がDWORDにアライメントされるのさえ気をつければOKです。それではDIBをBitBltしてあげましょう。文字の背景が塗りつぶされてしまってますが。)を指定しなかったのと同じ状態です。困りもんです、エロCGが文字でげしげし塗りつぶされたらユーザーに殺されてしまいます。DirextXを使っているときには抜き色指定が出来るので一発解決です。問題はノーマルなWindowsを使っている場合ですが、うってつけのAPIがあります。その名もTransparentBlt、なぜか全然知られていませんが抜き色指定付のBltです。いやぁ、便利、つーか便利すぎ。使うしかないっしょ、うはうは。...で、何で使われていないのかというと、このAPIが通常のBltと違うライブラリ(msimg32.lib)なのとメモリーリーク疑惑のせいでしょう。検索をかけるとわらわらヒットします。Win95系ではリソースを食いつぶすらしいです。なんで便利なものに限ってこうなんでしょうね。Win2000上でBoundcheckerをかけても漏れてなさそうだし、VAIO(Win98)でも大丈夫そうです。直したのかもしれないですね。気になる方はここでほぼ互換なTransparentBltが公開されているので、そちらを使うのが吉でしょう。これは昔ながらのマスクを作って抜き色を実現するタイプです。いうわけでサンプルソース。『スクリーンフォントの縁を滑らかにする』のチェックが入っているとありがたみが分かりません。UpDate:01/11/17プログラマーにとって命の次に大事なものは、過去のプログラムソースではないでしょうか。過去の自分が書いたソースなんてろくなもんが無くて、速攻新しく書き起こすことの方が多いんですが。そんなわけで今回はフルスクリーンの話です。ゲーマーは100人中120人くらいは『フルスクリーン萌え〜』であるという統計があります。Windowsプログラマーも『萌え萌え〜』といいながら対応せざるを得ません。DirectXの場合はSetCooperativeLevelでDDSCL_FULLSCREENを指定すれば簡単にフルスクリーンになります。通常のWinプログラムでもフルスクリーン化したいニーズが多々あります。今回の場合、VGAをヘッドマウントディスプレイに映して遊びたいとの欲望から発生しました。別ウインドウが眼前で巨大に見えては面白くないですからね。昔書いたフルスクリーン化のソースを掘り出してきました。わけでソースのクリーニングついでに紹介しましょう。やることは1:EnumDisplaySettingsでフルスクリーン時の画面モードを探す2:ChangeDisplaySettingsでフルスクリーンに3:満足したらもう一度ChangeDisplaySettingsして元の画面モードに戻す。この手のソースはネットでいくらでも公開されてます。鵜呑みにしてはいけません。グラフィックカードの挙動なんてモノは彼女の気持ちよりも怪しいのです。Win95では現在の色数から別の色数に変更できないという噂があります。OSR2以降ならOKとか2000なんかでは余裕とか色々ありますが、怖いんで私は色数変更はしないことにしています。色数を気にする時はDirectX使ってます。リフレッシュレートの問題があります。ChangeDisplaySettingsでリフレッシュレートを渡してもその値にならなかったり、いきなりハングったりすることがあります。ついでに、Win95系では現在のリフレッシュレートを取得できません(NT系はOK)。レジストリのHKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\Class\Display\000?\MODES\??\各解像度を参照すると書いてあることもありますが、少なくとも家のVAIOC1はなんも書いてありませんでした。他にも特定の機械では関数の戻り値が当てにならなかったり、リフレッシュレートの違いだけで再起動を要求したりします。そこらへんはここが詳しいのでそちらを参照してください。サーチで最初に引っかかったリフレッシュレート』にしています。480のモードを持っている可能性が非常に高い。リフレッシュレートについては神を信じる。以上で、フルスクリーン化の話は終わりなんですが、実はまだ解決しなければならないことが残っています。『フルスクリーン(低解像度)にすると元の画面モード(高解像度)に戻った時、ウインドウ位置がおかしくなる』ってことです。これはかなりうっとうしいです。開発中でもプログラムを実行するたびにVCのウインドウが画面の左隅に追いやられてしまうんですね。毎回ウインドウをずりずり引っ張ることになります。考えてみれば当たり前な話で、解像度が変わった影響で画面外にウインドウが存在する状況の方がOSにしてみれば何かと不自然なわけです。画面外に出たウインドウを無理に変更後の解像度に押し込もうとしてウインドウ位置が狂ってしまうわけです。逆に元の解像度に戻る時(低->高解像度)にはウインドウは画面内に収まっているので変更したウインドウ位置がそのまま残るって寸法です。そんな訳で解像度を切り替える前に元のウインドウ位置を覚えておいて、解像度を戻すときに元の位置に戻してあげなければなりません。これも実は簡単で1:フルスクリーン化する前にEnumWindowsで画面上のウインドウに対してコールバック関数を呼び出す2:コールバック関数内でGetWindowPlacementしてウインドウ位置を取得、保存する3:元の解像度に戻すときに再度EnumWindowsで画面上のウインドウに対してコールバック関数を呼び出す3:コールバック関数内でSetWindowPlacementしてウインドウ位置を元に戻すといった感じです。意外なことにこのソースはあまり見かけないんですよね。フルスクリーン化するときには必須だと思うんですが...。以上2つのフィーチャを突っ込んだサンプルソースです。古式ゆかしい640x480はもちろん320x240やら320x200なんかもプログラムからは指定可能なようです。UpDate:01/11/14実は私は大変時間に正確なのです。約束の時間を5分過ぎたころに到着します。いいかげん反省しなきゃならんような気もしますが、体の芯まで染み付いたこの習慣は死ぬまで抜けきらねーとか諦めてます。某社の面接も遅刻した挙句筆記用具も忘れたという素敵さっぷり。いやぁ、脳味噌が溶けてるとしか思えませんな。ゲーム作ってるときにどうしても必要になってくるのがこの時間管理のお話。昔から秒間60フレームがどうとかこうとか言って、VSYNC(垂直帰線)基準でカウントすることが多かったんですが、まぁテレビで動くゲームの場合VSYNCにあわせて描画するとちらつきのないきれいな描画が出来るという至極まっとうな理由があったわけです。そんなわけでコンシューマーやら昔のPCなんかはVSYNC待ちをかけてやるのが一番です。いえば気合の入った人々はHSYNC(水平帰線)使ってラスタースクロールかけたりしてましたね。ところが世の中例外があって、液晶ゲーム機とWindowsの世界ではVSYNCは使いません。液晶には端からVSYNCなんてないし(もちろん何らかの更新間隔はありますが)、Windowsの窓で動いてるプログラムにもVSYNCなんて関係ありません。Windowsなんてグラフィックカードのリフレッシュレートがまちまちなんで当てになりません。DirectXにもVSYNC絡みの命令がいくつかありますがそんなわけであまり使いたくないです。裏技的にWindowsのリフレッシュレートをプログラムから変更するって方法もありますがちょっとやりたくないですね。すんだよ〜ってな話がDirectXBBSやらBBXやらに定期的に登場し、すでに風物詩と化している訳です。結論から言うと仮想VRAMを用意してあらかじめそこに描画しておいて、タイマー割り込みを使って一気にVRAMに転送するってのが基本です。これで一定周期でそこそこ美しく描画が出来ます。そこで問題なのがタイマーの精度。私は『適当』をモットーに生きているので細かいことは気にしません。ポケステでえここを作ってたときは『めんどっちーからfor文まわしちゃえ〜』とかやっていました。下手すると最適化で削除されかねない適当さっぷりです。『人が見てなけりゃいいんじゃ』っつー了見なんですが、もちろんハードの種類が一つしかないから出来る技なわけです。将来高速なポケステ2とか出ると大変なことになるでしょう(出ないか(^^;)じゃあWindowsの場合はどーすんだよというわけでやっと本題です。真っ先に思いつくのがWM_TIMERメッセージですがこんなもんはポイです。精度がせいぜい50msなんで実現可能なフレームレートは20FPS程度になります。60フレーム萌え〜とか言ってる皆さんから刺し殺されかねない駄目さっぷりです。timeGetTimeなんかを使って自前で経過時間を見守ってあげることになります。WebでもGetTickCountを使ってる記事を良く見かけます。ところがこいつが曲者で妙に精度が低いんですよ。大体15ms程度の誤差ってところでしょうか。60FPSってのが16.6ms間隔なんでさすがに誤差が大きすぎます。そこでtimeGetTimeを使うのが日曜ゲームプログラマーのお約束になっています。MSDNには1msの精度があるんだぜ〜などと書いてありますが実際には誤差は5ms程度、まぁ必要十分なところでしょうか。今まで私もこいつを使っていました。Windows2000にしてから調子が悪いんですよ。machine.いやぁNTなんて使わないんですっかり見落としてました。MSDN様のおっしゃることには95系で1msの精度、NT系では5msより低い精度だとのこと。サバ読まれてることを考えるとNTではかなり素敵な誤差が出てそうです。調べてみるとWindows2000では約10msの誤差が出ています。timeGetTime.という言葉を信じて精度を1msに設定してみてもどうも改善されない気が...。そこで登場するのがQueryPerformanceCounterであります。このあまり知られていないタイマーは高分解能パフォーマンスタイマーと呼ばれるだけあってかなりいい感じです。BH6)なる値が出ています。ことは1msの間に1000回位はカウンターが増えるはず、十分過ぎです。皆さんもQueryPerformanceCounter使いましょーね。こいつを使ってもいまいち精度が良くなった気がしません。なんでじゃ〜っと暴れることしばし、改めてソースを見るとsleep君がいるじゃないですか。;こんな感じですが、これは『めんどっちーからfor文まわしちゃえ〜』精神にのっとっていると言えます。私的には全然OKなのですが、世間では大変嫌われます。まがりなりにもマルチタスクOSであるWindowsで空ループを回すとそこで全CPUパワーを食ってしまって同時に起動している別プログラムが死ぬほど遅くなっていしまいます。何より芹香(我が家の旧メインマシン)が許してくれません。)させてからループに突入させていました。寝て別タスクに実行権を進呈するわけですね。ちなみにこの-3というのはsleepの精度がそれくらいだと、どこかで伝え聞いて信じているという大変アバウトな理由。今回改めて計測してみるとWindows2000上でのsleepの精度は10ms程度・・・、駄目じゃん。16.6ms間隔でループまわすのに10msの誤差があるもんは使えません。(もちろん30FPS以下でループを実行するときは十分使えますが)妥協案としてsleep(0)してからループに入ることにします。面白いことに『0ms寝ててね』という命令でそれなりにCPUに空き時間を作れます。それでも無駄にCPUパワーを食っていますが熱暴走しないくらいにはなるかと。以上を踏まえたフレームレート管理のサンプルソース(まだ作ってない)。適当にいじってお使い下さい。真面目に知りたい方は、こことかここ辺りを参照してください。こちらの日記によると『fpsの調節をするためにビジーループでQueryPerformanceCounterを呼び出していると、環境によっては数秒に一度関数からしばらく戻って来ないでフリーズする現象が発生します。どのくらいフリーズするのか実際に計ったわけではありませんが、全体の速度から計算すると300ms以上戻らないようです。これはアセンブリ言語のRDTSC命令でも同じ症状が発生しました。』(引用)とのことです。いやいや、ためになります。