▽OpenGLでテクスチャマッピング(その1)

MFCベースのプログラムでOpenGLを使用するときのサンプルです。今回は簡単な(=手抜きの)アニメーションとOpenGL 1.0までの形式でのテクスチャマッピングを行ってみます。

まずはサンプルプログラムをどうぞ。

サンプルプログラム
sk00b-07.lzh

テクスチャマッピングをされた箱とティーポットがぐるぐる回るだけのサンプルです。

箱とティーポットぐるぐる
箱とティーポットがぐるぐる回る図

以下、サンプルプログラムのピンポイント解説です。

○簡単なアニメーション

一応、アニメーションしていますが、これはSetTimer()で1/30secごとにタイマーを発生させ、画面全体を更新しているだけの非常に手抜きなものです。

今回のように実験で使うか、もしくは簡単なツールのようなものならこれでも問題ないでしょうが、シビアなタイミングが要求されるときはこのような手抜きな方法は使わないようにしましょう。

まともなやり方だとフレームの描画が終わったところでtimGetTime()を使ってタイミングを計ったりするのですが・・・ まぁ、これについてはまた別の機会に。気になる人は自分で調べてみてください。

今回は関係ありませんが・・・ ポインタの影を有効にしてると、マウスカーソルをウィンドウに乗せるだけでそれなりに負荷がかかったりします。

ちなみに、アニメーションで使用しているカウンタの取り扱いも非常に手抜きです。このサンプルではOpenGLの環境が貧弱で駒落ちが発生するような場合、アニメーションの速度も遅くなります。

このあたりも、本来なら環境に関係なく一定の速度でアニメーションするべきですが・・・ まぁ、実験用のプログラムなので手抜きです。

実は、OpenGLの描画に何も考えずにGDIの描画をかぶせているのも問題があります。しばらく画面を見ていれば、文字の部分がちらついているのはすぐにわかるかと思います。この現象を回避するには以下のような方法が考えられます。

1.すべてOpenGLで描画する
ある意味、これが一番まじめなやり方です。ゲームなどでは必然的にこうする事になるでしょう。文字の描画はビットマップに行って、それをテクスチャマッピングで利用します。
2.OpenGLのレンダリング結果をGDIで利用する
この場合、『OpenGLで描画→ビットマップにデータを取り込み→ビットマップにGDIで描画→まとめて画面に転送』と、こんな感じの流れになります。大きな転送が2回はいるので処理が重そうに見えますが、実際にやってみると結構軽く動きます。ツールなどで利用するならこれが無難なのかもしれません。
3.あきらめる
ソフトの目的次第ですが、本質と関係ないところの問題は見て見ぬふりするのもプログラマーの腕です。たぶん。

○テクスチャマッピング(旧形式)

引き続き、サンプルプログラムを使って、今度はテクスチャマッピングの説明をします。ちなみに、ここでの説明はOpenGL 1.0までの古い形式です。基礎知識として押さえておきましょう。ときどき、役に立ちます。

左で回っている青い箱にはテクスチャマッピングがしてあります。これはプログラムと同じフォルダにある“tex.bmp”を読み込んで箱に貼り付けて表示しているものです。

画像の読み込み部分は以下の通り。

HBITMAP         CSk00View::LoadTexture( const char* fn )
{
    //      指定されたファイルをテクスチャとして読み込む

    //      とりあえず画像を読み込み

    HBITMAP bmp = skLoadImage( fn );
    if( bmp == NULL )
        return  NULL;

    BITMAP  inf;
    GetObject( bmp,sizeof(BITMAP),&inf );

    //      今回は24ビットカラーのデータのみを採用

    if( inf.bmBitsPixel == 24 )
        return  bmp;

    //      テクスチャに使えないので破棄

    DeleteObject( bmp );

    return  NULL;
}

まず、ファイルから画像データをHBITMAPに読み込んでいます。ちなみに、skLoadImage()は内部的にはLoadImage()を読んでるだけです。昔に比べるとbmpファイルの読み込みも簡単になりましたねぇ・・・

そのあと、念のために画像データの情報を調べています。本当は幅と高さが2の累乗になっているかどうかも調べないといけないんですが、ここでは手抜きをしています。

データが用意できたのでテクスチャマッピング関係の設定を行います。これは、CSk00View::GLSetState()でOpenGL関係のその他の設定と一緒にまとめて行っています。

    //      テクスチャの設定

    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S,      GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T,      GL_REPEAT );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,  GL_LINEAR );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,  GL_LINEAR );
    glTexEnvf( GL_TEXTURE_ENV,      GL_TEXTURE_ENV_MODE,    GL_MODULATE );

GL_TEXTURE_MIN_FILTER以外はデフォルトで良さそうな気がしないでもないですけど、念のために関係のありそうなものはすべて変更しています。

ここまで準備が整ったら、あとは実際にテクスチャを貼るだけです。HBITMAPのデータをテクスチャとして転送するための処理はCSk00View::SetTexture()にまとめてあります。

void            CSk00View::SetTexture( HBITMAP bmp )
{
    //      指定されたビットマップをテクスチャとして転送

    if( bmp != NULL ) {

        //      データを転送

        BITMAP  inf;
        GetObject( bmp,sizeof(BITMAP),&inf );

        glTexImage2D( GL_TEXTURE_2D,0,3,
                      inf.bmWidth,inf.bmHeight,0,
                      GL_BGR_EXT,GL_UNSIGNED_BYTE,inf.bmBits );

        //      テクスチャマッピングを有効にする

        glEnable( GL_TEXTURE_2D );

    } else {

        //      テクスチャマッピングを無効にする

        glDisable( GL_TEXTURE_2D );
    }
}

有効なハンドルの場合、画像データをglTexImage2D()で転送したあと、GL_TEXTURE_2Dを有効にしています。ハンドルとしてNULLが渡された場合はテクスチャマッピングをはずすため、GL_TEXTURE_2Dを無効にしています。

ポイントとしては、HBITMAPのデータを直接貼り付けるためにGL_BGR_EXTを使っているところぐらいでしょうか。手抜きといえなくもないです。


[skLib] Presented by See.Ku [2005/1/12]