2

I'm working with some OpenGL code for scientific visualization and I'm having issues getting its rubber banding working on newer hardware. The code is drawing a "Zoom Window" over an existing scene with one corner of the "Zoom Window" at the stored left-click location, and the other under the mouse as it is moved. On the second left-click the scene zooms into the selected window.

The symptoms I am seeing as the mouse is moved across the scene are:

  1. Rubber banding artefacts appearing which are the lines used to create the "Zoom Window" not being removed from the buffer by the second "RenderLogic" pass (see code below)
  2. I can clearly see the contents of the previous buffer flashing up and disappearing as the buffers are swapped

The above problem doesn't happen on low end hardware such as the integrated graphics on a netbook I have. Also, I can't recall this problem ~5 years ago when this code was written.

Here are the relevant code sections, trimmed down to focus on the relevant 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;
}

Any pointers on how to remove the artefacts will be much appreciated.

@Krom - image below

Artefacts from Zoom Window

mcdave
  • 2,550
  • 17
  • 22

3 Answers3

2

With OpenGL it's canonical to redraw the whole viewport if just anything changes. Consider this: Modern system draw animates complex scenes at well over 30 FPS.

But I understand, that you may want to avoid this. So the usual approach is to first copy the frontbuffer in a texture, before drawing the first rubberband. Then for each rubberband redraw "reset" the image by drawing a framebuffer filling quad with the texture.

datenwolf
  • 159,371
  • 13
  • 185
  • 298
  • 1
    "redraw the whole viewport if just anything changes" -- that's what I'd suggest too, simply because it leaves no doubts. Still it's stunning that `a^b^b != a`. The code above looks like it really _should_ work. Unless `Point::Coords` is not a `const` function or the OP forgot to mention that some other thread modifies e.g. `m_pntCurrLoc`... – Damon Jan 23 '12 at 10:59
  • Thanks for the suggestion of redrawing the whole scene - I agree the XOR isn't necessary these days and I may have to do a bit of a refactor to remove the logic op. I was trying to avoid this by getting the old code to work with as few modifications as possible. – mcdave Jan 23 '12 at 11:03
  • @Damon - yes, it does look like it should work ... and it does work perfectly on our machines with intel integrated graphics, just not on our ATI or NVIDIA cards! This has me thinking there might be some triple buffering or something funky going on. My OpenGL is workable however far from advanced. I could either experiment more or just refactor to remove the XOR operation. – mcdave Jan 23 '12 at 11:06
  • 2
    @Damon: There are a lot of things that may mess up the XORing trick. antialiasing for example completely trashes it. Things like gamma correction, color management, i.e. everything nonlinear will make it not work. – datenwolf Jan 23 '12 at 11:12
  • Fixed by some relatively simple refactoring to draw the rubber banding over the rendered scene. Will copy the front buffer to a texture as suggested by @datenwolf if user feedback indicates more performance is needed. – mcdave Jan 24 '12 at 00:13
1

I know I'm posting to a year and half old question but in case anyone else comes across this.

I've had this happen to me myself is because you are trying to remove the lines off of the wrong buffer. For example you draw your rectangle on buffer A call swapBuffer and then try to remove the rectangle off of buffer B. What you would want to do is keep track of 2 "zoom window" rectangles while your doing the drawing one for buffer A and one for buffer B and then keep track of which one is the most recent.

kaiken
  • 77
  • 7
0

If you're using Vista/7 and Aero, try switching to the Classic theme.

genpfault
  • 51,148
  • 11
  • 85
  • 139