【问题标题】:Rendering OpenGL canvas within a WC_TABCONTROL在 WC_TABCONTROL 中渲染 OpenGL 画布
【发布时间】:2017-12-20 05:22:43
【问题描述】:

我的目标是使用包含用于渲染 OpenGL 的画布的 Win32 API 创建一个基本的选项卡控件。我的选项卡包含一个用于渲染 OpenGL 的静态控件。但是,我可以让画布出现在 GUI 中的唯一方法是排除选项卡控件(在我的示例中注释掉 CREATE_TAB_PANE 宏来执行此操作)。

我的例子如下:

// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out

#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")

#define CREATE_TAB_PANE

enum { IDC_TAB = 200, IDC_CANVAS = 201 };

static HWND TabPaneId;
static HWND CanvasId;

static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;
static HDC CanvasDc;

////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
                                UINT msg,
                                WPARAM wParam,
                                LPARAM lParam) {

  switch (msg) {
    case WM_CREATE:
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
                                         UINT msg,
                                         WPARAM wParam,
                                         LPARAM lParam) {

  if (msg == WM_PAINT) {
    PAINTSTRUCT ps;
    BeginPaint(hwnd, &ps);
    EndPaint(hwnd, &ps);

    wglMakeCurrent(CanvasDc, CanvasRc);
    SwapBuffers(CanvasDc);

    return 0;
  }

  return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {

  glClearColor(0.0, 0.0, 0.0, 0.0);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(0, 1, 0, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {

  glClear(GL_COLOR_BUFFER_BIT);

  glLoadIdentity();
  glColor3d(1, 0, 0);

  glBegin(GL_LINE_STRIP);
    glVertex2d(0.2, 0.2);
    glVertex2d(0.8, 0.8);
  glEnd();

  SwapBuffers(CanvasDc);
}

////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd) {

  // Create window

  const char *className = "OpenGlTab";

  WNDCLASSEX wc;
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = className;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc)) {
    exit(0);
  }

  HWND winId = CreateWindowEx(0,
                              className,
                              "Tab Pane w/ OpenGL",
                              WS_OVERLAPPEDWINDOW,
                              100, 100, 600, 400,
                              0,
                              0,
                              hInstance,
                              0);
  if (!winId) {
    exit(0);
  }

  ShowWindow(winId, nShowCmd);
  UpdateWindow(winId);

#ifdef CREATE_TAB_PANE

  // Create tab pane

  TabPaneId = CreateWindow(WC_TABCONTROL,
                           0,
                           WS_CHILD | WS_VISIBLE,
                           10, 10, 566, 343,
                           winId,
                           HMENU(IDC_TAB),
                           hInstance,
                           0);

  TCITEM tabItem = {0};
  tabItem.mask = TCIF_TEXT;
  tabItem.pszText = "Tab";

  SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));

#endif

  // Create OpenGL canvas

  int w = 200;
  int h = 200;

  CanvasId = CreateWindow("static",
                          "",
                          WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN |  WS_CLIPSIBLINGS,
                          20, 100, w, h,
                          winId,
                          HMENU(IDC_CANVAS),
                          hInstance,
                          0);

  CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
                                            GWLP_WNDPROC,
                                            (LONG_PTR)OpenGlCanvasProc);

  CanvasDc = GetDC(CanvasId);

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1, // version
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32, // color depth
    0, 0, 0, 0, 0, 0,
    0, 0,
    0, 0, 0, 0, 0,
    16, // depth buffer
    0,
    0,
    0,
    0,
    0, 0, 0
  };

  int pixelFormat = ChoosePixelFormat(CanvasDc, &pfd);
  SetPixelFormat(CanvasDc, pixelFormat, &pfd);

  CanvasRc = wglCreateContext(CanvasDc);

  // Render OpenGL canvas

  wglMakeCurrent(CanvasDc, CanvasRc);
  CanvasResize(w, h);
  CanvasInit();
  CanvasDisplay();
  SwapBuffers(CanvasDc);

  // Execute GUI

  MSG msg;

  while (GetMessage(&msg, 0, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

我的更新示例与每个 Chris 的更改:

// OpenGlTabWin32.cpp
// NOTE: canvas displays fine if TabPane creation is commented out

#include <windows.h>
#include <commctrl.h>
#include <gl/GL.h>

#pragma comment(linker, "\"/manifestdependency:type='win32' name='Microsoft.Windows.Common-Controls' version='6.0.0.0' processorArchitecture='*' publicKeyToken='6595b64144ccf1df' language='*'\"")
#pragma comment(lib, "comctl32.lib")
#pragma comment(lib, "opengl32.lib")

#define CREATE_TAB_PANE

enum { IDC_TAB = 200, IDC_CANVAS = 201 };

static HWND TabPaneId;
static HWND CanvasId;

static WNDPROC CanvasWndProc;
static HGLRC CanvasRc;

////////////////////////////////////////////////////////////////////////////////
// WndProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK WndProc(HWND hwnd,
                                UINT msg,
                                WPARAM wParam,
                                LPARAM lParam) {

  switch (msg) {
    case WM_CREATE:
      break;

    case WM_DESTROY:
      PostQuitMessage(0);
      break;

    default:
      return DefWindowProc(hwnd, msg, wParam, lParam);
  }

  return 0;
}

////////////////////////////////////////////////////////////////////////////////
// CanvasInit
////////////////////////////////////////////////////////////////////////////////
static void CanvasInit() {

  glClearColor(0.0, 0.0, 0.0, 0.0);
}

////////////////////////////////////////////////////////////////////////////////
// CanvasResize
////////////////////////////////////////////////////////////////////////////////
static void CanvasResize(int width, int height) {

  glViewport(0, 0, width, height);

  glMatrixMode(GL_PROJECTION);
  glLoadIdentity();

  glOrtho(0, 1, 0, 1, -1, 1);
  glMatrixMode(GL_MODELVIEW);
  glLoadIdentity();
}

////////////////////////////////////////////////////////////////////////////////
// CanvasDisplay
////////////////////////////////////////////////////////////////////////////////
static void CanvasDisplay() {

  glClear(GL_COLOR_BUFFER_BIT);

  glLoadIdentity();
  glColor3d(1, 0, 0);

  glBegin(GL_LINE_STRIP);
    glVertex2d(0.2, 0.2);
    glVertex2d(0.8, 0.8);
  glEnd();
}

////////////////////////////////////////////////////////////////////////////////
// OpenGlCanvasProc
////////////////////////////////////////////////////////////////////////////////
static LRESULT CALLBACK OpenGlCanvasProc(HWND hwnd,
                                         UINT msg,
                                         WPARAM wParam,
                                         LPARAM lParam) {

  switch (msg) {
    case WM_PAINT:
    {
      PAINTSTRUCT ps;
      HDC hdc = BeginPaint(hwnd, &ps);

      wglMakeCurrent(hdc, CanvasRc);
      CanvasDisplay();
      SwapBuffers(hdc);
      wglMakeCurrent(NULL, NULL);
      ReleaseDC(hwnd, hdc);

      EndPaint(hwnd, &ps);

      return 0;
    }

    case WM_SIZE:
    {
      HDC hdc = GetDC(hwnd);
      wglMakeCurrent(hdc, CanvasRc);
      CanvasResize(LOWORD(lParam), HIWORD(lParam));
      wglMakeCurrent(NULL, NULL);
      ReleaseDC(hwnd, hdc);

      return 0;
    }
  }

  return CallWindowProc(CanvasWndProc, hwnd, msg, wParam, lParam);
}

////////////////////////////////////////////////////////////////////////////////
// WinMain
////////////////////////////////////////////////////////////////////////////////
int WINAPI WinMain(HINSTANCE hInstance,
                   HINSTANCE hPrevInstance,
                   LPSTR lpCmdLine,
                   int nShowCmd) {

  // Create window

  const char *className = "OpenGlTab";

  WNDCLASSEX wc;
  wc.cbSize = sizeof(WNDCLASSEX);
  wc.style = 0;
  wc.lpfnWndProc = WndProc;
  wc.cbClsExtra = 0;
  wc.cbWndExtra = 0;
  wc.hInstance = hInstance;
  wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
  wc.hCursor = LoadCursor(NULL, IDC_ARROW);
  wc.hbrBackground = (HBRUSH)(COLOR_BTNFACE + 1);
  wc.lpszMenuName = NULL;
  wc.lpszClassName = className;
  wc.hIconSm = LoadIcon(NULL, IDI_APPLICATION);

  if (!RegisterClassEx(&wc)) {
    exit(0);
  }

  HWND winId = CreateWindowEx(0,
                              className,
                              "Tab Pane w/ OpenGL",
                              WS_OVERLAPPEDWINDOW,
                              100, 100, 600, 400,
                              0,
                              0,
                              hInstance,
                              0);
  if (!winId) {
    exit(0);
  }

  ShowWindow(winId, nShowCmd);
  UpdateWindow(winId);

#ifdef CREATE_TAB_PANE

  // Create tab pane

  TabPaneId = CreateWindow(WC_TABCONTROL,
                           0,
                           WS_CHILD | WS_VISIBLE | WS_CLIPSIBLINGS,
                           10, 10, 566, 343,
                           winId,
                           HMENU(IDC_TAB),
                           hInstance,
                           0);

  TCITEM tabItem = {0};
  tabItem.mask = TCIF_TEXT;
  tabItem.pszText = "Tab";

  SendMessage(TabPaneId, TCM_INSERTITEM, 0, LPARAM(&tabItem));

#endif

  // Create OpenGL canvas

  int w = 200;
  int h = 200;

  CanvasId = CreateWindow("static",
                          "",
                          WS_CHILD | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
                          20, 100, w, h,
                          winId,
                          HMENU(IDC_CANVAS),
                          hInstance,
                          0);

  CanvasWndProc = (WNDPROC)SetWindowLongPtr(CanvasId,
                                            GWLP_WNDPROC,
                                            (LONG_PTR)OpenGlCanvasProc);

  HDC hdc = GetDC(CanvasId);

  static PIXELFORMATDESCRIPTOR pfd = {
    sizeof(PIXELFORMATDESCRIPTOR),
    1, // version
    PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER,
    PFD_TYPE_RGBA,
    32, // color depth
    0, 0, 0, 0, 0, 0,
    0, 0,
    0, 0, 0, 0, 0,
    16, // depth buffer
    0,
    0,
    0,
    0,
    0, 0, 0
  };

  int pixelFormat = ChoosePixelFormat(hdc, &pfd);
  SetPixelFormat(hdc, pixelFormat, &pfd);

  CanvasRc = wglCreateContext(hdc);

  // Render OpenGL canvas

  wglMakeCurrent(hdc, CanvasRc);
  CanvasResize(w, h);
  CanvasInit();
  CanvasDisplay();
  SwapBuffers(hdc);
  wglMakeCurrent(NULL, NULL);
  ReleaseDC(CanvasId, hdc);

  // Execute GUI

  MSG msg;

  while (GetMessage(&msg, 0, 0, 0) > 0) {
    TranslateMessage(&msg);
    DispatchMessage(&msg);
  }

  return 0;
}

【问题讨论】:

  • 您应该为 OpenGL 画布创建自己的窗口类。 SetPixelFormat要求不设置windows类CS_PARENTDC样式,但默认静态类可能有,可能会导致问题。

标签: c++ winapi opengl


【解决方案1】:

您在此示例中发生了许多事情,这些事情都可能有所贡献:

  • 最直接的问题可能只是选项卡窗口覆盖了画布窗口,而您实际上并未绘制任何响应 WM_PAINT 的内容。选项卡控件将在其无效后立即在您的画布上进行绘制,因为 Windows 通常允许子窗口相互绘制;因此将 WS_CLIPSIBLINGS 添加到选项卡控件可能会有所帮助。

  • 在将 HDC 与当前 wgl 上下文相关联后,您正在将 HDC 抓取到静态控件并持有它。除非您使用带有 CS_OWNDC 的窗口类,尤其是不使用可能具有 CS_PARENTDC 的窗口类,否则您不应该真正这样做(因为这样,一旦父窗口或不同的子窗口绘制,DC 就会重新与从未关联过 SetPixelFormat 的窗口)。

  • 您只是将您的 opengl 上下文设置为最新,并希望稍后设置它。这很好 - 假设您有一个带有 HDC 的 CS_OWNDC 窗口,您可以在前面抓住并保留 - 并且假设您出于任何原因永远不想创建第二个 GL 上下文。

因此,当您在不控制窗口类样式(或可能有多个 OpenGL 上下文)的应用程序中执行 OpenGL 时,您需要确保始终清除当前上下文并在您完成后立即释放 DC已经完成了。

例如,您的 CanvasWindowProc 应该看起来更像这样:

case WM_PAINT:
  {
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd,&ps);
    wglMakeCurrent(glrc,hdc);
    CanvasDisplay();
    SwapBuffers();
    wglMakeCurrent(NULL,NULL);
    EndPaint(&ps);
  }
  return 0;
case WM_SIZE:
  {
    HDC hdc = GetDC(hwnd);
    wglMakeCurrent(glrc,hdc);
    CanvasResize(LOWORD(lParam),HIWORD(lParam));
    wglMakeCurrent(NULL,NULL);
    ReleaseDC(hwnd,hdc);
  }
  break;

【讨论】:

  • 克里斯 - 谢谢你的帮助。我根据您的建议进行了更改:(1) 将 WS_CLIPSIBLINGS 添加到选项卡控件,(2) 清除当前上下文并在使用完毕后释放 DC。这反映在上面的第二个代码示例中。不幸的是,这些更改并没有解决问题。
  • 我将您的代码粘贴到我的环境中,翻转了窗口创建,以便在 Canvas 静态之后创建选项卡控件并且它绘制得很好。好吧,我还删除了您放入 WM_PAINT 处理程序的额外 ReleaseDC。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-11-23
  • 1970-01-01
  • 2012-08-15
  • 2018-04-19
  • 1970-01-01
  • 2012-10-06
  • 2013-02-18
相关资源
最近更新 更多