建立COpenGL類別繼承CWnd,封裝OpenGL渲染上下文建立、場景繪製、全螢幕切換功能,可直接嵌入MFC對話框:
// 對話框初始化
CRect rect;
GetDlgItem(IDC_OPENGL)->GetWindowRect(&rect);
ScreenToClient(&rect);
m_openGL.Create(rect, this); // m_openGL為COpenGL成員變數
// 渲染迴圈 (如OnTimer或獨立執行緒)
m_openGL.RenderGLScene();
class COpenGL : public CWnd {
public:
COpenGL();
virtual ~COpenGL();
// 建立OpenGL視窗
void Create(CRect rect, CWnd* pParent);
// 渲染場景 (衍生類別覆寫)
virtual void RenderGLScene();
// 全螢幕切換
BOOL SetFullScreen(int width, int height, int depth);
BOOL SetNormScreen();
protected:
// 視窗訊息處理
afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
afx_msg void OnPaint();
afx_msg void OnSize(UINT nType, int cx, int cy);
afx_msg void OnKeyDown(UINT nChar, UINT nRepCnt, UINT nFlags);
DECLARE_MESSAGE_MAP()
private:
// 內部實作
int InitGL(); // 初始化OpenGL狀態
void KillGLWindow(); // 清理資源
// 成員變數
CRect m_rect;
CWnd* m_pParent;
BOOL m_bFullScreen;
BOOL m_bInit;
DEVMODE m_DMsaved; // 儲存原始顯示模式
HDC m_hDC; // 裝置內容
HGLRC m_hRC; // OpenGL渲染上下文
};
void COpenGL::Create(CRect rect, CWnd* pParent) {
if (m_bInit) return;
m_rect = rect; m_pParent = pParent;
// 註冊視窗類別 (CS_OWNDC關鍵:每個視窗獨立DC)
CString className = AfxRegisterWndClass(
CS_HREDRAW | CS_VREDRAW | CS_OWNDC,
NULL, (HBRUSH)GetStockObject(BLACK_BRUSH), NULL);
CreateEx(0, className, _T("OpenGL"),
WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN,
rect, pParent, 0);
}
int COpenGL::OnCreate(LPCREATESTRUCT lpCreateStruct) {
if (CWnd::OnCreate(lpCreateStruct) == -1) return -1;
// 取得目前顯示模式 (用於全螢幕切換還原)
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &m_DMsaved);
// 像素格式描述子 (PFD)
static PIXELFORMATDESCRIPTOR pfd = {
sizeof(PIXELFORMATDESCRIPTOR), 1,
PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
PFD_TYPE_RGBA,
m_DMsaved.dmBitsPerPel, // 色深
0, 0, 0, 0, 0, 0, // 色位元忽略
0, // 無Alpha緩衝
0, 0, 0, 0, 0, // 累積緩衝忽略
16, // 16-bit深度緩衝
0, // 無模板緩衝
0, // 無輔助緩衝
PFD_MAIN_PLANE, 0, 0, 0, 0
};
m_hDC = ::GetDC(m_hWnd);
int nPixelFormat = ChoosePixelFormat(m_hDC, &pfd);
SetPixelFormat(m_hDC, nPixelFormat, &pfd);
m_hRC = wglCreateContext(m_hDC);
wglMakeCurrent(m_hDC, m_hRC);
InitGL(); // 初始化OpenGL狀態
m_bInit = TRUE;
return 0;
}
int COpenGL::InitGL() {
glShadeModel(GL_SMOOTH);
glClearColor(0.0f, 0.0f, 0.0f, 0.5f);
glClearDepth(1.0f);
glEnable(GL_DEPTH_TEST);
glDepthFunc(GL_LEQUAL);
glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST);
return TRUE;
}
void COpenGL::OnPaint() {
CPaintDC dc(this);
RenderGLScene();
SwapBuffers(m_hDC); // 雙緩衝交換
}
void COpenGL::OnSize(UINT nType, int cx, int cy) {
CWnd::OnSize(nType, cx, cy);
if (cx == 0 || cy == 0) return;
glViewport(0, 0, cx, cy);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
gluPerspective(45.0, (GLfloat)cx/(GLfloat)cy, 0.1, 100.0);
glMatrixMode(GL_MODELVIEW);
glLoadIdentity();
}
void COpenGL::RenderGLScene() {
// 衍生類別實作具體繪製邏輯
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// ... 繪製幾何體 ...
}
BOOL COpenGL::SetFullScreen(int width, int height, int depth) {
if (m_bFullScreen) return FALSE;
DEVMODE dmScreenSettings = {0};
dmScreenSettings.dmSize = sizeof(DEVMODE);
dmScreenSettings.dmPelsWidth = width;
dmScreenSettings.dmPelsHeight = height;
dmScreenSettings.dmBitsPerPel = depth;
dmScreenSettings.dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT;
// 切換顯示模式
if (ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN) != DISP_CHANGE_SUCCESSFUL)
return FALSE;
// 調整視窗樣式與位置
SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
SetWindowPos(NULL, 0, 0, width, height, SWP_NOZORDER | SWP_FRAMECHANGED);
m_bFullScreen = TRUE;
return TRUE;
}
BOOL COpenGL::SetNormScreen() {
if (!m_bFullScreen) return FALSE;
ChangeDisplaySettings(&m_DMsaved, 0); // 還原原始模式
SetWindowLong(m_hWnd, GWL_STYLE, WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS | WS_CLIPCHILDREN);
SetWindowPos(m_pParent->GetSafeHwnd(), m_rect.left, m_rect.top,
m_rect.Width(), m_rect.Height(), SWP_FRAMECHANGED);
m_bFullScreen = FALSE;
return TRUE;
}
void COpenGL::KillGLWindow() {
if (m_bFullScreen) SetNormScreen();
if (m_hRC) {
wglMakeCurrent(NULL, NULL);
wglDeleteContext(m_hRC);
m_hRC = NULL;
}
if (m_hDC) {
::ReleaseDC(m_hWnd, m_hDC);
m_hDC = NULL;
}
m_bInit = FALSE;
}
COpenGL::~COpenGL() { KillGLWindow(); }
class CMyOpenGL : public COpenGL {
float m_fAngle;
public:
CMyOpenGL() : m_fAngle(0.0f) {}
virtual void RenderGLScene() override {
COpenGL::RenderGLScene(); // 清除緩衝
glLoadIdentity();
glTranslatef(0.0f, 0.0f, -6.0f);
glRotatef(m_fAngle, 0.0f, 1.0f, 0.0f);
glBegin(GL_TRIANGLES);
glColor3f(1,0,0); glVertex3f(0, 1, 0);
glColor3f(0,1,0); glVertex3f(-1,-1, 0);
glColor3f(0,0,1); glVertex3f(1,-1, 0);
glEnd();
m_fAngle += 0.5f;
}
};
COpenGL類別完整封裝MFC對話框中OpenGL渲染所需的視窗建立、像素格式協商、渲染上下文管理、雙緩衝繪製、尺寸自適應與全螢幕切換功能。衍生類別僅需覆寫RenderGLScene()即可專注於3D場景邏輯,大幅降低整合複雜度。