前回は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