05/06/20 | VBでOOP・STL風味編−2(異なるクラスに対するソート)[★★★★◎◎] | |||||||||
前回は汎用性のあるソートを行ってみましたが、今回はそれについてもう少し踏み込んでみます。 クラスの並べ替え 並べ替え対象のクラス作成 比較用クラス作成 実際にクラスを並べ替えてみる まとめ おまけ:型によらない代入 クラスの並べ替え 前回のプログラムでは、比較用クラスだけを入れ替えることによって整数配列に対して様々なソートを行いました。 この汎用ソートはコレクションで配列を渡すため、Variantで格納できる値なら何でもソート対象に出来ます。(もちろん比較用クラスが適切に作成されていれば、ですが) そこで、今度は整数ではなくクラスを入れてみることにします。 1種類のクラスではつまらないので、StudentクラスとTeacherクラスが混在しているコレクションのソートをしてみることにします。 ここでは両クラスが共にHeight・Weight・Ageプロパティを持っているものとします。 後はHeightやWeightで比較するような比較用クラスを作成すればStudentだろうがTeacherだろうが関係なくソートできます。 VBのVariant型(やObject型)では、実行時に「そのメソッド・プロパティが存在するか?」をチェックするので、その比較用クラスが要求するプロパティさえ持っていればどんな型でもソートできることになります。 それとは別に、自分の属性を文字列で返すようなメソッドをStudentにもTeacherにも準備しておきます。 このメソッドも名前を揃えておけば中身に関係なく属性を文字列で返させることができます。 ここではGet_Stringメソッドとしています。
並べ替え対象のクラス作成 さて、実際にStudentクラスとTeacherクラスを作成してみます。 プロパティとしてHeight・Weight・Ageだけではつまらないので、StudentにはGrade(学年)、TeacherにはDomain(科目)というプロパティも持たせてみました。
どちらもCreate_Instanceというメソッドを作っていますが、これは初期値を与えつつ新しいインスタンスを作成するトリックみたいな手法です。 VBでは初期値を指定しつつクラスをNewすることが出来ません。 1つ1つNewした後で初期値を指定するのは面倒です。 なので、内部に初期値を指定するとその初期値を持つクラスを返してくれるような関数を作成しておきます。 とはいえ、Create_Instanceメソッドを呼ぶのにやはりインスタンスが必要なので、1つだけ静的にインスタンスを作成しておいて、後はそこからCreate_Instanceでポコポコとインスタンスを作成するという手法を取ります。 JavaやC++だとインスタンスを作らなくてもstatic属性のメソッドは呼ぶことが出来るし、そもそもNewの時に初期値も与えられるんでこんなことしなくてもいいんですけどね… 比較用クラス作成 では、比較用のクラスを作成してみます。 クラスのHeight・Weight・Ageのプロパティで比較するだけなのでそれほど難しくはありません。
いずれも前回同様ICompareをImplementsしています。 それぞれ異なるプロパティに対して大小比較の結果を返すだけの関数です。 実際にクラスを並べ替えてみる 実際に3つのボタンによってそれぞれ異なるソート結果をList1に出力するプログラムを書くと、以下の様になります。 Create_InstanceのためにStudent・Teacherを1つずつだけDim文で作成しています(stとte)
例えばIndex=0のボタンを押した場合、CmpHeightを使ってソートするので出力結果は以下の様になります。 文字列化のためのメソッドも名前を統一してあるので、StudentもTeacherも関係なく出力できます。
まとめ 前回作成したソートプログラムを利用すると、クラスに関してもソートを行うことが出来ました。 また、VBの「動的にメソッド・プロパティの存在を確認する」という特性を用いて、異なるクラスの混ざった状態でもソートをうまく行うことが出来ました。 Student・Teacher以外にもHeightとWeightとAgeとGet_Stringさえ持っていればどんなクラスでも同じようにソートすることが出来ます。 まぁ今回も速度的にはかなり遅そうですが、整数のソートに比べると複雑な機能がわりとさらっと実現できるのは面白いところだと思うのですがどうでしょうか。 ソートはここまでにして、次回は配列の要素それぞれに対して関数を呼び出すということをしています。 STLで言うところのfor_eachに相当します。 さらに、ちょこっとfunctorみたいな事をしてみる予定。(Binder1stとか…) 単にfor_eachやfunctorだけではSTLの劣化コピーでしかないので、VBならではの手法として規定プロパティを利用してさらにfor_eachをトリッキーに利用してみようかと思います。 まぁプログラム実験部屋に先にコード出してますが(^^; おまけ:型によらない代入 今回の様なプログラムを扱う場合、Variant型の中に整数や小数の様な型が入ることもあれば、クラスが入ることもあります。 ここで厄介となるのが、「クラスの代入にはSet文が必要である」ということです。 あるVariant型変数vの中身がクラスかどうかわからないけど別の変数aに代入したい場合、以下の様に書く必要があります。
まぁ面倒ですが一応解決できています。 では、aに代入するのがvではなくて何か計算式だとか関数の返り値だったらどうするか。 例えば関数SomeFuncというものがあったとします。 そのままvのあった場所に当てはめると、まずIsObject関数でオブジェクトかどうかを判定するためにIsObject(SomeFunc())とすると…
またSet a = SomeFunc()とかa = SomeFunc()の様に2回関数を実行する羽目になります。 これは処理時間的にも問題ですし、そもそもStatic変数やグローバル変数が絡んでたりすると2回の結果が同じとは限りません。 「じゃあ一端SomeFunc()の結果を変数に代入すればいいじゃん」 って今やろうとしていること自体が代入なわけで、このままではぐるぐるめぐり。 「SomeFunc関数の中で別の変数に結果を保存しておいて、別にその結果を再度取り出す関数を作れば?」 確かにSomeFunc関数自身が返り値をクラスかそうでないのか知っているならばうまく行きそうですが、今回のソートの様な場合関数内で扱う値が結局クラスかどうかわからないので、「別の変数に結果を保存」で結局代入の問題がでてきます。 結局どこかでこの問題を解決する手法を考えないといけません。 解決法としては、「クラスだろうとそうでなかろうととにかく一端変数に代入する」ということがあります。 まぁそれが出来ないから苦労してるんじゃないかと言われればそれまでですが、一応解決策は存在します。 とりあえず思いついたのが2つなので両方紹介しておきます。 速度的にはどっちも微妙ですが…
しかし、正直Setと書かされる言語仕様はどうかと思いますね… クラスでない変数の代入ももともとLetが必要だったのが省略されたんで、こっちも省略可能にしてしまえばいいのに。 VBにとっては、Variant型の代入の時にはどのみち両辺の型をチェックしてから代入するんだし、Variantでないときはコンパイル時にクラスかどうかわかるんだし、わざわざSetと書かせるメリットというと、利用者に「これはクラスの代入なんだ」と意識させることぐらいしか思いつかないです。 この代入の話はソートだとどうってことはないですが、for_eachみたいな事をやろうとすると気になるところなので今回のうちに扱っておきました。 (というか自分がやってて気になった…) |
05/05/19 | VBでOOP・STL風味編−1(汎用ソートアルゴリズム)[★★★★◎] | |||||||||||||||||
VBはオブジェクト指向の言語としては非常に未熟とか機能不足とか言われます。 実際そうなわけですが、VBの持っている機能でもがんばればそこそこまで行ける!ということで、無理やりな利用法にチャレンジ。 今回のはC++やJava等、オブジェクト指向寄りな言語を使ったことが無いとちょっとわかりにくいかもしれないです。 さしあたりVBのクラスモジュールの機能を知っていると読みやすいと思います。 C++のSTLをベースにするのでSTLを知っているとさらによし。 今回はSTLにあるsort関数っぽいものをVBで再現したいと思います。 今回の内容はプログラム実験部屋にプログラムを置いてあります。 汎用性のあるソートアルゴリズム VBにおいて呼び出す関数を動的に変えるには? 比較方法を表すクラスを考えてみる ソートの中身を考える 整数を並べ替えてみよう まとめ 補足:ICompareの意義? 汎用性のあるソートアルゴリズム プログラムを書いていると、配列などのデータ群をソートする場面はよくあります。 ただ、VBでこれらのプログラムを書く場合、型ごとやソートの順番ごとにソートの関数を作成したりすることは多いと思います。 しかし、大抵の場合ソートは、 「なんらの基準で2値を比べて、並べ替える」 という処理になっています。 極端な話、数値のソートと文字列のソートなんて大小比較の部分がちょっと違うだけで後は同じ処理でソートできます。 (まぁ場合によってクイックソートが使いたいとかヒープソートが使いたいとかあるかも知れませんが・・・) そこで、共通化できる部分は共通化し、変えたい部分(上の例では大小比較の部分)だけ変えることで使いまわしの出来るソート関数を考えて見たいと思います。 実際C++のSTLのソートではそのような設計になっています。 VBにおいて呼び出す関数を動的に変えるには? さて、上の様に大小比較の所だけ変えるようにしたい場合、比較関数を用意しておいて、必要に応じて呼ぶ関数を変えることで大小比較のやり方も変えることが出来ると便利です。 必要に応じて呼ぶ関数を変えることは出来るのでしょうか。 C言語などには関数ポインタという概念があります。 関数ポインタの中身を書き換えることで動的に呼ぶ関数を変えることが出来ます。 VBでは関数のアドレスはAddressOf演算子で取得することが出来ますが、そのアドレスの関数を呼ぶことは出来ません。 (コールバックを用いるAPIを使ってトリッキーにやることは出来るかもしれませんが、言語仕様としては無理) では、どのように関数を動的に変えるか。 ここで、VBだとそれほど有効に利用されていない(気がする)クラスモジュールが登場します。 VBで、あるクラスA型の変数aとB型の変数bがあったとします。 どちらもあるメソッドhogeだけが利用できるとします。 a.hogeだとAのhogeメソッドが呼ばれ、b.hogeだとBのhogeメソッドは呼ばれるのは当たり前のことです。 では、次の様により汎用の型に代入して実行してみるとどうなるでしょうか。
ちゃんとそれぞれのhogeメソッドが呼ばれると思います。 これでわかることは、呼ぶ関数を直接切り替えることは出来なくても、同じメソッドを持つクラスならより汎用的なクラス(ここではObject型)に代入することで呼ぶメソッドを変える事が出来ると言うことです。 この例では、クラスAもBは(まぁ他の機能もあるのかもしれませんが)さしあたり呼ぶ関数を切り替えるためだけのクラスといえます。 このように、関数のように振舞うオブジェクトのことを関数オブジェクトと言います。 これを使うと、ソートの大小比較をクラスで表して、そのインスタンスを切り替えることで比較のやり方を動的に変えられるような気がしませんか? 比較方法を表すクラスを考えてみる ここまでで、どうやらクラスをうまく使えば比較方法を動的に切り替えることが出来そうだということがわかったと思います。 ただ、上の例でもわかるように異なるクラスでもメソッド名が一致すればうまく呼び出せますが、メソッド名がわからないとどうにもなりません。 ある比較用クラスではLessというメソッドかもしれないし、他ではGreaterとかCompareとかBiggerとか名前がばらけてしまう可能性があります。 そこで、ある程度比較用クラスの仕様は統一しておく必要があります。 そのためにVBではImplementsというステートメントを利用できます。 JavaのImplementsとほぼ同じ意味ですね。 比較用のベースとなるクラスを作っておき、各比較関数はベースのクラスに対してImplementsすることでメソッド名などの仕様をあわせることが出来ます。 ここで、比較用のベースとなるクラスICompareを作成してみます。 比較用のクラスは、必ずCompareメソッドというメソッドを持つことにしてみましょう。 ICompareは以下の様な感じになります。
メソッドの中身は空で構いません。 中身はこれをImplementsするクラスが生めていきます。 というよりも比較用クラスは、このCompareメソッドを作る必要があります。 さしあたりこのメソッドは2つのVariant値を取ってBooleanを返すメソッドだということを示したことになります。 ソートの中身を考える さて、ようやくImplementsするクラスを作るか…と行きたい所ですが、先にソート全体の仕組みを考えておきます。 ソートするからには、ソートする対象となる配列か何かが必要です。 ただ、VBの場合配列というと決まった型の配列になってしまいます。 そこで、今回はCollectionを使用します。 ユーザー定義型が 代入できないのは少し痛いですが、Variant型に代入できる型であれば、整数でも小数でも文字列でも、あとはクラスでもほうりこめます。 Collectionと比較用クラスを引数に取って、Collectionの並べ替えを行うソート関数は以下の様に書くことが出来ます。 (2005/06/20修正)
ソートの手法は別にクイックソートでも何でもいいんですが、ここではわかりやすくバブルソートにしてみました。 このアルゴリズムでは、単純に隣同士の2値を比較して並び順が逆の方がよい(Compareの結果で判断)のであれば入れ替えるということを延々と繰り返します。 Collectionでは直接値を交換することは出来ないようなので、一端Collectionからはずして挿入しなおすということをしています。 (速度的には非常に無駄が多いですが、今回の本題は速度では無いのでそこは一端おいといてください。) Collectionの中身がオブジェクトでもそうでないものでもいいように、IsObjectで中身がオブジェクトかどうかチェックしつつ入れ替えを行っています。 整数を並べ替えてみよう 上のソート関数を使って実際に整数を並べ替えて見ます。 入力の引数はCollectionと比較関数です。 Collectionはソート対象の整数をどんどん入れていくだけでOKです。 となると、ソートを行うためには比較関数を作る必要があります。 まぁどんな並べ替えでもいいんですが、ここでは単純に 「小さい順」「大きい順」「文字列化して小さい順」 の3種類のクラスを作ってみます。 各クラスはICompareクラスをImplementsし、Compareメソッドの中身を埋めればOKです。 Compareメソッドは2値を比較してBooleanを返すだけなのでそれほど深く考えず作って見ましょう。
ここまでやると、ようやく準備終了です。 実際にこのソート関数と比較クラスを使ってソートをして見ます。 ここではコマンドボタンCommand1を3つ作り、それぞれIndexが0〜2となるようにしていたとします。
さて、結果はどうなるかというと・・・
結果を見ると、確かに意図したとおりの結果になりました。 まとめ 今回は1つの汎用ソート関数Algorithm_Sortを作成し、後は比較用のクラスを作っただけで1種類の関数でソートの方法を変えることが出来ました。 C++のSTLあたりだとガリガリ速度面で最適化してくれるんですが、VBだと残念ながら余り最適化は望めません。 Algorithm_Sortにどんな比較クラスが入ってくるかわからないし、CollectionはCollectionで中身が整数か文字列かクラスかわからないからです。 後、残念なのがユーザー定義型の並べ替えが出来ないことでしょうか。 まぁ入力をCollectionでは無く、ユーザー定義型の配列にすれば出来るんですが、そうするとその関数は特定のユーザー定義型専用になってしまいます。 それでも構わない(どうせ1種類の型でしか使わないなど)場合はいいかもしれません。 というわけで速度的には余り有効ではありませんが、スマートにプログラムをすることを考えるとなかなか面白くないですかね。 次回はこのAlgorithm_Sortを使ってもうちょい色々やる予定です。 補足:ICompareの意義? 今回比較用クラスはICompareクラスをImplementsして作成しました。 しかし、このICompareは本当に必要でしょうか? 最初のクラスA・BをObject型に代入している例を見てみると、別にA・Bはメソッドhogeを持つクラスをImplementsしているわけではありません。 それでもなぜちゃんとメソッドhogeが呼べるかというと、メソッドの呼び出しの時に「そのクラスはhogeというメソッドを持つかどうか?」を実行時に判断しているからです(レイトバインディング)。 じゃあ比較クラスもICompareなんて使わずに、Objectを使えばいいじゃんと思うかも知れません。 実際Algorithm_Sortの引数CompClassの型はICompareでなくObjectでも問題なく動きます。 ただ、ICompare型を間に挟む利点として以下の要素が考えられます。
|
05/03/11 | VBでOpenGL編−2(ライティング・テクスチャ)[★★◎◎] | |||||||||||||||||||||||||||||||||||
(自分で書いててナンですが、今回はあまり乗り気でないこともあって出来がかなり微妙。) 前回はOpenGLの初期化・終了についてやりました。 今回はライティング・テクスチャと通常の3Dプログラムを作る際の基本的なものについて扱います。 前回も書きましたが、ここでの内容は「VBからOpenGLを使うこと」を重視しており、OpenGLそのものの活用法についてはC言語とそう変わりはないのであまり細かく触れません。 C言語からOpenGLを使う場合は、OpenGLプログラミングコースがオススメです。 簡単な3Dの概念の説明もあるので、行列変換などの話はそちらを参考に。 で、3D・ライティング・テクスチャについて「VBから使う点で必要なこと」を重視してみていきます。 3Dの基本 行列による変形 ライティング テクスチャ 最後に 3Dの基本 前回は2次元の3角形を描画しましたが、今度は3Dの描画をして見ます。 なお、ここの内容はVBならではの内容は特になく、C言語から利用する際も同じなので、OpenGLプログラミングコースを見るのもいいでしょう。 2次元の時はglBegin〜glEndの間にglVertex2fを頂点の数だけ記述しました。 glVertex2fは2とついているように2つの引数、つまりXとYだけ指定しました。 3次元にする場合は単純にglVertex3fを使ってX,Y,Zの3引数を送ればいいです。 現在のままでは、描画した順番に図形が描画されますが、実際は奥にある面は手前にある面で隠されなければいけません。 そこで、描画の際Zバッファを利用するように指定します。 これはglEnableでZバッファとの値の比較(glcDepthTest、C言語ならGL_DEPTH_TEST)を有効にするだけでOKです(下のプログラムの上2行)。 各フレームの最初にglClearでバッファのクリアを行う場合は、clrDepthBufferBlt(C言語ならGL_DEPTH_BUFFER_BIT)も指定してZバッファもクリアする必要があります(下2行)。
後は3次元で描画するので適切な透視変換などの行列を設定することになります。 まず、カメラのレンズに相当する射影の処理に関しては、OpenGLでは透視投影を行う場合はglFrustumまたはgluPerspective、平行投影を行う場合はglOrthoまたはgluOrtho2Dの各関数を使ってパラメータを設定します。 ここら辺は単に引数に値を設定するだけなので、上のOpenGLプログラミングコースの通りに値を設定すればいいでしょう。 行列による変形 ある物体を描画するとき、物体全体を拡大させたり回転させたりしたい場合があります。 しかし、各頂点でプログラム側で回転させたりするのは面倒です。 OpenGLでは、変形に対応する行列を設定しておくと各頂点に変形を加えてくれます。 よくある変形として、平行移動や回転がありますがこれは既にOpenGLに準備されています。 単純な回転はglRotatef、拡大縮小はglScalef、平行移動はglTranslatefで指定することが出来ます。 これらは引数に回転角度や拡大率を指定すればいいのですが、問題は任意の行列を指定したい場合です。 OpenGLに行列を渡したい場合にどのようにするかということが問題になります。 OpenGLでは行列は基本的に4x4となります。 行列を指定する命令はglLoadMatrixf、glMultMatrixfがあります。 前者は指定した行列を代入し、後者は現在の行列に指定した行列を掛け合わせます。 じゃあ、行列ってどうやって渡すのよ、という話。 ここら辺でようやく「VBであるために気にすべきこと」が登場ですね。 4x4の行列を表現するとなると、普通に考え付くのが4x4の要素を持つ2次元配列か、16個の要素を持つ1次元配列を使うことになります。 実際には、どちらでも利用することが出来ます。 まず、16個の要素を持った配列a(15)を考えた場合、4x4の行列の各要素は以下の様に対応します。
直感的には、横方向に配列のインデックスが進んでいきそうな感じですが、実際には縦に進みますので注意です。 次に、4x4の2次元配列を考えてみる前にVBでの多次元配列の要素がメモリ中にどのように並んでいるかを考えて見ます。 例えば3次元の配列a(2,2,2)があったとすると、VBでは a(0,0,0)、a(1,0,0)、a(2,0,0)、a(0,1,0)、・・・、a(2,2,0)、a(0,0,1)、・・・、a(2,2,1)、a(0,0,2)、・・・、a(2,2,2) と前にある要素の値が1つずれると隣の要素になります。 後ろの要素ほど1値がずれると大きく配置が変わります。 (C言語ではa[0][0][0]、a[0][0][1]、a[0][0][2]と順序が逆) ここでa(3,3)というような2次元配列を考えると、実際にメモリには a(0,0)、a(1,0)、a(2,0)、a(3,0)、a(0,1)、・・・、a(3,3) の順に並んでいます。 これをa(15)の1次元配列の時の様に行列に当てはめると、
となります。右に行くと2つ目の要素が大きくなり、下に行くと1つ目の要素が大きくなります。 これは数学で行列Aの第i行j列目の要素を表すAijという表記と近くて扱いやすいですね。 さらに配列を定義するときに Dim a(1 to 4,1 to 4) の様にインデックスを1スタートにしておくと数学上の表記と一致し、さらに扱いやすくなります。 (C言語では列と行が逆になってしまい、ちょっと直感的でない…) ここで説明したように、16個の要素を持つ1次元配列か4x4の2次元配列を用いて4x4の行列を表現することが出来ます。 値をセットした後、OpenGLに渡すには次の様にします。(それぞれ1次元・2次元の場合) Call glLoadMatrixf(a(0)) Call glLoadMatrixf(a(0,0)) なんで行列を渡すのに最初の要素だけでいいのか?と思うかもしれませんが、通常glLoadMatrixの引数はByRefで定義されています。 ByRefでは実際には指定した値のアドレスが渡されますので、OpenGL側で他の要素も順に取得できることになります。 これは次のライティングでも重要です。 以下の例は実際にVBで行列を指定する例です。 以下の2つの図は、左側は普通に立方体を描画したものであり、右側はZ軸で45度回転した上にY軸を中心に45度回転したものです。 Z軸およびY軸の回転はそれぞれに行列を作り、glMultMatrixを用いて掛け合わせています。 具体的には以下の様なコードになります。
ライティング 基本的にはglEnable(glcLighting)やglEnable(glcLight0)でライティングをオンにし、glVertexで頂点を指定する際に同時にglNormal3f等で法線を割り当てていくだけです。 そこら辺は上にもあるOpenGLのプログラミングコースを見てください。 上の行列関連の命令では、行列をOpenGLに渡す必要がありましたが、このときはByRefを用いて配列の先頭のアドレスを渡すということがありました。 ライティングでも色の値やベクトルを配列を用いて渡す機会があります。 OpenGLのライティングでは、光源の位置や光の色を指定するのにglLightfvという関数を使います。(fはFloat型(VBで言うSingle)、vはベクトルを表す。型が違うglLightdvなんかもある) ここでは光源の座標や光の色のRGBAの値を要素数4の配列を用いて引数に渡します。 例えば、拡散反射の色値をスクロールバーから読み取って指定するには以下の様にします。 glLightfvに渡すのは配列の先頭の要素だけというのは上の行列の話と同じですね。
ライティングに関してVBならではの部分はこのぐらいなので、あとはプログラミングコースを見ながらやれば色々出来るでしょう。 テクスチャ OpenGLではメモリの内容を読み込んでテクスチャとして利用することが出来ます。 メモリの内容をテクスチャとして読み込むにはglTexImage2Dという関数で行うことが出来ます。 読み込んだテクスチャの使い方の概論はプログラミングコースを参照してください。 そこら辺はVBもCもほとんど関係ないので。 で、VBにおける問題は「テクスチャをどのように準備するか?」です。 OpenGLではテクスチャのRGBAの値をそれぞれ1バイト・2バイト・4バイト整数のほか、Single型でも読み込ませることが出来ます。 とはいえRGBA各4バイトも取ってしまうと1ピクセル16バイトにもなってしまいますので、おそらくOpenGLが途中で変換しているのと思われます。(まぁ最近は浮動小数でRGBAを4バイトずつ取ることの出来るGPUも増えてきましたが) 普通は画像を準備する際、RGBはそれぞれ0-255の1バイトの値をとることが多いと思います。 そのため、ここではRGBAがそれぞれ1バイトのテクスチャを考えたいと思います。 OpenGLにテクスチャを渡す場合、各ピクセルは以下の様に配置されている必要があります。
(0,99)のRGBA、(1,99)のRGBA、…、(99,99)のRGBA (0,98)のRGBA、(1,98)のRGBA、…、(99,98)のRGBA ・ ・ ・ (0,0)のRGBA、(1,0)のRGBA、…、(99,0)のRGBA これをみて何かピンと来る人もいるかもしれません。 よく見ると、WindowsのBMPファイルやDIBSection内の色要素の並び方と一緒です。 そのため、BMPファイルをOpenGLのテクスチャとして読み込むにはBMPファイルからDIBSectionを生成するAPIであるLoadImageを用いると非常にラクです。 OpenGLに読み込ませるテクスチャのデータをglTexImage2Dで送るわけですが、行列などと同様色のデータの先頭アドレスを送ればOKです。 glTexImage2Dの引数には、7つ目の引数にRGBAの要素が各何バイトかを指定する部分があります。 OpenGLのAPI宣言をするのに前回紹介したタイプライブラリを用いた場合、VBの補完機能で代入できる値の一覧が表示されると思います。 ここではglcByteとかglcLongとか出てくるので、 「RGBAそれぞれが1バイトだからglcByteだな」 と思ってglcByteにするとうまく行きません。 C言語の方をよく見ると、C言語では7つ目の引数に代入できる値にGL_UNSIGNED_BYTEとGL_BYTEがあります。 このタイプライブラリのglcByteの値はGL_BYTEと同じであり、符号付1バイト整数値を表すことになります。 VBのByte型は0〜255を表すということで符号付ではありませんので、これではうまく行きません。 このタイプライブラリではGL_UNSIGNED_BYTEに相当する値は定義されていないので、自分で定義するか直接対応する定数を書く必要があります。 ちなみにGL_UNSINGED_BYTEの値は5121です。 最後に 申し訳ないのですが最初にも書いたとおり、今回はあまり乗り気ではないことも会って手抜きな部分が多い。 どちらかというと「VBで3D描画をガリガリやりたい人向け」というよりは、「VBでOpenGLをやるときにちょっと注意する点」みたいな感じの説明文になってしまっている。 (プログラム講座にあるサンプルにある)プログラム作成自体は面白かったんだけど、説明となるとOpenGLプログラミングコースで十分説明されてしまっていることもあり気が乗らず。 ということで今回はVBならではの点を注意して見ました。 具体的にはここの説明を見るよりも、実際にコードを追ってみるかソースを見たほうがわかりやすいかも。 もっと自分が興味を持てるネタを扱わないとモチベーションが上がらないね。 |