前回はOpenGL環境のセットアップまで行った。これでようやく絵を描く準備が整ったことになる。今回は、いよいよ実際に画面に絵を表示させてみよう。まずはゲームの背景画像を描いてみる。
テクスチャの作成
OpenGLを使って絵を描く場合は、画像ファイルを読み込んで、これをテクスチャとして利用することになる。画像ファイルを読み込むところまではCocoaやCore GraphicsのAPIを使い、テクスチャを作成するところはOpenGLの関数を使うことになるだろう。
早速だが、そのような動作を行うソースコードを紹介しよう。loadTextureName:texture:と名付けたメソッドだ。このメソッドは、引き数を2つとる。第1引き数は、読み込む画像ファイルの名前だ。これは、プロジェクトに直で追加されていることを想定している。第2引き数は、作成されたテクスチャの名前となる。GLuintのポインタで渡すので、メソッドからの出力値として使われることになる。
List 1.
- (void)loadTextureName:(NSString*)imageName texture:(GLuint*)texture
{
// CGImageを取得する
CGImageRef cgImage;
cgImage = [UIImage imageNamed:imageName].CGImage;
// データプロバイダを取得する
CGDataProviderRef dataProvider;
dataProvider = CGImageGetDataProvider(cgImage);
// ビットマップデータを取得する
CFDataRef cfData;
GLubyte* data;
cfData = CGDataProviderCopyData(dataProvider);
data = (GLubyte*)CFDataGetBytePtr(cfData);
// テクスチャを作成する
glGenTextures(1, texture);
// テクスチャをバインドする
glBindTexture(GL_TEXTURE_2D, *texture);
// ビットマップ画像を設定する
glTexImage2D(
GL_TEXTURE_2D, 0, GL_RGBA,
CGImageGetWidth(cgImage), CGImageGetHeight(cgImage),
0, GL_RGBA, GL_UNSIGNED_BYTE, data);
CFRelease(cfData);
// テクスチャの設定を行う
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glEnable(GL_TEXTURE_2D);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
}
このメソッドでの処理は、大きく分けると2つに分かれる。画像ファイルを読み込んでビットマップを取り出すまでと、そこからテクスチャを作成するところだ。
画像ファイルからのビットマップ取り出しは、主にCore GraphicsのAPIを使っている。詳しいことは、本連載の第4回『カメラアプリの作り方 (4) - 写真にエフェクトをかける』を参考にしてほしい。
テクスチャの作成には、OpenGLの関数を使う。まず、glGenTextruesを使ってテクスチャを作成する。次に、glBindTextureを使ってそのテクスチャをバインドし、glTextImage2Dでビットマップ画像を設定するのだ。あとは、いくつか必要な設定を行っている。
glTexImage2Dを呼び出すときは、その引き数に注意してほしい。ビットマップデータのフォーマットやデータ形式を指定するのだが、今回は決めうちで行っている。本来は、読み込んだ画像ファイルのフォーマットに従って、変更できるようにすべきだろう。今回は画像ファイルのフォーマットが、Big EndianのTIFFかそれと同等のものになる、という前提にあわせて設定している。
また、画像の大きさも重要だ。OpenGL ES 1.0では、テクスチャの縦、横の大きさが、2のべき乗でなくてはいけない、という制限がある。読み込む画像ファイルは、この制限に適合するように気をつけて作成しよう(この制限は、OpenGL ES 2.0で緩和される)。
たとえば、ゲームの背景画像を読み込ませることを考えよう。iPhoneのスクリーンサイズは320 x 480なので、この大きさの画像を用意することになる。だが、この縦横の値は、2のべき乗ではない。そこで、これらの値より大きい2のべき乗の値になるサイズにしなくてはいけない。つまり、512 x 512の画像になるのだ。
具体的には、次のような画像を用意すればいい(本稿の最下部からダウンロードできるサンプルソースコードに512 x 512のTIFF画像を含めてある)。
余分なところは黒で塗りつぶしてしまおう。または、この領域に別の画像を埋め込んでしまうこともできる。これについては、次回以降で説明しよう。
ポリゴンの作成とテクスチャの表示
テクスチャを作成したら、それを画面上に表示しよう。OpenGLで表示するので、ポリゴンを作ることになる。そこにテクスチャを貼付けてやればいいのだ。
ゲームの背景画像を表示するためのメソッドを紹介しよう。drawBackgroundだ。
List 2.
- (void)drawBackground
{
// 頂点を作成する
GLfloat vertices[] = {
-1.0f, -1.5f,
1.0f, -1.5f,
-1.0f, 1.5f,
1.0f, 1.5f
};
glVertexPointer(2, GL_FLOAT, 0, vertices);
// 色を設定する
GLubyte colors[] = {
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
255, 255, 255, 255,
};
glColorPointer(4, GL_UNSIGNED_BYTE, 0, colors);
// テクスチャの設定をする
GLfloat coord[] = {
0, 0.9375,
0.625, 0.9375,
0, 0,
0.625, 0,
};
glTexCoordPointer(2, GL_FLOAT, 0, coord);
// 描画を行なう
glBindTexture(GL_TEXTURE_2D, backTexture);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glBindTexture(GL_TEXTURE_2D, 0);
}
このメソッドでの処理は、頂点の配列を用意してポリゴンの作成、色の配列を用意して頂点カラーの設定、テクスチャ座標の配列を用意して頂点への設定、そしてテクスチャをバインドしてポリゴンの描画、という流れになる。
順に見ていこう。まず、頂点配列を用意する。今回はスクリーンいっぱいに表示するポリゴンとなるので、それにあわせた頂点を用意している。スクリーン左下が(-1.0, -1.5)、右下が(1.0, -1.5)、左上が(-1.0, 1.5)、右上が(1.0, 1.5)となる。この座標系の値については、前回の記事を参考にしてほしい。この頂点配列をglVertexPointerに渡すことで、ポリゴンが作成される。
ポリゴンを作成するときに、glBeginおよびglEndと、glVertexを使っていないことに注意してほしい。なぜこれらの関数を使わないのかというと、OpenGL ESではこれらはサポートされていないからだ。ポリゴンを作成するときは、頂点配列を用意してglVertexPointerを使うことになる。この関数を使うときはglEnableClientStateで、GL_VERTEX_ARRAYを有効化しておかないといけないのだが、これは前回紹介したrenderメソッドの中で行っている。
次に、頂点カラーを設定する。これにはglColorPointerを使う。頂点カラーとして半透明色を指定すれば、テクスチャが半透明で描画されるだろう。
続いては、テクスチャ座標系の設定だ。それぞれの頂点に、テクスチャのどの座標を表示するかを指定する。ここで気をつけてほしいのが、テクスチャのサイズと表示したい領域が違っていることだ。テクスチャサイズは512 x 512だが、背景として使いたいのはその中の320 x 480の領域である。そこで、その座標を比率として計算して、テクスチャ座標の配列を作る必要があるのだ。
最後に描画を行う。glBindTextureで目的のテクスチャをバインドし、glDrawArraysを呼ぶことで画面に描画できる。
これが、OpenGLを使った2次元描画となる。
ここまでのソースコード: Defense-1.zip