【问题标题】:Opengl texture flickers/moves sometimes on window resizeOpengl纹理有时会在窗口调整大小时闪烁/移动
【发布时间】:2018-09-07 02:52:01
【问题描述】:

我有一个小问题。

我有一个带有 OpenGL 子窗口的窗口, 到目前为止,Opengl 窗口所做的只是在窗口的右下角渲染一个纹理。

如果我从左到右快速调整窗口大小,纹理有时会向左移动,这就像它的重绘速度不够快,即使我知道我直接绘制到 opengl 上下文。

我解决此问题的唯一方法是在调整主窗口大小时使 opengl 窗口的 InvalidateRect 无效,但我不确定为什么这会解决问题,因为我正在绘制到 opengl 上下文,所以我不应该需要让它在之后绘制它自己我已经根据上下文进行了绘制。

如果bHandled 为假,则调用 defWndProc,否则返回函数

主窗口 WM_SIZE

int ControllerMainWnd::OnSize(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    bHandled = true;

    int width = LOWORD(lParam);
    int height = HIWORD(lParam);

    //Set OpenGL Window Size
    ::SetWindowPos(glHandle, 0, 0, 0, width, height, SWP_NOZORDER);

    //Invalidate OpenGL Window
    ::InvalidateRect(glHandle, 0, FALSE); // if i comment this out my texture sometime moves

    return 0;
}

OpenGL 窗口

int ControllerGL::OnPaint(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    //bHandled = true;
    //LRESULT lRes = defWinProc(WM_PAINT, wParam, lParam);

    Paint();

    //return lRes;
    return 0;
}

int ControllerGL::OnSize(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    bHandled = true;

    int width = LOWORD(lParam);
    int height = HIWORD(lParam);

    modelGL->setWindowSize(width, height);

    Paint();

    return 0;
}

int ControllerGL::OnEraseBkgnd(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    bHandled = true;
    return 1L;
}

auto ControllerGL::Paint() -> void
{
    openGLcontext->activateContext();
    modelGL->draw();
    openGLcontext->swapBuffers();
}

【问题讨论】:

  • 你为什么还要打电话给InvalidateRect,你从来没有验证过呢?该区域在您的代码中总是无效。无需告诉系统,它已经知道了什么。除非您开始验证无效区域。 We've been through this 已经。
  • 因为在没有 InvalidateRect 的情况下,当窗口快速调整大小时,纹理会移动/闪烁一点。所以我在问为什么
  • 更明确 - 始终在 WM_PAINT 处理程序中使用 BeginPaintEndPaint - 这是验证无效区域所必需的 - 并正确裁剪到要更新的区域。跨度>
  • @ChrisBecke 我用 openGl 上下文而不是 windows api 绘图。所以我还需要使用 beginPaint?
  • 您不需要,但您应该验证无效区域,以防止您的应用不断收到WM_PAINT 消息流。

标签: c++ winapi opengl


【解决方案1】:

您的问题可能与您在调整大小期间每次移动鼠标时都会导致窗口重绘 2 次有关。

首先,当您调整窗口区域的大小时,Windows 会自动使其无效(您可以使用 CS_HREDRAW 和 CS_VREDRAW 类样式来控制它,因为调整大小通常需要完整的窗口重绘,所以应该始终为 OpenGL 窗口设置这些样式)。

然后您(多余地)手动使窗口矩形无效。然后你强制它绘画 - 现在排队等待未来的绘画。

OpenGL 窗口的 OnPaint 处理程序应该如下所示:

int ControllerGL::OnPaint(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd,&ps);
    wglMakeCurrent(hdc,hglrc);
    scene->Display();
    SwapBuffers(hdc);
    wglMakeCurrent(hdc,NULL);
    EndPaint(hwnd,&ps);
    bHandled = true;
    return 0;
}

我将它作为正确封装 hwnd 和 hglrc 的练习,以这种方式编写它明确显示 SwapBuffers 在 DC 上工作,而不是在隐式 OpenGL 上下文中工作,并且 wglMakeCurrent 应该使用动态获得的窗口来限定范围直流。

大多数 OpenGL 示例非常懒惰并使用 DC 类,只是让 openGL 内容一直处于“活动状态”,但是一旦线程上的两个窗口尝试执行 OpenGL,这就会失败。最好省略 CS_OWNDC(如果有的话)并在实际需要时获取(并释放)DC。

即如果您想更传统地处理 WM_SIZE(类似于 glut Reshape 回调),您可以这样做:

int ControllerGL::OnSize(WPARAM wParam, LPARAM lParam, bool & bHandled)
{
    bHandled = true;

    int width = LOWORD(lParam);
    int height = HIWORD(lParam);

    HDC hdc = GetDC(hwnd);
    wglMakeCurrent(hdc,hglrc);

    scene->Reshape(width,height);

    wglMakeCurrent(hdc,NULL);
    ReleaseDC(hwnd,hdc);
    return 0;
}

ControllerGL 到我称为scene 的所有其他呼叫以及您呼叫modelGL 的所有其他呼叫都将被类似地包装。

【讨论】:

    猜你喜欢
    • 2014-12-29
    • 2016-03-19
    • 2015-08-29
    • 1970-01-01
    • 2014-01-24
    • 2022-10-13
    • 2012-11-17
    • 1970-01-01
    • 2019-12-06
    相关资源
    最近更新 更多