2009年12月31日 星期四

.NET - 將OpenGL加入Windows Forms(C++/CLI)

這裡要介紹如何把OpenGL加入到Windows Forms裡面,
利用OpenGL來做3D的繪圖工作。

  1. 需要先確定已經將OpenGL的*.h, *.LIB等檔案已經裝到電腦裡,
    並且已經可以使用了!
  2. 開啟一個CLR專案的Windows Forms,專案名稱取做OpenGLForm
  3. OpenGLForm屬性頁,設定opengl32.lib, glut32.lib, glu32.lib的連結。


  4. 將windows.h, gl.h, glu.h加入header檔

    #include < windows.h>
    #include < gl.h>
    #include < glu.h>
    
  5. 利用DLLIMPORT將opengl32.dll提供的wglSwapBuffers可以使用

    [DllImport("opengl32.dll")]
     extern HDC wglSwapBuffers( HDC hdc );
    



  6. 增加下列幾個變數

    static HWND m_hWnd;
    static HDC  m_hDC;
    static HGLRC m_hRC;
    float m_rAngle;
    

  7. 替Windows Form增加一個panel,叫做OpenGLPanel。以及一個timer叫做timer1


  8. 將timer1增加一個Tick事件的處理函式

    System::Void timer1_Tick(System::Object^  sender, System::EventArgs^  e)
    {
         // Rendering ------------------------------------------------
         wglMakeCurrent(m_hDC, m_hRC);
         glClearColor( 0.0f, 0.0f, 0.0f, 1.0f );
         glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT) ;
         glMatrixMode(GL_PROJECTION);
         glLoadIdentity();
    
         glMatrixMode(GL_MODELVIEW);
         glLoadIdentity();
    
         m_rAngle = m_rAngle + 1;
         if(360 < m_rAngle) m_rAngle -= 360;
         glRotatef( m_rAngle, 0.0f, 0.0f, 1.0f );
         glBegin(GL_QUADS);
         glColor3f(1.0, 0.0, 0.0);
         glVertex3f(-0.5,-0.5,0);
         glVertex3f( 0.5,-0.5,0);
         glVertex3f( 0.5, 0.5,0);
         glVertex3f(-0.5, 0.5,0);
         glEnd();
    
         glFlush();
         SwapBuffers( m_hDC );  
    }
    

  9. 加入OpenGL Init的設定,並在Form的建構子呼叫它。使得程式啟動時,就開始繪圖。

    Form1(void)
    {
          InitializeComponent();
          //
          //TODO: 在此加入建構函式程式碼
          //
          OpenGLInit();
    }
    
    void OpenGLInit(void)
    {
          // Get Handle
          m_hWnd = (HWND)this->OpenGLPanel->Handle.ToInt32();
          m_hDC = GetDC( m_hWnd );
          //WGL::wglSwapBuffers((unsigned int)m_hDC);
          wglSwapBuffers(m_hDC);
    
          // Set pixel format
          PIXELFORMATDESCRIPTOR pfd;
          //WGL::ZeroPixelDescriptor(pfd);
          pfd.nVersion   = 1; 
          pfd.dwFlags    = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER; 
          pfd.iPixelType = (byte)(PFD_TYPE_RGBA);
          pfd.cColorBits = 32;
          pfd.cDepthBits = 32;
          pfd.iLayerType = (byte)(PFD_MAIN_PLANE);
    
          int nFormat = ChoosePixelFormat(m_hDC, &pfd);
          SetPixelFormat(m_hDC, nFormat, &pfd);
    
          // Create OpenGL Rendering Context
          m_hRC = (wglCreateContext(m_hDC));
          if (m_hRC == 0) 
           MessageBox::Show("wglCreateContext Error");
          // Assign OpenGL Rendering Context
          if (wglMakeCurrent(m_hDC, m_hRC ) == false) 
           MessageBox::Show("wglMakeCurrent Error");
    
          glClearColor(0.0f,0.0f,0.0f,0.0f);
          glEnable(GL_DEPTH_TEST);
          glDepthFunc(GL_LEQUAL);
          //GL.glEnable(GL.GL_CULL_FACE);
          glCullFace(GL_BACK);
    
          // ------------------------------------------------------------------
          timer1->Interval = 10;
          timer1->Enabled = true;
    }
    

  10. 執行程式應該可以看到如下圖,有一個紅色方塊在視窗內旋轉。
    之後,就是利用timer來進行繪圖工作,或是可以在timer內加入一個renderscene()的函式來進行繪圖。


附件範例程式:

5 則留言:

  1. 太感謝了!!
    我弄了一整天一直找一直試都沒辦法解決Get Handle的問題!
    明明在bcb一行GetDC(OpenGLPanel->Handle);就搞定了,
    轉到VC++一開始說資料型態不同不能編譯(IntPtr跟HWND),
    找了半天好不容易改成
    GetDC((HWND)(this->OpenGLPanel->Handle).ToInt32());
    可以編譯了,卻是Get不到任何東西...

    雖然還是沒辦法確定原本方式哪邊錯了~"~
    但是用Sammy大的方法總算是可以使用了Q_Q
    真的真的太感謝了!!!

    回覆刪除
  2. 請問 如果是要開始obj的檔案
    是否可以這樣使用?

    是否可以設置一個以上的panel來放置opengl?
    可以分配位置嗎?

    回覆刪除
  3. 感謝分享,我想請叫一下
    如何將這支程式寫在CPP而不是.H檔呢?(不好意思我是C++新手)。

    回覆刪除
  4. good
    有考慮發到codeproject嗎

    回覆刪除