You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在单个MFC对话框窗口中创建两个独立的OpenGL视图

如何在单个MFC对话框窗口中创建两个独立的OpenGL视图

嗨,既然你已经能搞定单个OpenGL视图的创建了,要扩展成两个独立的其实核心就是给每个视图单独维护一套OpenGL上下文和渲染资源,这样它们就能各自渲染不同的场景、用不同的shader和VAO而互不干扰。下面我给你一步步拆解具体实现:

1. 先准备UI元素

在你的MFC对话框编辑器里,拖两个Picture Control控件,分别给它们设置独一无二的ID,比如IDC_OPENGL_VIEW1和IDC_OPENGL_VIEW2,这就是两个视图的载体。

2. 封装独立的视图数据

因为要区分两个视图的资源,不能再用单个的m_pDC这类成员变量了,我们可以写个结构体来统一存放每个视图的所有相关资源:

struct OpenGLViewData {
    CWnd* pWnd;          // 对应的控件窗口
    CDC* pDC;            // 设备上下文
    HGLRC hRC;           // OpenGL渲染上下文
    GLuint vao;          // 该视图的VAO
    GLuint shaderProgram;// 该视图的shader程序
};

// 在对话框类里声明两个实例
OpenGLViewData m_view1, m_view2;

3. 编写通用的视图初始化函数

把你原来的setRenderingContext()改成可以针对单个视图初始化的通用函数,这样两个视图都能复用这个逻辑:

BOOL CMYAPPDlg::InitOpenGLView(OpenGLViewData& view, UINT ctrlID) {
    // 获取对应的控件窗口
    view.pWnd = GetDlgItem(ctrlID);
    if (!view.pWnd) return FALSE;

    // 获取控件的DC
    view.pDC = view.pWnd->GetDC();
    if (!view.pDC) return FALSE;

    // 定义像素格式描述符,和你原来的逻辑一致
    static PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,
        PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
        PFD_TYPE_RGBA,
        32, // 32位颜色深度
        0, 0, 0, 0, 0, 0,
        0,
        0,
        0,
        0, 0, 0, 0,
        24, // 24位深度缓冲
        8,  // 8位模板缓冲
        0,
        PFD_MAIN_PLANE,
        0,
        0, 0, 0
    };

    // 选择并设置像素格式
    int pixelFormat = ChoosePixelFormat(view.pDC->m_hDC, &pfd);
    if (!pixelFormat) return FALSE;
    if (!SetPixelFormat(view.pDC->m_hDC, pixelFormat, &pfd)) return FALSE;

    // 给当前视图创建独立的渲染上下文
    view.hRC = wglCreateContext(view.pDC->m_hDC);
    if (!view.hRC) return FALSE;

    // 激活当前视图的上下文,初始化它的专属资源
    wglMakeCurrent(view.pDC->m_hDC, view.hRC);
    
    // 这里写该视图的VAO创建、shader编译链接逻辑
    // 比如初始化第一个视图的VAO和shader,第二个视图这里的代码可以不同(或者写成参数化的函数)
    glGenVertexArrays(1, &view.vao);
    glBindVertexArray(view.vao);
    // 绑定VBO、设置顶点属性指针、编译/链接shader到view.shaderProgram...

    // 暂时释放上下文,避免干扰其他视图的初始化
    wglMakeCurrent(NULL, NULL);
    return TRUE;
}

然后在对话框的OnInitDialog()里调用两次这个函数,初始化两个视图:

BOOL CMYAPPDlg::OnInitDialog() {
    CDialogEx::OnInitDialog();

    // 其他初始化代码...

    // 初始化两个OpenGL视图
    if (!InitOpenGLView(m_view1, IDC_OPENGL_VIEW1) || !InitOpenGLView(m_view2, IDC_OPENGL_VIEW2)) {
        AfxMessageBox(L"OpenGL视图初始化失败!");
        return FALSE;
    }

    // 启动定时器触发渲染,比如每30ms渲染一次
    SetTimer(1, 30, NULL);
    return TRUE;
}

4. 实现独立的渲染逻辑

每次渲染时,必须先切换到目标视图的上下文,再进行渲染操作,这样才能保证资源是独立的。比如在定时器响应函数里实现:

void CMYAPPDlg::OnTimer(UINT_PTR nIDEvent) {
    if (nIDEvent == 1) {
        // ===== 渲染第一个视图 =====
        wglMakeCurrent(m_view1.pDC->m_hDC, m_view1.hRC);
        // 清除缓冲,用第一个视图的背景色
        glClearColor(0.2f, 0.3f, 0.3f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // 使用第一个视图的shader和VAO
        glUseProgram(m_view1.shaderProgram);
        glBindVertexArray(m_view1.vao);
        // 绘制第一个场景,比如画三角形
        glDrawArrays(GL_TRIANGLES, 0, 3);
        // 双缓冲交换
        SwapBuffers(m_view1.pDC->m_hDC);
        // 释放上下文
        wglMakeCurrent(NULL, NULL);

        // ===== 渲染第二个视图 =====
        wglMakeCurrent(m_view2.pDC->m_hDC, m_view2.hRC);
        // 用第二个视图的背景色
        glClearColor(0.1f, 0.4f, 0.6f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
        // 使用第二个视图的shader和VAO
        glUseProgram(m_view2.shaderProgram);
        glBindVertexArray(m_view2.vao);
        // 绘制第二个场景,比如画四边形
        glDrawArrays(GL_QUADS, 0, 4);
        SwapBuffers(m_view2.pDC->m_hDC);
        wglMakeCurrent(NULL, NULL);
    }
    CDialogEx::OnTimer(nIDEvent);
}

5. 资源清理不能忘

对话框销毁时,要分别清理每个视图的OpenGL资源和上下文,避免内存泄漏:

void CMYAPPDlg::OnDestroy() {
    // 清理第一个视图
    wglMakeCurrent(m_view1.pDC->m_hDC, m_view1.hRC);
    glDeleteVertexArrays(1, &m_view1.vao);
    glDeleteProgram(m_view1.shaderProgram);
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(m_view1.hRC);
    m_view1.pWnd->ReleaseDC(m_view1.pDC);

    // 清理第二个视图
    wglMakeCurrent(m_view2.pDC->m_hDC, m_view2.hRC);
    glDeleteVertexArrays(1, &m_view2.vao);
    glDeleteProgram(m_view2.shaderProgram);
    wglMakeCurrent(NULL, NULL);
    wglDeleteContext(m_view2.hRC);
    m_view2.pWnd->ReleaseDC(m_view2.pDC);

    CDialogEx::OnDestroy();
}

关键注意事项

  • 上下文切换是核心:每次操作某个视图的OpenGL资源前,必须用wglMakeCurrent切换到它的上下文,操作完最好释放(切换到NULL),避免和另一个视图的操作冲突。
  • 资源完全独立:两个视图的VAO、shader、VBO等资源都要分开创建,绝对不能共用,这样才能保证场景完全独立。
  • 如果代码量变大,建议把每个OpenGL视图封装成自定义控件类(从CWnd派生),这样代码更整洁,后期维护也方便。

内容来源于stack exchange

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.08 13:43:07