ゲームプログラマ志望が福岡で叫ぶ 『絶望』 -10ページ目

ゲームプログラマ志望が福岡で叫ぶ 『絶望』

プログラマーになりたい!!!!! あ、風のうわさで聞いた最近若者で流行っているトゥイッターなるものを始めてみました (・ト・) @toshi_desu_yo

先週悩んでた事の1つ、
DirectShowでテクスチャ
にムービーを貼れた!
(`・ω・´)



音も再生できている。



テクスチャに貼れたので、ポリゴンとの色の合成と回転を行っている。






正直、
DirectShowのリファレンスを読む時間のほうが長かった・・・
3日ぐらい読んでいた気がする。。。(ヽ´ω`)



僕の頭はお豆腐なので
リファレンスに言い回しが難しい日本語がいっぱい出てくると



理解に時間がかかるのですわ(´・ω・`)

正直専門的な用語を調べ、理解するのにくっっっっそ時間かかりました。




プログラム自体はすぐ出来ました。



マイクロソフトのサンプルに対して言い表せない程の恐怖を持っているので最初は戦慄していましたが、

これはものすっっっっご簡単でした( ゚д゚ )
作成した方凄い。


最初からこっちみて、
わからないところをリファレンス見ればよかったかも。。。



あと、
ほぼサンプルのパクリです(ドヤ顔)



*********************************

前、記述したとおり、

DirectShowは "フィルタ" と呼ばれる部品をつなぎ合わせることで、
最終的に動画の再生やファイルコンバーター等のアプリケーションを作成できる。



===========================

(例)
ファイルの読み込みフィルタ ↓ ①
   ビデオファイルの解凍をするフィルタ ↓ ②
            動画、音の再生をするフィルタ ↓ ③

      ”ウィンドウに出る!”

===========================






DirectShowが最高な点は、 ① と ③ をドッキングさせると、


自動で 途中の ② を作成して繋げてくれる。



つまり、
『どの形式を読み込んだら、このフィルタを繋げて更にこのフィルタを~・・・』


 という作業が要らない。
基本的には読み込みフィルタと最後のフィルタを繋げるだけでおk




 最高。 (*´д`*)エクスタスィー




Windows Media Player もこのフィルタを繋ぎあわせて再生しているらしく、

再生に関して、
Windows Media Player で再生できる形式DirectShowで再生可能。




今回お仕事で依頼されたビデオ形式は mp4。

通常の Windows Media Playermp4の再生に対応してないみたい。




なので、最初は再生できなかった。
      → ぬるりとハマった。





avi を再生するときは
aviを読み込んで → ”解凍”して解析 → 動画と音のファイルに分ける。

そこから描画処理に回すのですが、
その解凍する部分がWindowsに無かったので再生も出来なかった。


つまり、
上の例で行くと ② が出来ないので ③ まで繋げることが出来ない。



解決策として、

新しく外部からWindowsのレジスタに
 "mp4を解凍するフィルタを追加" することで 再生可能になった!


うれしかったわぁ

参考サイト様↓
【 (URL) mp4をWIndowsで再生 】 ありがとうございますm(_ _)m





最初は


 ど・ど・どうするよ((((;゚Д゚))))ガクガクブルブル
mp4再生できない!? (((( ;゚д゚))))アワワワワ 


これは別の形式に変換しないといけない!?

今更仕様を変えるよう連絡するとかぅtふぁfsf! アタヽ(д`ヽ彡ノ´д)ノフタ


・・・・


と、
尋常じゃなくテンパッた。
仕様変えるとか怖くて言えない。 (ボソッ




よくよく考えると再生できないはずがない。

できなかったら
 Windowsではmp4を再生できないということになる。

インターネットって便利だわぁ・・
分からないことは大抵解決できるもん。。。(言語の壁は高い)




ただプレーヤーを作るだけではなく、他にも


③の部分を
---
---------------------------------------------------------------------------
  ”解析したmp4ファイルをAVIに変換するフィルタ”
------------------------------------------------------------------------------
に変更し、

その後に新しく
------------------------------------------------------------------------------
 "デスクトップに、変換したビデオを書き出すフィルタ"
 
------------------------------------------------------------------------------
を繋げると


mp4形式を読み込ますと、
 デスクトップにAVI形式のビデオが吐き出されます。 
おえええええ!



めっちゃ便利 DirectShow ( ゚д゚ )

正直なんでこれがDirectXでサポートされなくなったのかが不明。
もう完成されたから? よくよく考えたらカテゴリがDirectXではなかった?




今回は、
 ③ の後に ”テクスチャに描画するフィルタ” をつなげたので

 テクスチャに描画できました!

理屈は超簡単



このフィルタはDirectShowの中にはありません。


そう、
フィルタは自分でも開発できるのです!
(  ゚д゚ )!




これがさらに DirectShow が面白いと思える所。


新しい形式をWindowsが対応してなかったら自分で作ればいい。 


そういうことです。




テクスチャに描画できたので、


変形や色の変化、

モデルに貼り付ることも出来そうです。
これは後日やってみたい。




今後は、

******************************************************

① 2D、3Dでまともに使えるようにプロジェクトを書き換える。
② フルスクリーン対応
③ 再生、停止、早送り、巻き戻し、コマ送り 等の操作を簡単にできるようにクラス作成。

******************************************************
が課題。

テクスチャに描画したので ①、② は簡単にできそう。


③はめんどくさそうなので後回しさ!
(b´∀`)ネッ!




あ、それと
 COM関連で最後にReleaseをしたらエラー吐き出すものがあった。。。(´・ω・`)

ちょっと調査しなきゃ。。。

最初からスマートポインタにしろということか・・・




*******************************


一応プロジェクト ↓

【 (URL) DirectShow_Texture_1

● VisualStdio2012 Desktop版
● Windows SDK 6.1  
● DirectX 2010 June
 → これはすいませんm(_ _)m  64bit対応しようと試行錯誤して面倒くさくなって手打ちしてもうた。 直すの今めんどい



が入れてあると多分再生できるんじゃないかと。。

後、64Bitにしてるので 32Bitの方はVisualStdio の
「ローカル Windows デバッガー」 の右の x64 を Win32 にしてもらえると・・いけるはず。


出来なかったら言ってください。。。
まだ修正中なので絶対できなさそうです (;´Д`)

(追記)
すいません、コレだと32bit版のかたは出来なさそう・・・
VC++ ディレクトリの設定をうまく直していただくと。。。 多分。。。
修正します!!



異常!



熊本に旅行に行ったその弐。


二日目は
鹿児島に言ったでごわす(西郷)




鹿児島! そう鹿児島ですよ!!

高速で約1時間30分・・・


どしゃ降りの雨の中死線を掻い潜ってようやく到着!!!



 鹿児島っぽい写真とってなかった






無計画を信条とする漢の中の漢の僕ら。
ただ僕がお金がないからとかそんなんじゃない!



取り敢えず鹿児島駅に行ってみました。 ↓





観覧者やぁ嗚呼ああ嗚呼!!!!!!FOOOOOOOOOOOO!!!


だからなんだって話ですね。




この日は鹿児島の成人式とかぶったのか、
着物姿の女性がちらほら。。。     (*´Д`)ハァハァ



着物レンタル屋さんで |д゚)チラッ と見たんですが、

あれレンタルして着るだけで 6万 ですってよ(焦慮)( ゚д゚ )






6万て・・・・ 
 
 綾鷹何本買えんねん・・・(´;ω;`)




成人式の思い出のために一日で6万使うよりかは、

僕に
6万渡して僕を風俗お水商売やさんに連れて行くほうが有意義じゃないっすかねえ!?





普通に、取り敢えず6万あったら携帯買い換えます
未だにガラケー。
ガラケーでいいんだけど、電池が何も触らなくても一日持たないので他の携帯に買い換えたい。



成人式?行かなかったよ。福岡に知り合いほぼゼロだし。。。。。。。。。。(慟哭)
ちゃ・ちゃんと友達いるわ!!!。。。。。。。(慟哭)




------


鹿児島で行く宛もなく、外は土砂降りなので、取り敢えず市内を歩いて探索することに。



鹿児島しろくまアイスの生まれた地!


関東圏の人は知っているのでしょうか?
しろくまアイス(´(ェ)`)



果物が入ったシャーベット状のアイスで
 甘さ控えめのシャーベットと沢山の果物の甘さが
  口の中でスプラッシュする




とても高くて美味しいアイスです。



歩いてたら市内で元祖シロクマ屋を見つけましたよ!!




し ろ く ま (リアル)

どれだけ儲かってるのかが一目でわかるいい置物ですね^^


値段は 400 ~ 700円でした! 
食べたかって? 残金1000円程度しか無かったから食ってねーよ( ゚Д゚)オラ



でKEEEEEEEEEEE!!!!




その ウサギさん林檎僕の手のひら以上ありますよ!

鹿児島恐ろしい・・・((((;゚Д゚))))ガクガクブルブル



遺伝子組み換えた果物を使ってシロクマのような重圧感と殺意を与えるとは・・・




しろくまアイス(威圧)



しろくまアイスの由来や鹿児島の街を知り得たので無計画でも中々楽しいものとなりました。



しっかし・・・ 鹿児島・・・

市内になんにもないぞ(絶望)




大きいアーケード街があったのでそこを歩いてたのですが、
お店が微妙なものしかなく、若者にはきっっっついですね。




その後に
暇だったのでイオンモール行ったのですが、


その途中の道のほうがチェーン店がたくさんあってすごかった。
少し離れるとお店いっぱいあって買い物には絶対困らないような感じになってた。



そして、 イオンモールでかかった!!!


取り敢えずお買い物、困ったらイオンモール。 これ安定。
ご飯困ったらイオンモール。 これ安定。



イオンモールはゲームセンターや幼児たちの遊び場はちゃんと確保されているので、

後は大人の遊び場(18禁)を取り入れてもらうとイオンモールは完全無欠の無敵要塞になりますね( ´∀`)


本当に住めるぞイオンモール・・・




その後、なんと・・・


夜景を見に行きました!!
   キャ━━━━(゚∀゚)━━━━!!





 男 二 人 で。
    Oh... Year!!!




iPod 画質わりぃいいいいい!!!

デジカメ もしくはカメラ性能がいい携帯に買い換えたい。。。





ねこがおったで!



めちゃくちゃ懐っこいですね。
 すりすりのもふもふで、くんかくんかしながらはぁはぁ・・・(*´Д`)






 うわああああああああああああああああああああ!!! ねこねこねこねこ!! かわいいよかわいいよおおおおおおおお!! うわあああああああああ!!
んぎいいいいいいいいいいいいいいい!! ねこおおおおおおおおおお
CATTTTTTTTTTTTTTTTT!! 
ごくっ・・・  ぷはぁ・・・





恐らくここに来た旅行者(大抵カップルでしょうね^^ )から大層食料をぶんどっているのだろう。

あざとい。 あざといぜ猫。。。


とても毛並みも綺麗で美人さんでした(´∀`*)






と、そろそろ終わりです。
特に面白いところにも行かず、夜は借りてきたDVDを鍋とピザを食いながら見たりしただけです。


あ、鹿児島に屋台村ってのが有りました。 ↓



特に行きたいとはおもわな・・・ ぐわああああああああ!!!

ちんちん電車あった。







【結論】

旅行に行くときはちゃんとした計画を持ってしっかりとした人を連れて行きましょう。



帰り際

「あれ?俺らなにしたっけ?」
という虚無感に苛まれます(マジ)



そう言えばクリームシチューの上田のお兄さんがやっている中華屋にも行った。
なんか、一回でいいかな- となった。




【DirectShowについて】



【 DirectShowシステムの概要】

マルチメディア を扱う場合以下の課題がある。
※マルチメディア : 画像や動画や音声など様々な形態の情報を統合して扱うこと。
             単に統合するだけではなく、インタラクティブ性を持たないと呼ばない場合もある

● 非常に高速に処理しなければならない大量のデータ。
● オーディオとビデオを同期し、同時に再生および終了、同じレート(割合)で再生されるようにする必要がある。
● データは ローカルファイル、ネット、TVチューナー、ビデオカメラなど様々なところに存在する。
●データのフォーマットは AVI, MPEG, 等様々である。
●プログラマは使用者がどのシステムでどのハードウェアで使用しているか知らない。



【DirectShowソリューション(商品)】

DirectShowは
データの流れやハードウェアの違い、同期の問題を開発者が気にならない設計になっています。

DirectShowではビデオ、およびオーディオの再生を高速化するため、DirectDraw, DirectSoundを使用しています。
これらはデバイスのサウンドカードやグラフィックカードを効率的にするので高速です。

様々なフォーマットや環境に対応するために DirectShow は "フィルタ" と呼ばれる複数の部品を組み合わせて使用します。

↓の図はアプリケーション、DirectShowコンポーネント、DirectShowがサポートするハードウェアコンポーネントおよびソフトウェアコンポーネントの関係を示している。


再生するファイルがデスクトップにあったり、TVチューナーだったりインターネット上にある可能性がある。
そしてフォーマットも AVI , MPEG 等様々。圧縮ファイルもある。
PCにサウンドカードがあったりなかったり。

この複雑な関係から開発者を隔離して、気にせずに使用できるようにするものが DirectShow。


=======【 フィルタグラフとそのコンポーネント 】======

DirectShowでフィルタを独自開発するための概要を示す。


【 DirectShowフィルタについて 】

第一回目で書いた通り、DirectShowはフィルタと呼ばれる各機能を持ったコンポーネントをつなぎ合わせることによって実現している。
フィルタはCOMオブジェクト。

DirectShowはあらかじめ標準のフィルタを幾つも提供しています。
拡張したい場合は、独自のカスタムフィルタを作成することもできます。

例で AVI ビデオファイルを再生するために必要な手順、それを実行するフィルタを示してみます。

①ファイルからデータを読み込む ( ソースフィルタ )
②解析してビデオとオーディオデータに分ける ( AVIスプリッタフィルタ )
③AVIは圧縮形式なのでビデオをデコード ( 解凍 )する。( 圧縮フォーマットに応じた様々なデコードフィルタ )
④ビデオを描画する ( ビデオレンダラフィルタ )
⑤オーディオをサウンドカードに送る ( デフォルト DirectSound デバイスフィルタ )


↓ 図で示すとこうなる。



図で分かる通り、フィルタ同士は接続されています。
この接続も”ピン”と呼ばれるCOMオブジェクトで行っています。
フィルタは、自分の機能でデータをごにょごにょした後、ピンで接続されている相手にデータを投げます。

上記のようにフィルタがつながっている全体を”フィルタグラフ”を言います。


ビデオファイルの再生において、再生、停止、ポーズという基本的な3つの状態があります。
実行中の場合はフィルタが処理をして、停止の時はフィルタは何もしません。
この流れを管理しているのもフィルタグラフです。


○フィルタのカテゴリ○

・”ソースフィルタ”
 データを読み込んでフィルタに導入する。
 ファイルやネットワークやカメラなどから。
・”変換フィルタ”
 受け取ったデータを一定の処理をして出力する。
 エンコーダやデコーダなど。
・”レンダリングフィルタ”
 一番最後のフィルタ。
 途中で変換したデータを受け取り、それをディスプレイに描画したりサウンドカードに送信して音声の再生をさせる。
 変換したファイルをデスクトップに書き込んだりできる。
・”スプリッタフィルタ”
 受け取ったデータを解析して ビデオ、オーディオ・・・ などに分けてそれぞれを別々のフィルタに出力する。
・”Muxフィルタ”
 複数の入力を受取り一つのデータにする。スプリッタの逆の処理をする。
 たとえば、ビデオとオーディオを受け取って AVIフォーマットで組みなおしたり。


すべてのフィルタは IBaseFilter インターフェイスを継承し
すべてのピンは IPin インターフェイスを継承しています。



【フィルタグラフマネージャについて】

フィルタグラフマネージャはフィルタグラフ内のフィルタを制御する COMオブジェクトです。

他にも「~を再生しました」や「停止しました」等のイベントをアプリケーションに通知したり、
フィルタグラフの作成、接続、接続解除するための関数が複数あります。


フィルタからフィルタへのデータの移動はフィルタ自体がピンを見て行う。
処理は独立したスレッドで行われます。

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




飽きた
COM オブジェクトとは

コンポーネント オブジェクト モデル ( COM ) オブジェクト。
ダイナミックリンクライブラリ ( DLL ) として実装するのが最も一般出来。

COMオブジェクトの処理は、公開している関数の呼び出しで実行される。

COMオブジェクトと C++ のオブジェクトのやり取りは似ている。


しかし、顕著な相違点がある。

・COMオブジェクトはC++オブジェクトよりもカプセル化の方法が厳密。
 特定の公開館数を利用したい場合、オブジェクトを生成して、その関数を保持しているインターフェイスを取得しなければならない。
・COMオブジェクトとC++オブジェクトの作成方法。
 COM固有の方法でCOMオブジェクトは作成される。
 DirectXAPIには様々な生成を容易にする関数が用意されている。
・オブジェクトの寿命はCOM特有の技法を用いる
・COMオブジェクトはDLLに含まれているが、COMを生成するに当たって自動的にDLLがロードされるので静的ライブラリのリンクをする必要がない。
・COMはバイナリなので様々な言語で記述でき、様々な言語でアクセスできる。


【オブジェクトとインターフェイス】

オブジェクトとインターフェイスの違い。

・オブジェクトはインターフェイスを1つ以上継承していることがある。
 例えば全てのオブジェクトは IUnknown インターフェイスを継承している。
 そのインターフェイスの関数を使いたい場合、オブジェクトの作成だけではなく
 正しいインターフェイスを取得する必要がある。



※注意
 インターフェイスを継承したオブジェクトはそのインターフェイス定義に含まれる全ての関数をサポートしなければならない。
 言い換えれば関数を呼び出すとき、それが存在するかを心配する必要はない。
 ただし、各メソッドの実装方法はオブジェクトによって異なる場合がある。
 結果は同じでもオブジェクトにより使用するアルゴリズムが異なるということ。
 そして、メソッドすべての機能がサポートされているというわけでもない。
 サポートされていないメソッドも正常に呼び出すことはできるが、 E_NOTIMAPL が返される。
 書くオブジェクトのドキュメントを参照して書くオブジェクトでのインターフェイスの実装状況を確認することを勧める。
 COM標準では、既存のインターフェイスに新しいメソッドを追加できない。
 変更するのではなく、新しいインターフェイスを作成する必要がある。
 
 1つのインターフェイスに複数の世代が存在する。
 多くの場合、オブジェクトはすべての世代のインターフェイスを公開する。
 古いアプリケーションでは古いインターフェイスを使い、新しいアプリケーションでは新しいインターフェイスの機能を利用することができる。

 初代が IMyInterface ならば 第二世代は IMyInterface2, 第三世代は IMyInterface3
 と、世代を示す整数が付加される。
 
 DirectXでは頭にDirectXのバージョン番号が振られている。


【GUID】
 グローバル一意識別子。
 128bitの構造体で同じGUIDは存在しない。
 
 COMでは次の2つの目的のため、GUIDを広く使う。
 
 ・特定のCOMオブジェクトを識別するため。
  COMオブジェクトに割り当てられたGUIDをクラス識別子 CLSID と呼ぶ。
  CLSIDは、そのCOMオブジェクトのインスタンスを作成する時に使われる。
 ・特定のCOMインターフェイスを識別するため。
  特定のCOMインターフェイスに割り当てられたGUIDをインターフェイス識別子 IID と呼ぶ。
  IIDはオブジェクトから特定のインターフェイスを要求する時に使用される。
  公開しているオブジェクトが異なっても、同じおんた~フェイスであれば IIDも同じ。

※オブジェクトやインターフェイスを呼び出すときは わかりやすいように IDirect3D9 などの説明的な名前が使用される。
 このため、混同される心配はないが、厳密には同じ説明的名前を持つオブジェクト、インターフェイスが存在しないという保証はない。
 特定のオブジェクトまたはインターフェイスを間違いなく表すことの出来る唯一の方法は GUID によるものである。
 GUID は typedef で等価な文字列で表現されていることもある。
 GUIDは 32進数の整数による 8-4-4-4-12 形式 { xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxx } となる。( xは16進数の整数に対応する )
 例えばIDirect3D9 インターフェイスのIIDを文字列形式で表すと次のようになる。
  { 1DD9E8DA-1C77-4D40-B0CF-98FEFDFF9512 }

実際のGUITは使いづらく誤入力しやすいので通常は GUID と共に等価な名前も用意される。
CoCreateInstanceなどの関数を呼び出すときに 上記の形式ではなく、等価な文字列が使用出来る。
インターフェイス( IID ) → IID_xxxx
オブジェクト( CLSID ) → XLSID_xxxx

が付けられる。
例えば IDirect3D9 インターフェイスの IID の名前は IID_IDirect3D8 となる。


【 HRESULT値 】

全ての COMメソッドは HRESULTと呼ばれる 32ビットの整数を返す。

・メソッドの成否
・メソッドの結果に関する詳細情報

この2つの情報が含まれている。

HRESULTは様々な成否を表すコードをいくつでも含めることが出来る。
慣習により、成功コードには S_xxx というプレフィックス、失敗には E_xxx というプレフィックスが付けられる。
単に成功、失敗を表すには S_OK, E_FAIL を使用する。


COMメソッドが成否を示す様々なコードを返すということは、 HERSULT値のテスト方法に注意が必要であることを意味する。
成功の場合は S_OK, 失敗の場合は E_FAIL が返されるという仮説に基づいてテストを行ったとする。 しかし、メソッドからはこれ以外の失敗または成功を表すコードが返される可能性もある。
次のコードは単純なテストを用いた場合の危険性を表す

HRESULT hr;

if ( hr == E_FAIL )
{
    // 失敗
}
else 
{
    // 成功
}

メソッドが失敗を示すために返す値が E_FAIL だけである限り、このテストは正しく機能する。
しかし E_NOTIMPL や E_INVALIDARG などのエラー値が返される可能性もある。
その場合、これらは成功として解釈され、アプリケーションでエラーが発生する原因となる。

関係のある各 HRESULT 値をテストする必要がある。
しかし、成功か失敗かだけを知りたい場合は

・ SUCCEEDED → 成功を示すコードに対しては TRUE, 失敗は FALSE
・ FAILED → 失敗を示すコードに対しては TRUE, 失敗は FALSE

マクロを使用すると良い。


FAIELD マクロを使用すると上記のコードは

if ( FAILED( hr ) )
{
    // 失敗
}
else
{
    // 成功
}

となる。
これだと E_NOTIMPLや R_INBALIDARG が返されてきても正しく処理する。



【ポインタのアドレス】

COMメソッドでは

HRESULT CreateDevice( ..., IDirect3DDevice9 **ppReturnedDeviceInterfade );

のようなダブルポインタの表記がある。
C, C++ で使われる通常のポインタとは違う。

この間接的呼び出しは ** で示され、変数名に pp というプレフィックスが付く。

前述の ppRTeturnedDeviceInterface パラメータは 一般的に IDirect3DDevice9 インターフェイスのポインタのアドレスと呼ばれる。


インターフェイスのアドレスのアドレスをメソッドに渡す。
メソッド内でアドレスに対してオブジェクトのメモリをとるとメソッドが終わったとき要求されたインターフェイスを指した変数が完成する。




【 COMオブジェクトの作成 】

COMオブジェクトを作成する方法はいくつか存在する。
DirectXプログラミングでは次の2つの方法が最も一般的である。

・直接的な方法。
 オブジェクトのクラス識別子( CLSID ) を CoCreateInstance 関数に渡す。
 この関数は、識別子からオブジェクトのインスタンスを作成し、指定されたインターフェイスへのポインタを返す。
・間接的な方法。
 そのオブジェクトを作成する DirectXメソッドまたは関数を呼び出す。
 呼び出したメソッドがオブジェクトを作成し、そのオブジェクトのインターフェイスを返す。
 この方法でオブジェクトを作成する場合、通常は、返るインターフェイスを指定できないない。


オブジェクトを作成する際は CoInitialize 関数を呼び出してあらかじめ COM を初期化しておく必要がある。
間接的にオブジェクトを作成する場合は、オブジェクト作成メソッド内部でこの処理が行われている。
CoCrateInstance を使ってオブジェクトを作成する場合は、明示的に CoInitialize を呼び出さなければならない。
操作を完了したら CoUninitialize を呼び出して終了しなければならない。 
COMを明示的に初期化する必要があるアプリケーションでは、初期化の最初で COMを初期化し、終了時に状態を戻すのが一般的である。

CoCreateInstance でCOMオブジェクトの新しいインスタンスを作成するには、オブジェクトのCLSIDが必要。
公開されている CLSID はリファレンスドキュメントまたは該当するヘッダーファイルに記述されている。
CLSIDが公開されていない場合は、そのオブジェクトを直接作成することはできない。

CoCreateInstance 関数には5つのパラメータがある。
DirectXで使うCOMオブジェクトを作成する場合は、通常、次のパラメータを設定する。


・rclsid → 作成するオブジェクトの CLSID 
・pUnkOuter → NULL設定。 このパラメータはオブジェクトを結合するときに使用する
・dwClsContext → CLSCTX_INPROV_SERVER に設定する。
 この設定はオブジェクトがダイナミックリンクライブラリ( DLL )として実行され、アプリケーションの一部として実行されることを示す。
・riid → 要求するインターフェイスのインターフェイス識別子( IID ) に設定する。
 この関数はオブジェクトを作成して、要求されたインターフェイスへのポインタを ppv パラメータに返す。
・ppv → 関数が戻った時に riid が指定するインターフェイスに設定されるポインタのアドレス。
 



通常はオブジェクトを間接的に作成するほうが格段に簡単。
オブジェクト作成関数にインターフェイスポインタのアドレスを渡すだけだから。
後は関数内部がすべて行ってくれる。
通常、間接的な方法でオブジェクトを作成する場合は、メソッドが返すインターフェイスを選択できない。
ただし、オブジェクトの作成補法について様々な事項を指定できる。

たとえば、次のコードは CreateDevice メソッドを呼び出して、ディスプレイアダプタを表すデバイスオブジェクトを作成している。
このメソッドはオブジェクトの IDirect3DDevice9 インターフェイスへのポインタを返す。
最初の4つのパラメータはオブジェクトを作成するために必要な各種の情報を指定し、5番目のパラ目^他はインターフェイスポインタを受け取る。

IDirect3DDevice9* g_pd3dDevice = NULL;
...
if ( FAILED( g_pD3D->CreateDevice( D3DADAPTER_DEFAULT,
                             3DDEVTYPE_HAL,
                             hWnd,
                             D3DCREATE_SOFTWARE_VERTEXPROCESSING,
                             &d3dpp,
                             &g_pd3dDevice ) ) )
    return E_FAIL;




【COMインターフェイスの使い方】
COMオブジェクトを作成すると、作成メソッドがインターフェイスポインタを返す。
このポインタを使って、そのインターフェイスの任意のメソッドにアクセスできる。


●追加のインターフェイスの要求

通常は、作成メソッドから受け取るインターフェイスポインタだけで用は足りる。
実際、オブジェクトが継承するインターフェイスが IUnknown 以外には1つだけであることも多い。
しかし、多くのオブジェクトは複数のインターフェイスを継承する。
このため、複数のインターフェイスへのポインタが必要になる場合もある。
その場合、新しいオブジェクトを作成する必要はない。
オブジェクトの IUnknown::QueryInterface メソッドを使って別のインターフェイスポインタを要求できる。

CoCreateInstance でオブジェクトを作成した場合は、IUnknown インターフェイスポインタを要求した後、 QueryInterface を呼び脱ことによって、必要なすべてのインターフェイスを要求できる。
ただし、この方法は、必要なインターフェイスが1つだけの場合は不便である。
また返すインターフェイスポインタを指定できないオブジェクト作成メソッドを使う場合は、全く役に立たない。
すべての COM インターフェイスは IUnknown インターフェイスを拡張するので、通常は明示的な IUnknown ポインタを取得する必要はない。

インターフェイスの拡張は、C++ のクラスの継承に似ている。
子インターフェイスは親インターフェイスのすべてのメソッドと子独自のメソッドを1つ以上公開する。
実際「拡張」の代わりに「継承」という表現を使うことも多い。
ただし、継承はオブジェクト内部で行われるものである。
アプリケーションがオブジェクトのインターフェイスを継承または拡張することはできない。
しかし、子インターフェイスを使うと、子または親インターフェイスの任意のメソッドを呼び出せる。

すべてのインターフェイスは IUnknown の子である。
このため、オブジェクトに対して取得したどのインターフェイスポインタを使っても QueryInterface を呼び出せる。
その場合は、要求するインターフェイスのインターフェイス識別子( IID )とインターフェイスポインタのアドレスを指定する必要がある。


【 COMオブジェクトの有効期限の管理 】
オブジェクトが作成されると、システムは必要なメモリリソースを割り当てる。
必要がなくなったオブジェクトは破棄する必要がある。
C++ では new, delete の各演算子でオブジェクトの有効期限を直接制御できる。
COMでは、オブジェクトを直接作成、または破棄できない。
これは同じオブジェクトを複数のアプリケーションが使っている可能性があるからである。
このようなオブジェクトを1つのアプリケーションが破棄すると、ほかのアプリケーションでは高い確率でエラーが発生する。
COMでは 参照カウント と呼ばれるシステムを使ってオブジェクトの有効期限を制御している。

オブジェクトの参照カウントは、そのオブジェクトのいずれかのインターフェイスが要求された回数を示す。
インターフェイスが要求されるたびに、参照カウントはインクリメントされる。
必要のなくなったインターフェイスをアプリケーションが解放すると、参照カウントはデクリメントされる。
オブジェクトは参照カウントが0にならない限り、メモリ上に保持される。
0になるとオブジェクトは破棄される。
オブジェクトの参照カウントについて知る必要はない。
オブジェクトのインターフェイスを正しく取得して解放する限り、オブジェクトの有効期限は適切に制御される。


”重要”
COMプログラミングでは、参照カウントを正しく処理することが非常に重要である。
この処理を正しく行わないと、メモリリークが生じやすくなる。
COMプログラマが最も犯しやすいミスの一つがインターフェイスの解放し忘れである。
インターフェイスが解放されないと、参照家運がいつまでも0にならず、オブジェクトはメモリに残り続ける。


●参照カウントのインクリメントとデクリメント

新しいインターフェイスポインタを取得するたびに、 IUnknown::AddRef を呼び出して参照カウントをインクリメントする必要がある。
ただし、通常アプリケーションでこのメソッドを呼び出す必要はない。
オブジェクト作成メソッドまたは IUnknown::QueryInterface を呼び出してインターフェイスポインタを取得した場合、オブジェクトは自動的に参照カウントをインクリメントする。
しかし、既存のポインタをコピーするなど、その他の方法でインターフェイスポインタを作成した場合は、 IUnknown::AddRef を明示的に呼び出す必要がある。
これを行わないと、元のインターフェイスポインt名が解放した時に、そのポインタのコピーを継続して使う必要があっても、オブジェクトが破棄されることがある。

インターフェイスポインタが不要になった場合は IUnknown::Release を呼び出して参照カウントをデクリメントする。
インターフェイスポインタはNULLで初期化し、解放時に再び NULL に設定するので、 NULLかどうかでインターフェイスのアクティブを検証することができる。
アプリケーションを終了する前に解放する必要がある。



【 CによるCOMオブジェクトへのアクセス 】

COMプログラミングで最も一般的に使われている言語は C++ であるが、 C を使っても COMオブジェクトにアクセスできる。
COMを使う方法は、操作は比較的わかりやすいが、複雑な構文が必要になる。

・すべてのメソッドでパラメータリストの最初にパラメータが1つ追加される。
 このパラメータはインターフェイスポインタに設定する必要がある。
・オブジェクトの vtable を明示的に参照する必要がある。

すべてのCOMオブジェクトにはそれぞれ vtable があり、この中には、そのオブジェクトが公開するメソッドへのポインタのリストが含まれている。
インターフェイスポインタは vtable 内の場所を示している。
そこには、呼び出されたメソッドへのポインタが格納されている。
C++ では、 vtable は基本的に不可視である。
ただし、CでCOMメソッドを呼び出す場合は、間接呼び出しのレベルを1つ追加して、vtable を明示的に参照する必要がある。

次のコードは C++ での呼び出し規則を使って IDirectplay8Peer::Initialize メソッドを呼び出す方法を示している。


g_pDP->Initialize( NULL, DirectPlayMessageHandler, 0 );


C で同じメソッドを呼び出すには、次の構文を使用する。
vtable ポインタの名前は lpVtbl と表記される。


g_pDP->lpVtbl->Initialize( g_pDP, NULLm DirectPlayMessageHandler, 0 );



【 マクロによるDirectX COMメソッドの呼び出し 】

COMインターフェイスの多くには、アプリケーションでより簡単にメソッドを使えるように、
各メソッド用に定義されたマクロが用意されている。
これらのマクロは、インターフェイスの宣言と同じヘッダーファイルで定義されている。
マクロは、 C, C++ の両方のアプリケーションで使えるように設計されている。
C++ マクロを使うには _cplusplus を定義する必要がある。
これを定義しない場合は、 Cマクロが使われる。
マクロの構文はどちらの言語でも同じであるが、ヘッダーファイルに含まれる一連のマクロ定義は異なり、それぞれの適切な呼び出し表記に合わせて拡張されている。

たとえば、d3d.h ヘッダーファイル内の次のコードでは、 IDirect3D9::GetAdapterIdentifier メソッドに対する C と C++ 両方のマクロが定義されている。


...
#define IDirect3D9_GetAdapterIdentifier( p, a, b, c ) ( p )->lpVtbl->GetAdapterIdentifier( p, a, b, c )
...
#else
...
#define IDirect3D9_GetAdapterIdentifier( p, a, b, c ) ( p )->GetAdapterIdentifier( a, b, c )
...
#endif


これらのマクロのいずれかを使うには まず、関連図けられているインターフェイスへのポインタを取得する必要がある。
マクロの最初のパラメータに、このポインタを設定する。
その他のパラメータは、メソッドのパラメータにマップされる。
マクロの戻り値は、メソッドが返す HRESULT 値である。
次のコードはマクロを使って IDirect3D9::GetAdapterIdentifier メソッドを呼び出している。
ここで、 pD3D は IDirect3D9 インターフェイスへのポインタを表す。



hr = IDirect3D9_GetAdapterIdentifier( pD3D, Adapter, dwFlags, pIdentifier );



【 DirectX 9.0 COMインターフェイスでの ATLの使用 】

Active Template Library ( ATL ) を使うためには、 ATL との互換性のためにインターフェイスを再定義する必要がある。
これにより CComQIPtr クラスを正しく使って、インターフェイスへのポインタを取得できる。


Attachメソッドを使用して ::Direct3DCreate9が返すインターフェイスポインタにインターフェイスをアタッチする必要がある。
こうしないと、 IDirect3D9インターフェイスはスマートポインタクラスによって正しく開放されない。


オブジェクトが生成された時と CComPtr クラスにインターフェイスが割り当てられたときに、 CComPtr クラスはインターフェイスポインタで内部的に IUnknown::AddRef を呼び出す。
インターフェイスポインタのリークを防ぐため ::Direct3DCreate9 が返すインターフェイスで IUnknown::AddRef を呼び出してはならない。

次のコードは IUnknown::AddRef を呼び出さずにインターフェイスを正しく開放する

CComPtr< IDirect3D9 > d3d;
d3d,Attach( ::Direct3DCrate9( D3D_SDK_VERSION ) );



上のコードを使用する。次のコードは使ってはならない。

CComPtr< IDirect3D9 > d3d = ::DIrect3DCreate9( D3D_SDK_VERSION );




【 IUnknown インターフェイス 】

すべての COMオブジェクトは IUnknown というインターフェイスをサポートする。
このインターフェイスは、オブジェクトの有効期間を制御する機能と、オブジェクトが実装するほかのインターフェイスを取得する機能を提供する。

IUnknown は次の3つのメソッドを持つ。

AddRef → インターフェイスの参照カウントを1ずつ増やす
QueryInterface → オブジェクトが特定の COM インターフェイスをサポートしているかどうかを判別する。
 インターフェイスをサポートしている場合、システムはオブジェクトの参照カウントを増やす。
 アプリケーションは、そのインターフェイスをすぐに使える。
Release → インターフェイスの参照カウントを1ずつ減らす。


AddRef と Release はオブジェクトの参照カウントを管理する。
たとえば、 オブジェクトを作成すると、そのオブジェクトの参照カウントは1に設定される。
関数は、そのオブジェクトのインターフェイスへのポインタを返すたびに、そのポインタを返すたびに、そのポインタを使って AddRef を呼び出し、参照カウントをインクリメントしなければならない。
また、 AddRef を呼び出した場合は、Releaseを呼び出さなければならない。
ポインタを破棄する前にはそのポインタを使って、 Release を呼び出さなければならない。
オブジェクトの参照カウントが0になると、オブジェクトは自動的に破棄される。

QueryInterface メソッドは、オブジェクトが特定のインターフェイスをサポートしているかどうかを調べる。
 目的のインターフェイスがサポートされている場合、そのインターフェイスのポインタが返る。

次に、そのインターフェイスのメソッドを使って、オブジェクトとやり取りができるようになる。
インターフェイスへのポインタが正常に帰った場合、 QueryInterface は非明示的に AddRef を呼び出して、参照カウントをインクリメントする。
このため、インターフェイスへのポインタを破棄する前にアプリケーションは Release を呼び出して、参照カウントをデクリメントしなければならない。



これも飽きた
前回から早数週間・・・


【 ( URL) iPhone : OpenGL ES で画像をイヤラシくだす。① 】




OpenGL ESで画像を生成したのはいいものの

[貼り付け] → [描画] を書いていないという現実逃走をしていたので



今回は実際に読み込んだ画像を画面に出してみます!(`・ω・´)




前回、
画像を読み込み

テクスチャに貼り付けて表示するには2のべき乗でないとダメなので
2のべき乗で生成したbitmapに画像を作りなおす
ところまでを終えました。



次に、
テクスチャを作成し
テクスチャに、生成したbitmapをぺたりと貼り付けてみようと思います。




テクスチャの生成は簡単です。

OpenGL ES初期化時に フレームバッファとレンダバッファを生成しましたが
それと同じ流れになります。 Gen で作成し、 Bind で設定する。



種類は違えど、

処理を同じ流れで作っていただくととても覚えやすくなりますね!




まずはヘッダーにテクスチャの識別番号を保持する入れ物を宣言します。

これも GLuint( 4 Byte ) なので、テクスチャ自身ではなく、
テクスチャが存在する場所の識別子を保持します。



----- [ ViewController.h ] ----- 

/** テクスチャ **/
GLuint mTexture;




次に .m の初期化関数の下の方でテクスチャを割り当ててみます


----- [ ViewController.m ] ----- 

/** テクスチャ領域を生成し、 mTexture 変数に識別番号を入れる。 **/
glGenTextures( 1, &mTexture );

/** これから使用するテクスチャをセットする。 **/
glBindTexture( GL_TEXTURE_2D, mTexture );





次に、テクスチャにBitMapを貼り付けるのですが、

その前に貼り付け方や保管方法などの設定をします。


----- [ ViewController.m ] ----- 

// 自動的にミップマップ画像を作成してくれる
glTexParameteri( GL_TEXTURE_2D, GL_GENERATE_MIPMAP_HINT, GL_TRUE );

// テクスチャを拡大縮小するときの保管方法
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER , GL_LINEAR );
glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );

//
テクスチャの繰り返し方法
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_SGL_CLAMP_TO_EDGE );
glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_TGL_CLAMP_TO_EDGE );




テクスチャを実際に画面に出す時、
テクスチャを実際のサイズより大きく表示したり小さく表示したりすることが多々あります。


その時、


予め適当な小さい画像、大きい画像を作成しておき
それを表示することでぼやけが少なくなります。

「ミップマップ画像を自動生成」というのはそれを自動で作成してくれるというものです。



内部で勝手にしてくれるので最初は適当に放置しておいてありがたく使用しておきましょう!





「テクスチャを拡大縮小するときの保管方法」
「テクスチャの繰り返し方法
」 とかは・・・


まぁこの設定でいいと思います(適当)





決して面倒くさいからとかではない・・・



というかココらへんは最初は別に気にせずに先行きましょ。

取り敢えず最初は描画できたらおっけー という感じで。






設定が終わったら後はテクスチャに2のべき乗のビットマップを貼り付け。



【 glTexImage2D 関数】
 
void glTexImage2D(
        GLenum target,                
/* GL_TEXTURE_2D 固定 */
        GLint level,                        /* MIPMAPレベル  */
        GLint internalFormat,      /* テクスチャの形式 */
        GLsizei width,                   /* テクスチャの幅 */
        GLsizei height,                 /* テクスチャの高さ */ 
        GLint border,                    /* テクスチャの境界線の幅 */
        GLenum format,              /* ピクセルの形式 */
        GLenum type,                  /* ピクセルの型 */
        const GLvoid *pixels );   /* 画像を格納した配列のポインタ */



・levelはMIPMAPを使わなければ 0 でおkです。
・テクスチャの幅高さは2のべき乗になっていなければなりません。
・境界線は基本 0 で。
・テクスチャの形式とピクセルの形式は 通常ではGL_RGBA になります。
・ポインタにビットマップのピクセルが入った配列を渡します。




// 画像をテクスチャに貼り付け。
// テクスチャは2のべき乗。
glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, mTexSize.width, mTexSize.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pImageData );




これで終わり!


テクスチャにbitmapを貼りつけたので
後はこれをポリゴンに貼り付けるだけです。



前回のプロジェクトを流用するので
iPhoneの画面全体にテクスチャを描画します。



注意点として、

テクスチャでは幅高さの大きさにかかわらず、
必ず大きさが 0.0 ~ 1.0 の間になります。





アーボッコさんでは ( 320, 480 )の画像 ( 512, 512 ) のビットマップに生成し直し、

それを( 512, 512 )のテクスチャに貼り付けました。



となると、テクスチャ座標系では

512 が 1.0  となります。



( 512, 512 ) のテクスチャから ( 320, 480 ) を切り出したいので、、、


( 幅 ) 320 / 512 = 0.625;
( 高 ) 480 / 512 = 0.9375;  





アーボッコさん画像全体のテクスチャ座標になります。




この数値を描画時に使用します。



ポリゴンを描画する前に、頂点ごとにテクスチャの座標を指定して、
使用するテクスチャを設定すればポリゴンにテクスチャが貼り付けられます。




どんな画像サイズが来てもいいように数値を直書きではなく

 「画像サイズ ÷ テクスチャサイズ」 をプログラム上に書きます。


// テクスチャ座標
GLfloat tx = 0.0f;
GLfloat ty = 0.0f;
GLfloat tw = mImageSize.width/mTexSize.width;
GLfloat th = mImageSize.height/mTexSize.height;

const
GLfloat texCoords[] =
{
    tx, ty, // 左上
    tx + tw, ty, // 右上
    tx, ty + th, // 左下
    tx + tw, ty + th, // 右下
};

//
テクスチャ機能を有効にする
glEnable( GL_TEXTURE_2D );
glBindTexture( GL_TEXTURE_2D, mTexture );



注意点としては


  glEnable で テクスチャを有効にし、
 glBindTexture で使用するテクスチャをコンテキストにバインドします。


これを行わなければポリゴンにテクスチャが貼れずに困惑します。




後はポリゴンの頂点座標と頂点色みたいにテクスチャ座標をOpenGL ESに教えます。


//** テクスチャ座標をOpenGL ESに教える
//**     座標の数、 型、 オフセット値, テクスチャ座標が入った配列
glTexCoordPointer( 2, GL_FLOAT, 0, texCoords );
glEnableClientState( GL_TEXTURE_COORD_ARRAY );





こんな結果に。





下のポリゴン色と混じってます(´・ω・`)




まぁ、コレでもいいんですが出来れば混ざってない物がいいですね。

ポリゴンとテクスチャの色をまぜまぜ操作するのが glTexEnvi 関数です。



void glTexEnvi(
    GLenum target,    /** GL_TEXURE_ENV 固定 **/
    GLnum pname,     /** GL_TEXTURE_ENV_MODE 固定 **/
    GLint param );      /** 合成方法 **/



三番目の引数の param を変更します!
※初期設定は GL_MODULATE になっています。


・GL_MODULATE
・GL_DECAL
・GL_REPLACE
・GL_BLEND



が指定できます。






----『 GL_REPLACE 』----

テクスチャ色でポリゴンを塗りつぶします



これが欲しい結果ですね!




----『 GL_DECAL 』----

テクスチャでアルファ値が 1.0 以下 の部分のみ ポリゴン色と混ざります。








しかし、
基本この関数は使わないと思います。



ポリゴン色を全部 ( 1.0, 1.0 1.0 1.0 ) の真っ白にしておくのが普通だと思います。




2Dでポリゴン食と混ぜることなんて殆ど無いですよねー・・・・  ?

ですよね?(不安)








後、2Dのテクスチャで使うといえば

アルファブレンドじゃないですかね。


アルファ値を持ったテクスチャと元々あった色を混ぜます。

これは


//** アルファブレンド
glEnable( GL_BLEND );
glBlendFunc( GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA );



この設定を利用するだけでいいと思います。


アルファ値が 0 ならば 完全に元々あった色が出てきて、 
0.5 ならば テクスチャの色と元々の色が半分ずつ混ざって表に出てきます。

これは最初に一回宣言しとけばおkです。




今回は↓







しゃー の所のアルファ値が 0 だったので完全に元々あった色が表に出てきています。




説明ベタなので今回の記事全てにおいて
よくわからなかったと思いますが、

ソースコードを見ていただくとわかると存じます!



【( URL ) ソースコード
【( URL ) OpenGLES_Test3




2D では これだけでゲームが作れると思います。
 


OpenGL ES は 3Dを使うと、とても気持ちよくなります。

3Dのためにも2Dをせっせと勉強してみたものの未だにわからない部分が多々あります・・・(´・ω・`)


勉強って大事ですね-・・・