ゲームフレームワーク的なものを作る。(9)〜OpenGLの初期化〜

今回はOpenGLを使った描画機能を追加していきます。久しぶりのOpenGLですね。

OpenGLの初期化

まず、OpenGLを利用するためのヘッダとライブラリをプロジェクトに追加します。VCをインストールすれば、標準で必要なファイルが含まれるので、特に新しくライブラリなどをインストールする必要はありません。

#include <gl/gl.h>
#pragma comment( lib, "opengl32.lib" )

以下、初期化手順です。詳細はgoogle先生に聞きましょう。「opengl 初期化

  • 1.PIXELFORMATDESCRIPTOR構造体でウィンドウに関するピクセルフォーマット情報を指定。
  • 2.ChoosePixelFormat()を呼び出して、デバイスコンテキストに最も適合するピクセルフォーマットを選択。
  • 3.SetPixelFormat()を呼び出して、2で選んだピクセルフォーマットを実際に設定。
  • 4.wglCreateContext()でOpenGLレンダリングコンテキストを作成。
  • 5.作成したレンダリングコンテキストをカレントコンテキストに設定。
  • 6.プログラム終了時にレンダリングコンテキストをカレントコンテキストから解除して、破棄。
初期化の前に、DeviceContextクラスを作っておく。

win32/wglでは頻繁にデバイスコンテキストを使うことになるので、クラス化しておくと便利です。

/**
 *  ウィンドウのデバイスコンテキストのラッパー
 */
class DeviceContext
    : noncopyable
{
public:
    explicit DeviceContext( HWND hwnd )
        : hdc( ::GetDC( hwnd ) ), hwnd( hwnd )
    {
    }

    ~DeviceContext()
    {
        ::ReleaseDC( hwnd, hdc );
    }

    //HDCへのキャスト
    operator HDC()const{ return hdc; }

private:
    HDC hdc;
    HWND hwnd;
};

Windowクラスにデバイスコンテキストを取得するメソッドを追加。

class Window
{
public:
    /**
     *  DC取得
     */
    DeviceContext GetDeviceContext()const{ return DeviceContext( hwnd ); }

/*略*/
};
あらためて、OpenGLの初期化処理を追加


class OpenGLView
    : public View
{
public:
    /**
     *  コンストラクタ
     */
    OpenGLView( const String& title, const Size& client_size );
    /**
     *  デストラクタ
     */
    ~OpenGLView();

private:
    /**
     *  OpenGLレンダリングコンテキストの生成
     */
    HGLRC CreateRenderingContext( const DeviceContext& dc );

private:
    DeviceContext device_context;
    HGLRC rendering_context;

/*略*/
};
/**
 *  コンストラクタ
 *
 *  @param  title       ウィンドウのタイトル
 *  @param  client_size ウィンドウのクライアントサイズ
 */
OpenGLView::OpenGLView( const String& title, const Size& client_size )
    : View( title, client_size )              //ウィンドウ生成
    , device_context( GetDeviceContext() )    //ウィンドウのDCを取得
    , rendering_context( CreateRenderingContext( device_context ) )  //RC生成
{
    ::wglMakeCurrent( device_context, rendering_context );
}


/**
 *  デストラクタ
 */
OpenGLView::~OpenGLView()
{
    ::wglMakeCurrent( device_context, nullptr );
    ::wglDeleteContext( rendering_context );
}

/**
 *  OpenGLレンダリングコンテキストの生成
 *
 *  @param  dc  デバイスコンテキスト
 */
HGLRC OpenGLView::CreateRenderingContext( const DeviceContext& dc )
{
    //PIXELFORMATDESCRIPTORにピクセルフォーマットを設定し、
    //デバイスコンテキストにもっと適合するピクセルフォーマットを選択、設定後
    //レンダリングコンテキストを生成する
    PIXELFORMATDESCRIPTOR pfd = { sizeof( PIXELFORMATDESCRIPTOR ) };
    pfd.nVersion = 1;
    pfd.dwFlags = PFD_DRAW_TO_WINDOW    // support window 
                | PFD_SUPPORT_OPENGL    // support OpenGL 
                | PFD_DOUBLEBUFFER      // double buffered 
                ;
    pfd.iPixelType = PFD_TYPE_RGBA;
    pfd.cColorBits = 32;
    pfd.cDepthBits = 16;
    pfd.iLayerType = PFD_MAIN_PLANE;
    int format = ::ChoosePixelFormat( dc, &pfd );
    if( format == 0 )
        throw std::runtime_error( "ChoosePixelFormat" );
    if( !::SetPixelFormat( dc, format, &pfd ) )
        throw std::runtime_error( "SetPixelFormat" );

    return ::wglCreateContext( dc );
}

以上で初期化完了です。

画面を指定色で塗りつぶす。

正しく初期化できているか確認するために、とりあえず適当な色で画面を塗りつぶしてみましょう。
OpenGLViewクラスにClear()とUpdate()の実装を追加します。

/**
 *  ViewのOpenGL実装
 */
class OpenGLView
    : public View
{
/*略*/

    //  -------------------------------------------------------------------------------
    //  IGraphics実装
    //  -------------------------------------------------------------------------------
public:
    /**
     *  シーンのクリア
     */
    void Clear();
    /**
     *  シーンのレンダリング開始
     */
    bool BeginScene();
    /**
     *  シーンのレンダリング終了
     */
    void EndScene();
    /**
     *  画面の更新
     */
    void Update();
};
/**
 *  シーンのクリア
 */
void OpenGLView::Clear()
{
    ::glClearColor( 0.1f, 0.1f, 0.1f, 1.0f );  //クリア色を指定
    //::glClearDepth( 1.0f );
    ::glClear( GL_COLOR_BUFFER_BIT /*| GL_DEPTH_BUFFER_BIT*/ );
}

/**
 *  シーンのレンダリング開始
 */
bool OpenGLView::BeginScene()
{
    return true;
}

/**
 *  シーンのレンダリング終了
 */
void OpenGLView::EndScene()
{
}

/**
 *  画面の更新
 */
void OpenGLView::Update()
{
    ::SwapBuffers( device_context );
}

使い方

OpenGLViewのインスタンスを生成して、Gameクラスに渡すだけです。

/**
 *  エントリーポイント
 */
int main()
{
    Application::Initialize();

    try
    {
        //ゲーム用ウィンドウの生成と描画エンジンの初期化
        //Direct3DView view( "hoge", Size( 720, 480 ) );    //ウィンドウタイトルとクライアントサイズを指定
        OpenGLView view( "hoge", Size( 720, 480 ) );        //ウィンドウタイトルとクライアントサイズを指定

        //ゲームを初期化して、ゲームループを開始する
        Game game( &view );
        //ゲームループ
        //  ウィンドウが閉じられるまで1フレームの処理を繰り返す
        while( !view.IsClosed() )
        {
            //1フレームの処理
            game.Run();

            //メッセージキューにある全てのwindowsイベントを処理する
            Application::DoEvents();
        }
    }
    catch( std::exception& e )
    {
        std::cout << e.what() << std::endl;
    }
    catch( ... )
    {
        std::cout << "予期せぬ例外" << std::endl;
    }

    Application::Finalize();
    return 0;
}


ダウンロード

次回予定

OpenGLにおいて、おそらく最初の鬼門となるのが「日本語の描画」。
wglではwglUseFontBitmapsA()、wglUseFontOutlinesA()という2つのAPIが用意されているが、日本語が扱えない。wglUseFontBitmapsW()、wglUseFontOutlinesW()であればUnicodeなので問題ないが、アンチエイリアスに対応していない。
ということで、次回はGetGlyphOutline()とテクスチャを使ってアンチエイリアスに対応した文字列描画を行います。