首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> 關於C語言

MFC對話框整合OpenGL渲染類COpenGL完整實作

     編輯:關於C語言

一、設計目標與使用方式

建立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();

二、核心類別定義 (OpenGL.h)


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渲染上下文
};

三、關鍵實作細節 (OpenGL.cpp)

1. 視窗建立與像素格式設定


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;
}

2. OpenGL狀態初始化


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;
}

3. 繪製與尺寸變更處理


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);
    // ... 繪製幾何體 ...
}

4. 全螢幕切換實作


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;
}

5. 資源清理


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場景邏輯,大幅降低整合複雜度。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved