【问题标题】:Removal of OpenGL rubber banding artefacts去除 OpenGL 橡皮筋伪影
【发布时间】:2012-01-23 02:59:55
【问题描述】:

我正在使用一些用于科学可视化的 OpenGL 代码,但在使其橡皮筋在较新的硬件上工作时遇到了问题。代码在现有场景上绘制一个“缩放窗口”,其中“缩放窗口”的一个角在存储的左键单击位置,另一个在鼠标移动时位于鼠标下方。在第二个左键单击场景放大到选定的窗口。

当鼠标在场景中移动时我看到的症状是:

  1. 出现的橡胶带伪影是用于创建“缩放窗口”的行,而第二个“RenderLogic”通道没有从缓冲区中删除(请参见下面的代码)
  2. 我可以清楚地看到前一个缓冲区的内容随着缓冲区的交换而闪烁和消失

上述问题不会发生在低端硬件上,例如我拥有的上网本上的集成显卡。另外,我不记得大约 5 年前编写此代码时的这个问题。

以下是相关的代码部分,精简后专注于相关的 OpenGL:

// Called by every mouse move event
// Makes use of current device context (m_hDC) and rendering context (m_hRC)
void MyViewClass::DrawLogic()
{
    BOOL bSwapRv = FALSE;

    // Make the rendering context current
    if (!wglMakeCurrent(m_hDC, m_hRC))
        // ... error handling

    // Perform the logic rendering
    glLogicOp(GL_XOR);
    glEnable(GL_COLOR_LOGIC_OP);
    // Draws the rectangle on the buffer using XOR op
    RenderLogic();
    bSwapRv = ::SwapBuffers(m_hDC);
    // Removes the rectangle from the buffer via a second pass
    RenderLogic();
    glDisable(GL_COLOR_LOGIC_OP);

    // Release the rendering context
    if (!wglMakeCurrent(NULL, NULL))
        // ... error handling
}

void MyViewClass::RenderLogic(void)
{
    glLineWidth(1.0f);
    glColor3f(0.6f,0.6f,0.6f);
    glEnable(GL_LINE_STIPPLE);
    glLineStipple(1, 0x0F0F);
    glBegin(GL_LINE_LOOP);
        // Uses custom "Point" class with Coords() method returning double*
        // Draw rectangle with corners at clicked location and current location
        glVertex2dv(m_pntClickLoc.Coords());
        glVertex2d(m_pntCurrLoc.X(), m_pntClickLoc.Y());
        glVertex2dv(m_pntCurrLoc.Coords());
        glVertex2d(m_pntClickLoc.X(), m_pntCurrLoc.Y());
    glEnd();
    glDisable(GL_LINE_STIPPLE);
}

// Setup code that might be relevant to the buffer configuration
bool MyViewClass::SetupPixelFormat()
{
    PIXELFORMATDESCRIPTOR pfd = {
        sizeof(PIXELFORMATDESCRIPTOR),
        1,                      // Version number (?)
        PFD_DRAW_TO_WINDOW      // Format must support window
        | PFD_SUPPORT_OPENGL    // Format must support OpenGL
        | PFD_DOUBLEBUFFER,     // Must support double buffering
        PFD_TYPE_RGBA,          // Request an RGBA format
        32,                     // Select a 32 bit colour depth
        0, 0, 0, 0, 0, 0,       // Colour bits ignored (?)
        8,                      // Alpha buffer bits
        0,                      // Shift bit ignored (?)
        0,                      // No accumulation buffer
        0, 0, 0, 0,             // Accumulation bits ignored
        16,                     // 16 bit Z-buffer
        0,                      // No stencil buffer
        0,                      // No accumulation buffer (?)
        PFD_MAIN_PLANE,         // Main drawing layer
        0,                      // Number of overlay and underlay planes
        0, 0, 0                 // Layer masks ignored (?)
    };
    PIXELFORMATDESCRIPTOR chosen_pfd;

    memset(&chosen_pfd, 0, sizeof(PIXELFORMATDESCRIPTOR));
    chosen_pfd.nSize = sizeof(PIXELFORMATDESCRIPTOR);

    // Find the closest match to the pixel format
    m_uPixelFormat = ::ChoosePixelFormat(m_hDC, &pfd);

    // Make sure a pixel format could be found
    if (!m_uPixelFormat)
        return false;
    ::DescribePixelFormat(m_hDC, m_uPixelFormat, sizeof(PIXELFORMATDESCRIPTOR), &chosen_pfd);

    // Set the pixel format for the view
    ::SetPixelFormat(m_hDC, m_uPixelFormat, &chosen_pfd);

    return true;
}

任何关于如何移除伪影的指示将不胜感激。

@Krom - 下图

【问题讨论】:

  • 可以发截图吗?

标签: c++ windows opengl rubber-band


【解决方案1】:

使用 OpenGL,如果有任何变化,则重新绘制整个视口是规范的。考虑一下:现代系统以超过 30 FPS 的速度绘制复杂场景的动画。

但我明白,您可能希望避免这种情况。所以通常的方法是在绘制第一个橡皮筋之前先复制纹理中的前端缓冲区。然后为每个橡皮筋重绘“重置”图像,方法是使用纹理绘制一个填充四边形的帧缓冲区。

【讨论】:

  • “如果有任何变化,则重绘整个视口”——这也是我的建议,因为它毫无疑问。 a^b^b != a 仍然令人惊叹。上面的代码看起来真的应该工作。除非 Point::Coords 不是 const 函数,否则 OP 忘了提到其他一些线程修改了例如m_pntCurrLoc...
  • 感谢重绘整个场景的建议 - 我同意这些天不需要 XOR,我可能需要做一些重构来删除逻辑操作。我试图通过尽可能少地修改旧代码来避免这种情况。
  • @Damon - 是的,它看起来应该可以工作......而且它在我们的带有英特尔集成显卡的机器上工作得很好,只是在我们的 ATI 或 NVIDIA 卡上不行!这让我想可能会有一些三重缓冲或一些时髦的事情发生。我的 OpenGL 是可行的,但远非先进。我可以进行更多试验,或者只是重构以删除 XOR 操作。
  • @Damon:有很多事情可能会搞乱 XORing 技巧。例如,抗锯齿完全破坏了它。伽马校正、色彩管理等东西,即所有非线性都会使其不起作用。
  • 修复了一些相对简单的重构,以在渲染场景上绘制橡皮筋。如果用户反馈表明需要更多性能,则会按照@datenwolf 的建议将前端缓冲区复制到纹理。
【解决方案2】:

我知道我要发布一个半年前的问题,但以防其他人遇到这个问题。

我自己也遇到过这种情况,因为您试图从错误的缓冲区中删除行。例如,您在缓冲区 A 调用 swapBuffer 上绘制矩形,然后尝试从缓冲区 B 中删除矩形。您想要做的是跟踪 2 个“缩放窗口”矩形,同时为缓冲区 A 绘制一个矩形和一个对于缓冲区 B,然后跟踪哪个是最新的。

【讨论】:

    【解决方案3】:

    如果您使用 Vista/7 和 Aero,请尝试切换到 Classic 主题。

    【讨论】:

    • 感谢您的建议 - 我刚刚尝试切换,人工制品仍然存在。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-30
    • 1970-01-01
    • 2013-12-24
    • 2019-09-05
    • 2014-06-15
    • 2011-01-28
    相关资源
    最近更新 更多