ゲームフレームワーク的なものを作る。(10)〜OpenGLでテクスチャを使う〜

まずはOpenGLでテクスチャを使用する際の基本から。

R8G8B8A8の32bitテクスチャを作る

GLuint texture;
Size size( w, h );
DWORD pixel[] = { ... };   //適当なピクセルデータを設定しておく
  • glGenTextures()で指定枚数のテクスチャを作成。とりあえず1でよい。
  • glBindTexture()で作成したテクスチャをバインドしてから、フィルタなどのパラメータを設定
    • フィルタの設定は必須。
  • glTexture2D()でピクセルデータをコピー。
  • バインド解除
    glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
    glGenTextures( 1, &texture );
    glBindTexture( GL_TEXTURE_2D, texture );
    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_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
        GL_RGBA, GL_UNSIGNED_BYTE, pixel
        );
    glBindTexture( GL_TEXTURE_2D, 0 );    //バインド解除
  • 使い終わったらglDeleteTextures()で削除
    glDeleteTextures( 1, &texture );

テクスチャの生成は以上で終了です。

スクリーンの設定

OpenGLではデフォルトで画面の中心を原点とした上に向かってY軸の正(-1.0〜1.0)、右に向かってX軸の正(-1.0〜1.0)となるような座標系になっているので、

画面の左上を原点、ウィンドウサイズに合わせた座標系に変更します。

    //ビューポート行列の設定
    glViewport( 0, 0, client_size.width, client_size.height );
    //プロジェクション行列の設定
    glMatrixMode( GL_PROJECTION );
    glLoadIdentity();
    gluOrtho2D(
        0.0, static_cast< GLdouble >( client_size.width ),  //left, right
        static_cast< GLdouble >( client_size.height ), 0.0  //bottom, top
        );
    //ビュー行列の設定
    glMatrixMode( GL_MODELVIEW );
    glLoadIdentity();

ポリゴンにテクスチャを貼る

  • glEnable()でテクスチャマップを有効化
  • glBindTexture()でポリゴンに貼るテクスチャを設定
  • glBegin()〜glEnd()でポリゴンの各頂点を設定
    • glTexCoord(s,t)でテクスチャ座標(0.0〜1.0)の設定
    • glVertex2f(x,y)で頂点座標の設定


    glEnable( GL_TEXTURE_2D );                          //テクスチャマップを有効にする
    glBindTexture( GL_TEXTURE_2D, texture );            //生成したテクスチャをバインド
    glBegin( GL_QUADS );                                //四角形ポリゴン
        //左上から時計回りの四角形
        glTexCoord2f( 0.0f, 0.0f ); glVertex2f( 0.0f, 0.0f );   //左上
        glTexCoord2f( 1.0f, 0.0f ); glVertex2f( 128.0f, 0.0f );  //右上
        glTexCoord2f( 1.0f, 1.0f ); glVertex2f( 128.0f, 128.0f ); //右下
        glTexCoord2f( 0.0f, 1.0f ); glVertex2f( 0.0f, 128.0f );  //左下
        glEnd();
    glBindTexture( GL_TEXTURE_2D, 0 );                  //テクスチャ解除
    glDisable( GL_TEXTURE_2D );                         //テクスチャマップ無効

ちなみにテクスチャは以下のように作っています。

    DWORD pixel[ 64 * 64 ];
    int index = 0;
    for( int y = 0; y < 64; y ++ )
    {
        for( int x = 0; x < 64; x ++ )
        {
            Color color;
            color.r = x / 63.0f;
            color.g = 0.0f;
            color.b = y / 63.0f;
            color.a = 1.0f;
            pixel[ index ] = color.to_R8G8B8A8();
            index ++;
        }
    }

ピクセルフォーマット

VC++2010ExpressEditionで32ビットテクスチャを作る場合、デフォルトで上位バイトからR8G8B8A8のフォーマットしか使えません(OpenGL 1.1)。Direct3Dと同じA8R8G8B8を使いたければ、こちらから「glext.h」「glxext.h」「wglext.h」をダウンロード、インストールして(VCのパスを通す)、インクルードする必要があります。(OpenGL 1.2以上)

format type
GL_BGRA GL_UNSIGNED_INT_8_8_8_8 0xbbggrraa
GL_RGBA GL_UNSIGNED_INT_8_8_8_8 0xrrggbbaa
GL_BGRA GL_UNSIGNED_INT_8_8_8_8_REV 0xaabbggrr
GL_RGBA GL_UNSIGNED_INT_8_8_8_8_REV 0xaarrggbb
/**
 *  ピクセルフォーマット
 **/
struct PixelFormat
{
    enum Type
    {
        Unknown,
        b8g8r8a8,   //0xbbggrraa
        r8g8b8a8,   //0xrrggbbaa
        a8r8g8b8,   //0xaarrggbb
        a8b8g8r8,   //0xaabbggrr
    };
};
    int format = 0, type = 0;
    switch( pixel_format )
    {
    case PixelFormat::b8g8r8a8: format = GL_BGRA, type = GL_UNSIGNED_INT_8_8_8_8; break;
    case PixelFormat::r8g8b8a8: format = GL_RGBA, type = GL_UNSIGNED_INT_8_8_8_8; break;
    case PixelFormat::a8r8g8b8: format = GL_BGRA, type = GL_UNSIGNED_INT_8_8_8_8_REV; break;
    case PixelFormat::a8b8g8r8: format = GL_RGBA, type = GL_UNSIGNED_INT_8_8_8_8_REV; break;
    default: format = GL_BGRA, type = GL_UNSIGNED_INT_8_8_8_8; break;
    }

    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
        format, type, &pixel[ 0 ]
        );

テクスチャクラスを作る

テクスチャインターフェイスOpenGLによる実装クラスを作ります。

テクスチャインターフェイス

デバッグ用にテクスチャの情報を出力するために文字列化インターフェイスを実装しておくと便利かもしれません。

/**
 *  テクスチャインターフェイス
 */
class ITexture
    : public IStringizable      //ストリームへ出力可能
    , noncopyable               //インスタンスのコピー禁止
{
public:
    /**
     *  デストラクタ
     */
    virtual ~ITexture(){}

public:
    /**
     *  サイズを取得
     */
    virtual Size GetSize()const = 0;
    /**
     *  ピクセルフォーマットを取得
     */
    virtual PixelFormat::Type GetPixelFormat()const = 0;
};

OpenGLテクスチャクラス

/**
 *  テクスチャクラス(OpenGLによる実装)
 */
class OpenGLTexture
    : public ITexture
{
    //  -------------------------------------------------------------------------------
    //  生成と破棄
    //  -------------------------------------------------------------------------------
public:
    /**
     *  コンストラクタ
     */
    OpenGLTexture( const std::vector< DWORD >& pixel, const Size& size, PixelFormat::Type pixel_format );
    /**
     *  デストラクタ
     */
    ~OpenGLTexture();


    //  -------------------------------------------------------------------------------
    //  ITexture実装
    //  -------------------------------------------------------------------------------
public:
    /**
     *  テクスチャサイズ取得
     */
    Size GetSize()const{ return size; }

    /**
     *  ピクセルフォーマット取得
     */
    PixelFormat::Type GetPixelFormat()const{ return pixel_format; }

    //  -------------------------------------------------------------------------------
    //  IStringizable実装
    //  -------------------------------------------------------------------------------
private:
    /**
     *  文字列への変換
     */
    String to_string()const;


    //  -------------------------------------------------------------------------------
    //  基本機能
    //  -------------------------------------------------------------------------------
public:
    /**
     *  インスタンスを取得
     */
    GLuint GetInstance()const{ return instance; }

private:
    Size size;                      ///<    テクスチャサイズ
    PixelFormat::Type pixel_format; ///<    ピクセルフォーマット
    GLuint instance;                ///<    OpenGLテクスチャの実体


};
/**
 *  コンストラクタ
 *
 *  @param  pixel           ピクセル配列
 *  @param  size            テクスチャサイズ
 *  @param  pixel_format    ピクセルフォーマット
 */
OpenGLTexture::OpenGLTexture( const std::vector< DWORD >& pixel, const Size& size, PixelFormat::Type pixel_format )
    : size( size )
    , instance( 0 )
    , pixel_format( pixel_format )
{
    //ピクセルフォーマットの選択
    int format = 0, type = 0;
    switch( pixel_format )
    {
    case PixelFormat::b8g8r8a8: format = GL_BGRA, type = GL_UNSIGNED_INT_8_8_8_8; break;
    case PixelFormat::r8g8b8a8: format = GL_RGBA, type = GL_UNSIGNED_INT_8_8_8_8; break;
    case PixelFormat::a8r8g8b8: format = GL_BGRA, type = GL_UNSIGNED_INT_8_8_8_8_REV; break;
    case PixelFormat::a8b8g8r8: format = GL_RGBA, type = GL_UNSIGNED_INT_8_8_8_8_REV; break;
    default: format = GL_BGRA, type = GL_UNSIGNED_INT_8_8_8_8; break;
    }

    //テクスチャ生成
    glPixelStorei( GL_UNPACK_ALIGNMENT, 4 );
    glGenTextures( 1, &instance );
    glBindTexture( GL_TEXTURE_2D, instance );
    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_MAG_FILTER, GL_NEAREST );
    glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST );
    glTexImage2D( GL_TEXTURE_2D, 0, GL_RGBA, size.width, size.height, 0,
        format, type, &pixel[ 0 ]
        );
    glBindTexture( GL_TEXTURE_2D, 0 );
}

/**
 *  デストラクタ
 */
OpenGLTexture::~OpenGLTexture()
{
    glDeleteTextures( 1, &instance );
}

/**
 *  文字列への変換。サイズを出力
 */
String OpenGLTexture::to_string()const
{
    std::stringstream ss;
    ss << size;
    return ss.str();
}

使い方

    //適当なテクスチャを作成
    std::vector< DWORD > pixel( 64 * 64 );
    int index = 0;
    for( int y = 0; y < 64; y ++ )
    {
        for( int x = 0; x < 64; x ++ )
        {
            Color color;
            color.r = ( x & 63 ) / 63.0f;
            color.g = 0.0f;
            color.b = ( y & 63 ) / 63.0f;
            color.a = 1.0f;
            pixel[ index ] = color.to_a8r8g8b8();
            index ++;
        }
    }
    OpenGLTexture texture( pixel, Size( 64, 64 ), PixelFormat::a8r8g8b8 );

    //適当なポリゴンを描画
    glEnable( GL_TEXTURE_2D );                              //テクスチャマップを有効にする
    glBindTexture( GL_TEXTURE_2D, texture.GetInstance() );  //生成したテクスチャをバインド
    glPushMatrix();
        glBegin( GL_QUADS );                                //四角形ポリゴン
            //左上から時計回りの四角形
            glTexCoord2f( 0.0f, 0.0f ); glVertex2f( 0.0f, 0.0f );       //左上
            glTexCoord2f( 1.0f, 0.0f ); glVertex2f( 128.0f, 0.0f );     //右上
            glTexCoord2f( 1.0f, 1.0f ); glVertex2f( 128.0f, 128.0f );   //右下
            glTexCoord2f( 0.0f, 1.0f ); glVertex2f( 0.0f, 128.0f );     //左下
        glEnd();
    glPopMatrix();
    glBindTexture( GL_TEXTURE_2D, 0 );                      //テクスチャ解除
    glDisable( GL_TEXTURE_2D );                             //テクスチャマップ無効

ダウンロード