C++/CLIでOpenGL描画する方法
C++/CLIのフォーム画面にOpenGLでレンダリングしたグラフィックを表示する方法をメモ書きします.なお,このTipsでは前回の内容をそのまま使っています.
C++/CLIでOpenGLを描画する準備
1. OpenGLSimpleAdapter.hをインクルードする
nursの日記様が作成した便利なヘッダファイル,OpenGLSimpleAdapter.hを使う.ちなみに元ソースのstatic PIXELFORMATDESCRIPTOR pfdの引数の詳細のみここに載せておく.
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), // size of this pfd
1, // version number
PFD_DRAW_TO_WINDOW | // support window
PFD_SUPPORT_OPENGL | // support OpenGL
PFD_DOUBLEBUFFER, // double buffered
PFD_TYPE_RGBA, // RGBA type
24, // 24-bit color depth
0, 0, 0, 0, 0, 0, // color bits ignored
0, // no alpha buffer
0, // shift bit ignored
0, // no accumulation buffer
0, 0, 0, 0, // accum bits ignored
32, // 32-bit z-buffer
0, // no stencil buffer
0, // no auxiliary buffer
PFD_MAIN_PLANE, // main layer
0, // reserved
0, 0, 0 // layer masks ignored
};
2. MyFormのデザイナにpanelを追加する
OpenGLSimpleAdapter.hはpanel要素にOpenGLの描画バッファを出力するようにしてある(pictureboxではないので注意).ツールボックス→コンテナー→Panel をフォームにドラッグして配置する.

3. 追加したPanelにイベントを追加する
追加したPanelをクリックしてアクティブになっている状態で,プロパティ内のイベントPanelをダブルクリックする.この時MyForm.h内にイベントハンドラpanel1_Paint()が自動で追加される.

MyForm.h内に次のインライン関数が追加される.
private: System::Void panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e){}
MyForm.hは多重インクルード禁止命令#pragma onceが書かれているのでここにコードを書き進めても良いのだが,自分の流儀だとMyForm.cppに分けたい.そこでMyForm.hのこいつを,
private: System::Void panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e);
と宣言に書き直し,MyForm.cppに
#include "MyForm.h" using namespace testWindowsUI; // <-- ここにはプロジェクト名を入れる System::Void MyForm::panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) { // ここに処理コードを書く }
と書いてやる.つまり,private:を消去し,関数名の前にMyForm::(WindowsUIの名前)を加えて,{}を入れてそこにコードを書くという流れ.今後関数を追加するたびにこの様にMyForm.hからMyForm.cppに移す必要がある(プロジェクト名のnamespaceは最初のみ追加する).
これと同様に,フォーム画面全体をアクティブにした状態で,プロパティからMyForm::MyForm_Load()関数も追加してやる.
4. MyForm.h内に次のコードを追加する
#pragma once #include "OpenGLSimpleAdapter.h" // ⇐ ここ! namespace testWindowsUI { //----------中略------------- #pragma endregion public: OpenGLSimpleAdapter^ GLAdapter; // ⇐ ここ! private: System::Void panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e); private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e); }; }
ついでにMyForm.cpp内もコードを追加.
System::Void MyForm::MyForm_Load(System::Object^ sender, System::EventArgs^ e)
{
GLAdapter = gcnew OpenGLSimpleAdapter(GetDC((HWND)panel1->Handle.ToPointer()));
}
System::Void MyForm::panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e)
{
GLAdapter->BeginRender();
{
}
GLAdapter->EndRender();
}
これでやっとOpenGLが描けるようになった.BeginRender()とEndRender()の間の{}内に,自由にOpenGLのコードを書くことができる(実は{}はいらないんだけど可読性がよくなるのでつけてる).ちなみにEndRender()はglutSwapBuffers()と同じ機能を有する.例えば次のように直線を書いてみる.
GLAdapter->BeginRender();
{
glClearColor(0, 0, 0, 0);
glClear(GL_DEPTH_BUFFER_BIT | GL_COLOR_BUFFER_BIT);
glOrtho(0, panel1->Width, panel1->Height, 0, -1, 1);
glColor4f(1.0f, 1.0f, 1.0f, 1.0f);
glLineWidth(1.0);
glBegin(GL_LINES);
glVertex2i(20, 40);
glVertex2i(200, 180);
glEnd();
}
GLAdapter->EndRender();
描画結果は次の通り.

UIから動的にOpenGLをいじってみる
スクロールバーを使ってアニメーションを作ってみる.
1. スクロールバー,ラベルを追加

2. ValueChangedイベントハンドラを追加
いつもの様にhScrollBar1_ValueChanged()をMyForm.hからMyForm.cppに移す.

3. MyFormクラスの中にpanelRefresh()関数を追加
この関数にはGLUTでいうglutDisplayFunc()に登録すべき描画コールバック関数の役割を持たせる.そして次の様にコードを書く.
MyForm.h
#pragma once //---------中略------------ #pragma endregion public: OpenGLSimpleAdapter^ GLAdapter; System::Void panelRefresh(void); // ⇐ ここ private: System::Void panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e); private: System::Void MyForm_Load(System::Object^ sender, System::EventArgs^ e); private: System::Void hScrollBar1_ValueChanged(System::Object^ sender, System::EventArgs^ e); }; }
MyForm.cpp
#include "MyForm.h" #include <math.h> using namespace testWindowsUI; static double th = 0.0; System::Void MyForm::MyForm_Load(System::Object^ sender, System::EventArgs^ e) { GLAdapter = gcnew OpenGLSimpleAdapter(GetDC((HWND)panel1->Handle.ToPointer())); } System::Void MyForm::panel1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) { panelRefresh(); } System::Void MyForm::hScrollBar1_ValueChanged(System::Object^ sender, System::EventArgs^ e) { th = (double)hScrollBar1->Value / (double)hScrollBar1->Maximum * 8.0 * atan(1.0); label1->Text = th.ToString("F2"); panelRefresh(); } System::Void MyForm::panelRefresh(void) { GLAdapter->BeginRender(); { // Initialize glClearColor(0, 0, 0, 0); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-1, 1, -1, 1, -1, 1); // Drawing glClear(GL_COLOR_BUFFER_BIT); glColor4f(1.0f, 1.0f, 1.0f, 1.0f); glLineWidth(1.0); glBegin(GL_LINES); glVertex2d(0, 0); glVertex2d(cos(th), sin(th)); glEnd(); } GLAdapter->EndRender(); }
実行結果

スクロールバーを動かすとリアルタイムに直線の傾きが変わる.