2014年6月27日金曜日
2014年6月26日木曜日
2014年6月22日日曜日
2014年6月19日木曜日
2014年6月18日水曜日
2014年6月17日火曜日
2014年6月16日月曜日
2014年6月14日土曜日
C++/CLI フォームアプリとOpenGLの連携(フォームへのレンダリングとGUI操作)
【はじめに】
前エントリの知識をベースに、フォームアプリとOpenGLを連携させます。
完成イメージ図です。
初期画面は何も出ません。Startを押すと画像が表れ図形が回転します。
Stopを押すと図形がとまります。
これを作るために必要な項目を列挙すると次の通りです。
1. ピクチャコントロールを追加する。
まず、前エントリのようにフォームアプリの新規作成まで行います。フォームデザイナにピクチャコントロールとボタンを追加しておきましょう。
ペイント用のイベントハンドラを追加しておきます。プロパティでイベントハンドラ一覧を表示します。雷のようなマークを選択します。
下のほうにPaintがあるので、これをダブルクリックします。
ソースコードに自動でイベントハンドラが追加されます。
ひとまず、こんなところで良しとします。
2. ピクチャコントロールにOpenGLの描画ができるようにする。
このテーマの肝です。glu以外使用しないでやりたいと思います。今までGLUTにお任せしておけば勝手にやってくれいた部分ですね。
次のサイトを大いに参考にさせていただきました。ありがとうございました。VC++の.NET Frameworkアプリケーションの子ウインドウでOpenGL
このサイトのソースコードを解釈する形で話を進めていきます。
大事なのは、attach_GL()関数です。ここでピクチャボックスのハンドルとGLレンダリングコンテキストを関連付けます。
その部分を抜粋してみます。
ここまで分かればあとは普通のOpenGLの描画を設定してしまえばOKです。
3. OpenGLの設定と描画関数を作成する。
先ほどのVC++の.NET Frameworkアプリケーションの子ウインドウでOpenGLを参考にさせていただき、OpenGL描画用のクラスを作りました。
せっかくなので描画する図形だけ少し変えました。gluCylinder()を使いました。次のサイトを参考にしました。GLUTによる「手抜き」OpenGL入門
一応ソースを公開します。後のイベントハンドラへの登録を意識したつくりになっています。
COpenGL.h
COpenGL.cpp
4. Timerコントロールを追加する。
コントロールからTimerを選択して、デザイナにドラッグアンドドロップします。プロパティを開き、「動作→Enable」をTrueにします。
同じくプロパティからイベント一覧を表示し、Tickをダブルクリックします。Intervalごとに呼ばれるイベントハンドラが定義されます。ここにOPenGLの描画を行えばアニメーションになります。
最終的にはOpenGLとの連携にもって行きたいです。
5. Buttonコントロールで挙動を設定(完成)
前エントリ同様に、Buttonのイベントハンドラを追加します。二つのボタンで動画フラグの入り切りを切り替えます。
これで完成です。Form側のソースコードを公開し、終了です。
前エントリの知識をベースに、フォームアプリとOpenGLを連携させます。
完成イメージ図です。
初期画面は何も出ません。Startを押すと画像が表れ図形が回転します。
Stopを押すと図形がとまります。
これを作るために必要な項目を列挙すると次の通りです。
- ピクチャコントロールを追加する。 これがないと話が始まりません。
- ピクチャコントロールにOpenGLの描画ができるようにする。 OpenGLのレンダリングコンテキストをピクチャコントロールに関連付けます。
- OpenGLの設定と描画関数を作成する。 普通のOpenGLの処理です。
- Timerコントロールを追加する。 動画を描画できるようにします。
- Buttonコントロールで挙動を設定(完成)。 ボタンで動画のフラグを制御します。
1. ピクチャコントロールを追加する。
まず、前エントリのようにフォームアプリの新規作成まで行います。フォームデザイナにピクチャコントロールとボタンを追加しておきましょう。
ペイント用のイベントハンドラを追加しておきます。プロパティでイベントハンドラ一覧を表示します。雷のようなマークを選択します。
下のほうにPaintがあるので、これをダブルクリックします。
ソースコードに自動でイベントハンドラが追加されます。
ひとまず、こんなところで良しとします。
2. ピクチャコントロールにOpenGLの描画ができるようにする。
このテーマの肝です。glu以外使用しないでやりたいと思います。今までGLUTにお任せしておけば勝手にやってくれいた部分ですね。
次のサイトを大いに参考にさせていただきました。ありがとうございました。VC++の.NET Frameworkアプリケーションの子ウインドウでOpenGL
このサイトのソースコードを解釈する形で話を進めていきます。
大事なのは、attach_GL()関数です。ここでピクチャボックスのハンドルとGLレンダリングコンテキストを関連付けます。
その部分を抜粋してみます。
// a. ピクチャボックスコントロールからハンドルを取得 if (!(hWnd=(HWND)(pbox->Handle).ToInt32())) { MessageBox(NULL, (LPCTSTR)("COpenGL::attach_GL called with 0 (NULL)"), NULL, MB_OK); return false; } // b. ハンドルからデバイスコンテキストを取得 if (!(hdc=GetDC(hWnd))) { MessageBox(NULL, (LPCTSTR)("COpenGL::attach_GL GetDC(hWnd)"), NULL, MB_OK); return false; } wglMakeCurrent(NULL, NULL); //おまじない // c. ピクセルフォーマットの設定 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 }; int pixFormat; // デバイスコンテキストにマッチするピクセルフォーマットを取得 if((pixFormat = ChoosePixelFormat(this->hDC, &pfd)) == NULL) { MessageBox(NULL, (LPCTSTR)("Error: ChoosePixelFormat() in COpenGL::setPixelFomat()"), NULL, MB_OK); return false; } // デバイスコンテキストにピクセルフォーマットを設定 if((SetPixelFormat(this->hDC, pixFormat, &pfd)) == NULL) { MessageBox(NULL, (LPCTSTR)("Error: SetPixelFormat() in COpenGL::setPixelFomat()"), NULL, MB_OK); return false; } // d. デバイスコンテキストからOpenGLレンダリングコンテキストを取得 if (!(hglrc = wglCreateContext(hdc))) MessageBox(NULL, (LPCTSTR)("COpenGL::attach_GL wglCreateContext(hdc)"), NULL, MB_OK) return false; } // e. 取得したOpenGLレンダリングコンテキストをカレントに設定 if(!wglMakeCurrent(hdc, hglrc)) { MessageBox(NULL, (LPCTSTR)("COpenGL::attach_GL wglMakeCurrent(hdc, hglrc)"), NULL, MB_OK); return false; } |
- ピクチャボックスコントロールからハンドルを取得 .Netのピクチャコントロールから、Windowsのウィンドウハンドルを取得します。ハンドルとは、そのウィンドに一意に対応付くIDみたいなものです。これをしないと次のデバイスコンテキストを取得できません。
- ハンドルからデバイスコンテキストを取得 先ほど取得したハンドルからデバイスコンテキストを取得します。デバイスコンテキストとは、描画を行うためのキャンパスと描画材料のセットです。こちらのサイトを読ませていただきました。Kab Studio - デバイスコンテキストとハンドル。
- ピクセルフォーマットの設定 OS側の描画パラメータを、OpenGLに合わせて設定してあげるOSとOpenGLの橋渡し的な役割のよう。色空間の定義や、色バッファの分解能などを設定しておきます。こちらのサイトを参考にさせていただきました。sonson@Picture&Software - [OpenGL] ピクセルバッファ
- デバイスコンテキストからOpenGLレンダリングコンテキストを取得 OSのデバイスコンテキストを、OplenGL用のデバイスコンテキストに切り替えます。
- 取得したOpenGLレンダリングコンテキストをカレントに設定 カレントコンテキストを設定します。これは複数のウィンドウに描画をしたい場合などに役に立ちます。
水彩画を描きたいと思ったときに、油絵用の画材セットでは書けません。描画するキャンパスに適した画材をセット用意する必要があります。
ここまで分かればあとは普通のOpenGLの描画を設定してしまえばOKです。
3. OpenGLの設定と描画関数を作成する。
先ほどのVC++の.NET Frameworkアプリケーションの子ウインドウでOpenGLを参考にさせていただき、OpenGL描画用のクラスを作りました。
せっかくなので描画する図形だけ少し変えました。gluCylinder()を使いました。次のサイトを参考にしました。GLUTによる「手抜き」OpenGL入門
一応ソースを公開します。後のイベントハンドラへの登録を意識したつくりになっています。
COpenGL.h
#pragma once #include <Windows.h> #include <GL/GLU.h> #include <math.h> #pragma comment (lib, "OpenGL32.lib") // wglAPIで必要 #pragma comment (lib, "GLU32.lib") #pragma comment (lib, "user32.lib") // win32APIで必要 #pragma comment (lib, "gdi32.lib") // setPixelFormat等で必要 ref class COpenGL { public: COpenGL(void); virtual ~COpenGL(void); bool attachGL(System::Windows::Forms::PictureBox^ inPicBox); void detachGL(void); void resizeScene(void); void paintWindow(void); private: void setPerspectivePrm(const int cInWinWIdth, const int cInWinHeight, const int cInAngle, const int cInZNear, const int cInZFar); void callGluPerspecrive(void); bool setPixelFomat(void); void renderScene(void); private: HWND hWnd; HDC hDC; HGLRC hGLRC; int winWidth; int winHeight; int angle; int zNear; int zfar; int tick; bool tickEnagle; }; |
COpenGL.cpp
#include "COpenGL.h" static const int scAngle = 40; static const int scZNear = 100; static const int scZFar = 1300; COpenGL::COpenGL( void ) { this->tick = 0; this->tickEnagle = false; } COpenGL::~COpenGL( void ) { } bool COpenGL::attachGL( System::Windows::Forms::PictureBox^ inPicBox ) { bool rc = false; do { // ピクチャボックスコントロールからハンドルを取得 if(!(this->hWnd = (HWND)(inPicBox->Handle).ToInt32())) { MessageBox(NULL, (LPCTSTR)("Error: (inPicBox->Handle).ToInt32() in COpenGL::attatchGL"), NULL, MB_OK); rc = true; break; } // ハンドルからデバイスコンテキストを取得 if(!(this->hDC = GetDC(this->hWnd))) { essageBox(NULL, (LPCTSTR)("Error: GetDC() in COpenGL::attatchGL"), NULL, MB_OK); rc = true; break; } // おまじない wglMakeCurrent(NULL, NULL); // ピクセルフォーマットの設定 if(this->setPixelFomat()) { MessageBox(NULL, (LPCTSTR)("Error: COpenGL::setPixelFomat() in COpenGL::attatchGL"), NULL, MB_OK); rc = true; break; } // デバイスコンテキストからOpenGLレンダリングコンテキストを取得 if(!(this->hGLRC = wglCreateContext(this->hDC))) { MessageBox(NULL, (LPCTSTR)("Error: wglCreateContext() in COpenGL::attatchGL"), NULL, MB_OK); rc = true; break; } // 取得したOpenGLレンダリングコンテキストをカレントに設定 if(!wglMakeCurrent(this->hDC, this->hGLRC)) { MessageBox(NULL, (LPCTSTR)("Error: wglMakeCurrent() in COpenGL::attatchGL"), NULL, MB_OK); rc = true; break; } } while(false); // 視野パラメータ設定 this->setPerspectivePrm(inPicBox->Width, inPicBox->Height, scAngle, scZNear, scZFar); // 視野設定 this->callGluPerspecrive(); return rc; } void COpenGL::detachGL( void ) { wglMakeCurrent(NULL, NULL) ; if (this->hGLRC) wglDeleteContext(hGLRC); if (hDC) ReleaseDC(hWnd, hDC); } void COpenGL::resizeScene( void ) { this->callGluPerspecrive(); } void COpenGL::paintWindow( void ) { if(this->tickEnagle == true) { glMatrixMode(GL_MODELVIEW); glLoadIdentity(); double leye=700.0; double dip=0.65; double rot=0.5; //dip,rot[rad] double ly,lxz,lx,lz; ly=leye*sin(dip); lxz=leye*cos(dip); lx=lxz*sin(rot); lz=lxz*cos(rot); gluLookAt(lx,ly,lz, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0); glClearColor(0,0,0.5,0); glClear(GL_COLOR_BUFFER_BIT); glClear(GL_DEPTH_BUFFER_BIT); // depth check } renderScene();//render here SwapBuffers(this->hDC); } void COpenGL::setPerspectivePrm( const int cInWinWIdth, const int cInWinHeight, const int cInAngle, const int cInZNear, const int cInZFar ) { this->winWidth = cInWinWIdth; this->winHeight = cInWinHeight; this->angle = cInAngle; this->zNear = cInZNear; this->zfar = cInZFar; } void COpenGL::callGluPerspecrive( void ) { glViewport(0, 0, this->winWidth, this->winHeight); glEnable(GL_DEPTH_TEST); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(this->angle, (GLdouble)this->winWidth/(GLdouble)this->winHeight, this->zNear, this->zfar); } bool COpenGL::setPixelFomat( void ) { bool rc = false; PIXELFORMATDESCRIPTOR pdf = { 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 }; int pixFormat; do { // デバイスコンテキストにマッチするピクセルフォーマットを取得 if((pixFormat = ChoosePixelFormat(this->hDC, &pdf)) == NULL) { MessageBox(NULL, (LPCTSTR)("Error: ChoosePixelFormat() in COpenGL::setPixelFomat()"), NULL, MB_OK); rc = true; break; } // デバイスコンテキストにピクセルフォーマットを設定 if((SetPixelFormat(this->hDC, pixFormat, &pdf)) == NULL) { MessageBox(NULL, (LPCTSTR)("Error: SetPixelFormat() in COpenGL::setPixelFomat()"), NULL, MB_OK); rc = true; break; } } while(false); return rc; } void COpenGL::renderScene( void ) { // quadric object を一つ生成する GLUquadricObj *quad = gluNewQuadric(); // 面の塗り潰しを指定する(線画ではなく陰影をつけた円柱を描く) gluQuadricDrawStyle(quad, GLU_FILL); // スムースシェーディングを行うよう設定する gluQuadricNormals(quad, GLU_SMOOTH); glRotatef((GLfloat)this->tick, 1, 0, 0); this->tick += 5; // 側面を描く(stacks = 1) const GLdouble radius = 100; const GLdouble height = 100; const int sides = 20; glColor3f(1, 0, -1); gluCylinder(quad, radius, radius, height, sides, 1); // height の高さに上面を描く glPushMatrix(); glTranslated(0.0, 0.0, height); glColor3f(1, 1, 0); gluDisk(quad, 0.0, radius, sides, 1); glPopMatrix(); // 図形を裏返して描くように設定する gluQuadricOrientation(quad, GLU_INSIDE); glColor3f(0, 1, 1); // 下面を描く gluDisk(quad, 0.0, radius, sides, 1); // 生成した quadlic object を削除する gluDeleteQuadric(quad); } |
4. Timerコントロールを追加する。
コントロールからTimerを選択して、デザイナにドラッグアンドドロップします。プロパティを開き、「動作→Enable」をTrueにします。
同じくプロパティからイベント一覧を表示し、Tickをダブルクリックします。Intervalごとに呼ばれるイベントハンドラが定義されます。ここにOPenGLの描画を行えばアニメーションになります。
最終的にはOpenGLとの連携にもって行きたいです。
5. Buttonコントロールで挙動を設定(完成)
前エントリ同様に、Buttonのイベントハンドラを追加します。二つのボタンで動画フラグの入り切りを切り替えます。
これで完成です。Form側のソースコードを公開し、終了です。
#pragma once #include "COpenGL.h" namespace GLForm { using namespace System; using namespace System::ComponentModel; using namespace System::Collections; using namespace System::Windows::Forms; using namespace System::Data; using namespace System::Drawing; /// <summary> /// MyForm の概要 /// </summary> public ref class MyForm : public System::Windows::Forms::Form { public: MyForm(void) { InitializeComponent(); // //TODO: ここにコンストラクター コードを追加します // this->glBase = gcnew COpenGL; this->glRunFlg = this->glBase->attachGL(pictureBox1); } protected: /// <summary> /// 使用中のリソースをすべてクリーンアップします。 /// </summary> ~MyForm() { if (glRunFlg) glBase->detachGL(); timer1->Enabled=false; delete glBase; if (components) { delete components; } } private: System::Windows::Forms::Button^ button1; private: System::Windows::Forms::Button^ button2; private: System::Windows::Forms::PictureBox^ pictureBox1; private: System::Windows::Forms::Panel^ panel1; private: System::Windows::Forms::Panel^ panel2; private: System::ComponentModel::IContainer^ components; protected: private: /// <summary> /// 必要なデザイナー変数です。 /// </summary> // 自作クラス private: System::Windows::Forms::Timer^ timer1; COpenGL^ glBase; bool glRunFlg; #pragma region Windows Form Designer generated code /// <summary> /// デザイナー サポートに必要なメソッドです。このメソッドの内容を /// コード エディターで変更しないでください。 /// </summary> void InitializeComponent(void) { this->components = (gcnew System::ComponentModel::Container()); this->button1 = (gcnew System::Windows::Forms::Button()); this->button2 = (gcnew System::Windows::Forms::Button()); this->pictureBox1 = (gcnew System::Windows::Forms::PictureBox()); this->panel1 = (gcnew System::Windows::Forms::Panel()); this->panel2 = (gcnew System::Windows::Forms::Panel()); this->timer1 = (gcnew System::Windows::Forms::Timer(this->components)); (cli::safe_cast<System::ComponentModel::ISupportInitialize^ >(this->pictureBox1))->BeginInit(); this->panel1->SuspendLayout(); this->panel2->SuspendLayout(); this->SuspendLayout(); // // button1 // this->button1->Location = System::Drawing::Point(8, 123); this->button1->Name = L"button1"; this->button1->Size = System::Drawing::Size(77, 22); this->button1->TabIndex = 0; this->button1->Text = L"Start"; this->button1->UseVisualStyleBackColor = true; this->button1->Click += gcnew System::EventHandler(this, &MyForm::button1_Click); // // button2 // this->button2->Location = System::Drawing::Point(7, 160); this->button2->Name = L"button2"; this->button2->Size = System::Drawing::Size(77, 25); this->button2->TabIndex = 1; this->button2->Text = L"Stop"; this->button2->UseVisualStyleBackColor = true; this->button2->Click += gcnew System::EventHandler(this, &MyForm::button2_Click); // // pictureBox1 // this->pictureBox1->Location = System::Drawing::Point(22, 25); this->pictureBox1->Name = L"pictureBox1"; this->pictureBox1->Size = System::Drawing::Size(182, 160); this->pictureBox1->TabIndex = 2; this->pictureBox1->TabStop = false; this->pictureBox1->Paint += gcnew System::Windows::Forms::PaintEventHandler(this, &MyForm::pictureBox1_Paint); // // panel1 // this->panel1->Controls->Add(this->button2); this->panel1->Controls->Add(this->button1); this->panel1->Dock = System::Windows::Forms::DockStyle::Right; this->panel1->Location = System::Drawing::Point(243, 0); this->panel1->Name = L"panel1"; this->panel1->Size = System::Drawing::Size(94, 214); this->panel1->TabIndex = 3; // // panel2 // this->panel2->Controls->Add(this->pictureBox1); this->panel2->Dock = System::Windows::Forms::DockStyle::Fill; this->panel2->Location = System::Drawing::Point(0, 0); this->panel2->Name = L"panel2"; this->panel2->Size = System::Drawing::Size(243, 214); this->panel2->TabIndex = 4; // // timer1 // this->timer1->Enabled = true; this->timer1->Tick += gcnew System::EventHandler(this, &MyForm::timer1_Tick); // // MyForm // this->AutoScaleDimensions = System::Drawing::SizeF(6, 12); this->AutoScaleMode = System::Windows::Forms::AutoScaleMode::Font; this->ClientSize = System::Drawing::Size(337, 214); this->Controls->Add(this->panel2); this->Controls->Add(this->panel1); this->Name = L"MyForm"; this->Text = L"MyForm"; (cli::safe_cast<System::ComponentModel::ISupportInitialize^ >(this->pictureBox1))->EndInit(); this->panel1->ResumeLayout(false); this->panel2->ResumeLayout(false); this->ResumeLayout(false); } #pragma endregion private: System::Void pictureBox1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) { this->glBase->paintWindow(); } private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) { this->glBase->paintWindow(); } private: System::Void button1_Click(System::Object^ sender, System::EventArgs^ e) { this->glBase->setTickEnagle(true); } private: System::Void button2_Click(System::Object^ sender, System::EventArgs^ e) { this->glBase->setTickEnagle(false); } }; } |
2014年6月13日金曜日
2014年6月12日木曜日
2014年6月11日水曜日
2014年6月9日月曜日
登録:
投稿 (Atom)