update: 2012/10/11
reference:
1. 將OpenGL加入Windows Forms(C++/CLI)
2. (VS2005,OpenGL)WinForm建立OpenGL视场
A. 說明:
本篇文章參考上面的二篇文章, 擷取不同的特點:
1. 將 OpenGL 的繪圖作業, 獨立成一個類別來處理.
2. 為 Form 加入以下的元件:
a. Panel: 作為 Device Context, 之後提供給 OpenGL 來建立 Render Context.
b. Timer: 計時器, 藉著啟用 Tick 事件, 定時作繪製的動作, 來達到動畫的效果.
3. 為 Form 加入以下的事件:
a. Paint: 首次的繪製. (如果不需要動畫的話, 可將 Timer 停用, 只畫此一次)
b. ClientSizeChanged: 當 Form 的大小改變時, 需要再重新繪製.
-------------------------------------------------------------------------------------
B. 前置作業:
1. 安裝 GLUT 與 GLEW: 參考這篇的 C 與 D.
-------------------------------------------------------------------------------------
C. 新增專案:
1. Visual Studio 2010 > 檔案 > 新增 > 專案
2. Visual C++ > CLR > Windows Form 應用程式
名稱: GLForm_Hello
位置: C:\Lanli\projects\WindowsForm_My\
為方案建立目錄: 取消勾選
> 確定
3. 新增給 OpenGL 使用的類別檔案:
點選 "GLForm_Hello" 專案 > 原始程式檔 > 滑鼠右鍵 > 加入 > 新增項目:
Visual C++ > 標頭檔 (.h)
名稱: OpenGL.h
> 新增
Visual C++ > C++ 檔 (.cpp)
名稱: OpenGL.cpp
> 新增
-------------------------------------------------------------------------------------
D. 專案屬性設定:
點選 "GLForm_Hello" 專案 > 滑鼠右鍵
> 屬性
> 組態屬性
1. > 一般: (Debug 與 Release)
字元集: 使用 Unicode 字元集
Common Language Runtime 支援: Common Language Runtime 支援 (/clr)
> 確定
2. > 連結器 > 輸入 > 其他相依性: (Debug 與 Release)
opengl32.lib
glut32.lib
glew32.lib
glew32s.lib
glu32.lib
> 確定
-------------------------------------------------------------------------------------
E. 撰寫 OpenGL 類別檔案
1. 開啟 OpenGL.h 檔案, 加入以下程式碼:
#pragma once
#include <windows.h>
#include <GL/glut.h>
// OpenGL 繪圖類別
class OpenGL
{
public:
OpenGL(); // 建構子
bool Init(HDC hdc); // 傳入參數: device context, 來建立 render context
void InitOpenGL(); // 初始化 OpenGL 環境
void InitScene(int x, int y, int width, int height); // 初始化場景
void RenderScene(); // 繪製場景
public:
~OpenGL(void); // 解構子
private:
HDC m_hDC; // device context handler
HGLRC m_rc; // render context handler
// -------------------- lights(光源)
GLfloat gLightDiffuse[4]; // light diffuse (散射光)
GLfloat gLightPosition[4]; // light position
// -------------------- rotate angle(旋轉角度)
float m_rAngle;
};
***************************************************
2. 開啟 OpenGL.cpp 檔案, 加入以下程式碼:
#include "stdafx.h"
#include <iostream>
#include "OpenGL.h"
// 建構子
OpenGL::OpenGL()
{
m_rc = NULL;
}
// 解構子
OpenGL::~OpenGL(void)
{
wglMakeCurrent(0,0);
wglDeleteContext(m_rc);
}
// 傳入參數: device context, 來建立 Render Context
bool OpenGL::Init(HDC hdc)
{
m_hDC = hdc;
// Set pixel format
PIXELFORMATDESCRIPTOR pfd;
memset(&pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);
pfd.nVersion = 1;
pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
pfd.iPixelType = PFD_TYPE_RGBA;
pfd.cColorBits = 32;
pfd.cDepthBits = 32;
pfd.iLayerType = PFD_MAIN_PLANE;
int cpf = ChoosePixelFormat(m_hDC, &pfd);
if(!cpf)
{
std::cerr << "Choose Pixel Format Error\n";
return false;
}
SetPixelFormat(m_hDC, cpf, &pfd);
m_rc = wglCreateContext(m_hDC);
if(!m_rc)
{
std::cerr << "Create Render Context Error\n";
return false;
}
if(!wglMakeCurrent(m_hDC, m_rc))
{
std::cerr << "wglMakeCurrent Error\n";
return false;
}
return true;
}
// 初始化 OpenGL 環境
void OpenGL::InitOpenGL()
{
glClearColor(1.0f, 1.0f, 1.0f, 1.0f); // White Background
//glColor3f(0.0, 0.0, 0.0); // 筆畫顏色: 黑
/*******************************************************/
// 光源設定
// light diffuse (散射光): 紅色
gLightDiffuse[0] = 1.0;
gLightDiffuse[1] = 0.0;
gLightDiffuse[2] = 0.0;
gLightDiffuse[3] = 1.0;
// light position (光源位置)
gLightPosition[0] = 1.0;
gLightPosition[1] = 1.0;
gLightPosition[2] = 0.0;
gLightPosition[3] = 1.0;
// 啟用第一個光源設定
glLightfv(GL_LIGHT0, GL_DIFFUSE, gLightDiffuse);
glLightfv(GL_LIGHT0, GL_POSITION, gLightPosition);
glEnable(GL_LIGHT0);
/*******************************************************/
glClearDepth(1.0f); // Depth Buffer Setup
glEnable(GL_DEPTH_TEST); // Enables Depth Testing
glEnable(GL_AUTO_NORMAL);
glEnable(GL_NORMALIZE);
glShadeModel(GL_SMOOTH); // Enable Smooth Shading
glDepthFunc(GL_LEQUAL); // The Type Of Depth Testing To Do
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
}
// 初始化場景
void OpenGL::InitScene(int x, int y, int width, int height)
{
glViewport(x, y, width, height);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glMatrixMode(GL_PROJECTION); // Select The Projection Matrix
glLoadIdentity(); // Reset The Projection Matrix
// 透視投影
/*
gluPerspective(
45.0f, // field of view in degree
(GLfloat)width/(GLfloat)height, // aspect ratio
0.1f, // Z near
100.0f); // Z far
*/
// 正交投影
//glOrtho(-5, 5, -5, 5, 5, 15);
glMatrixMode(GL_MODELVIEW); // Select The Modelview Matrix
glLoadIdentity(); // Reset The Modelview Matrix
// 眼睛(攝影機) 位置與觀看方向
gluLookAt(
0.0, 0.0, 10.0, // eye is at (0,0,10.0)
0.0, 0.0, 0.0, // eye look at (0,0,0)
0.0, 1.0, 0.0); // up is in positive Y direction
}
// 繪製場景
void OpenGL::RenderScene()
{
// Clear Screen And Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // Reset The Current Modelview Matrix
// -------------------- 啟用光源設定
glEnable(GL_LIGHTING);
// render the 2D/3D object
/*
glBegin(xxx);
....
glEnd();
*/
// 3. 再位移
//glTranslatef(3.0f,0.0f,0.0f); // Move Right 3 Units
// 2. 再旋轉角度
m_rAngle = m_rAngle + 1;
if(360 < m_rAngle) m_rAngle -= 360;
glRotatef(m_rAngle, 0.0f, 1.0f, 0.0f); // 對 Y 軸(垂直軸)作旋轉
// 1. 先畫圖
//glPointSize(1.0f);
glColor3f(0.4, 0.6, 0.7);
glutWireTeapot(0.5);
// -------------------- 停用光源設定
glDisable(GL_LIGHTING);
SwapBuffers(m_hDC);
}
-------------------------------------------------------------------------------------
F. 為專案的 Form 添加成員變數
開啟 Form1.h 檔案 ( Form1.h > 滑鼠右鍵 > 檢視程式碼), 修改如下:
// step 01:
#pragma once
#include "OpenGL.h"
OpenGL w_openGL; // w: 代表由 wgl 所建立的 GL
....
// step 02:
private:
/// <summary>
/// 設計工具所需的變數。
/// </summary>
System::ComponentModel::Container ^components;
//@add for OpenGL handle
HDC m_hDC; // Device Context Handler
int m_width;
int m_height;
-------------------------------------------------------------------------------------
G.為專案的 Form 加入新元件與事件:
開啟 Form1.h 設計工具 ( Form1.h > 滑鼠右鍵 > 設計工具檢視):
1. 更改 Form 的顯示名稱:
> 屬性 > Text: GLForm Hello
Size: 400, 400
2. 幫 Form 加入 Paint 事件:
> 屬性 > 事件(閃電圖示):
點二下 Paint 事件
3. 幫 Form 加入 ClientSizeChanged 事件:
> 屬性 > 事件(閃電圖示):
點二下 ClientSizeChanged 事件
4. 幫 Form 加入 Panel 元件:
> 工具箱 > 將 Panel 拖拉到 Form 上
> 屬性:
(Name): OpenGLPanel
Duck: Fill (填滿)
5. 幫 Form 加入 Timer 元件與 Tick 事件:
> 工具箱 > 將 Timer 拖拉到 Form 上
> 點二下 Tick 事件
-------------------------------------------------------------------------------------
H. 為專案的 Form 加入 OpenGL 的處理:
開啟 Form1.h 檔案 ( Form1.h > 滑鼠右鍵 > 檢視程式碼), 修改如下:
....
// step 01:
public:
Form1(void)
{
InitializeComponent();
//
//TODO: 在此加入建構函式程式碼
//
//@add for OpenGL
OpenGLInit();
}
//@add for OpenGL
void OpenGLInit(void)
{
// Get Device Context
//m_hDC = GetDC((HWND)(this->Handle.ToInt32()));
m_hDC = GetDC((HWND)(this->OpenGLPanel->Handle.ToInt32()));
if(!w_openGL.Init(m_hDC))
{
MessageBox::Show("OpenGL Init Error");
}
w_openGL.InitOpenGL();
m_width = this->ClientSize.Width;
m_height = this->ClientSize.Height;
w_openGL.InitScene(0, 0, m_width, m_height);
// 下面二行註解的話, 就不會產生動畫效果,
// 只會執行一次 Form1_Paint 事件裡的 w_openGL.RenderScene();
timer1->Interval = 20;
timer1->Enabled = true;
}
....
// step 02:
private: System::Void Form1_Paint(System::Object^ sender, System::Windows::Forms::PaintEventArgs^ e) {
//@add for OpenGL
w_openGL.RenderScene();
}
private: System::Void Form1_ClientSizeChanged(System::Object^ sender, System::EventArgs^ e) {
//@add for OpenGL
int width=this->ClientSize.Width;
int height=this->ClientSize.Height;
w_openGL.InitScene(0,0,width,height);
w_openGL.RenderScene();
}
private: System::Void timer1_Tick(System::Object^ sender, System::EventArgs^ e) {
//@add for OpenGL
w_openGL.RenderScene();
}
};
....
-------------------------------------------------------------------------------------
I. 建置並啟動::
繞著 Y 軸(垂直軸)旋轉的 Teapot