在OpenGL中撕裂?

Posted

技术标签:

【中文标题】在OpenGL中撕裂?【英文标题】:Tearing in OpenGL? 【发布时间】:2010-04-16 22:59:26 【问题描述】:

我正在使用 win32 和 opengl,并且我设置了一个窗口,其投影位于窗口坐标的 glOrtho。我启用了双缓冲,也使用 glGet 对其进行了测试。如果我的程序不断被翻译,我的程序似乎总是会撕裂我试图利用它的任何原语。

这是我的 OpenGL 初始化函数:

glClearColor(0.0f, 0.0f, 0.0f, 1.0f);

glViewport(0, 0, 640, 480);
glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    glOrtho(0, 640, 0, 480, 0, 100);

glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    glDrawBuffer(GL_BACK);

glLoadIdentity();

这是我的渲染函数,gMouseX 和 gMouseY 是鼠标的坐标:

glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

glTranslatef(gMouseX, gMouseY, 0.0f);
glColor3f(0.5f, 0.5f, 0.5f);
glBegin(GL_TRIANGLES);
    glVertex2f(0.0f, 128.0f);
    glVertex2f(128.0f, 0.0f);
    glVertex2f(0.0f, 0.0f);
glEnd();

SwapBuffers(hDC);

无论渲染函数的运行频率如何,都会出现同样的撕裂问题。我在这里做错了什么或遗漏了什么?

【问题讨论】:

【参考方案1】:

如果您只渲染一两个三角形,您的帧率将会非常高(可能达到数千 FPS),这意味着它就像地狱一样的 SwapBuffering,这意味着您可能总是在屏幕刷新(即使打开双缓冲)。一旦您的场景开始包含更多内容并且帧速率下降到更真实的值,它应该停止撕裂。或者,考虑启用 V-Sync,例如在 Windows 上使用 wglSwapIntervalEXT

【讨论】:

我忘了说帧率的上限大约是 60 FPS(使用 WM_TIMER 每 16 毫秒),无论我将定时器设置为多少,它仍然会流泪。 如@AshleysBrain 所说启用垂直同步。使用计时器手动限制帧速率不会与屏幕刷新同步。 调用 glGetString(GL_EXTENSIONS) 后,我发现我的系统上没有启用 VSync,因为字符串“WGL_EXT_swap_control”没有返回。是否真的没有其他方法可以减少撕裂,因为我可以下载的几乎所有其他游戏都没有这个问题。 @kaykun:wgl_ext_swap_control 不是 GL 扩展。这是一个 wgl 扩展。有关如何处理 wgl 扩展的一般信息,请参阅 opengl.org/registry/specs/EXT/wgl_extensions_string.txt。 Windows 上的所有现代 GL 实现都支持它,afaik。【参考方案2】:

正如互联网上的所有人都试图建议的那样,这个问题不一定与垂直同步有关。在我的情况下,问题是由窗口初始化的方式引起的。我注意到,摆脱 dwStyle=WS_POPUP 解决了这个问题,但只是部分因为那时窗口没有处于全屏模式。最后对我有用的是在创建窗口后使用 SetWindowLong(hwnd, GWL_STYLE, 0); 。它基本上清除了窗口样式。所以我初始化窗口的代码是这样的:

dwStyle=WS_POPUP;           //Windows Style
ShowCursor(FALSE);          //Hide Mouse Pointer

//Create the Window
hwnd= CreateWindowEx(NULL,
    "GLClass",
    appName,
    dwStyle,
    0, 0,                               
    (long)windowWidth,
    (long)windowHeight, 
    NULL,                               
    NULL,                               
    hInstance,                          
    NULL);                              
SetWindowLong(hwnd, GWL_STYLE, 0);

修改后,我所有的撕裂和无法打印屏幕的问题都得到了解决。我不需要在任何地方触摸 VSync。

【讨论】:

【参考方案3】:

在定义您的 win32 应用程序窗口样式时,不要包含 WS_POPUP 在调试了一个全屏会导致屏幕撕裂的问题后,我发现了全屏的样式,在删除 WS_POPUP 之后,它似乎已经停止了单手撕裂,而不必强制渲染循环到刷新率。 WS_POPUP 以存在问题而闻名,我猜。它还会阻止任何全屏屏幕截图以完成白色。

【讨论】:

这个问题是由垂直同步引起的。您有任何证据表明 WS_POPUP 可能相关吗? 在调试了一个全屏会导致屏幕撕裂的问题后,我发现了全屏的样式,在删除 WS_POPUP 后,它似乎已经停止了单手撕裂,而无需强制渲染循环刷新速度。 WS_POPUP 以存在问题而闻名,我猜。它还会阻止任何全屏屏幕截图以完成白色。 您应该将该解释添加到您的答案中)【参考方案4】:

撕裂是由于未将更新与显示器的垂直消隐间隔同步造成的。它在 OpenGL 中有点依赖硬件(我认为,不是专家),从寻找 glXWaitVideoSyncSGI 开始。谷歌搜索“opengl sync to vblank”也有回报。

【讨论】:

glX 是 Unix,OP 说他在 win32 上。【参考方案5】:

尝试对帧速率设置上限/限制或降低帧速率(如果您没有垂直同步):

这是伪代码:

let tick equals new_tick
if tick is less than last_tick do
    yield
done

calculate last_tick

尝试睡眠功能并将其设置为 1,以便让步:

Sleep(1);

还请记住,限制或限制帧速率并不可取,因为某些功能实际上是为了减少帧计算。

【讨论】:

以上是关于在OpenGL中撕裂?的主要内容,如果未能解决你的问题,请参考以下文章

避免linux + QT中的屏幕撕裂

防止 OpenGL 缓冲帧

intel集成显卡开opengl模式卡

英特尔 GMA 4500HD 和垂直同步 [关闭]

如何在qt中执行opengl

OpenGL浮点精度