Saturday, June 12, 2010

glDrawPixels + Qt (Part 3)

glDrawPixels + Qt (Part 3)

In this part, we will discuss the use of a pixel buffer in OpenGL.  A pixel buffer is an advanced feature, which is aimed at increasing screen draw rate.
For OpenGL 2.1, this pixel buffer is a part of a standard extension and not in OpenGL core. 

To use this extension, things become much more complicated, but can still be explained step by step, as follows.
  1. We need to include <GL/glew.h> before any other OpenGL context.  In Qt, we can normally include it in a header file before we include any QGLWidget.
    For your information, this extension is OpenGL Extension Wrangler Library (GLEW).

  2. Since GLEW is a library, we have to declare its use in a .pro file.  Open a .pro file and add a line

    LIBS    += -lGLEW


    Your project can now link to this library properly.

  3. Initialize GLEW before any other OpenGL work.  This should be done in the initializeGL method of QGLWidget.
    Initializing GLEW, however, may fail, especially if we try to initialize it for the second time.
    Thus, it is recommended to prepare for initialization error, as shown below.

    GLenum initStatus = glewInit();
    if (initStatus != GLEW_OK) {
        cout << "Cannot initialize glew." << endl;
        exit(1);
    } else
        cout << "glew is successfully initialized." << endl;


  4. Bind data to a buffer.  We need to provide a non-local GLuint to be employed as a buffer ID.  This may be a class-member or global variable.
    Assume that we declare the ID variable as a class member by

    GLuint pixelBuffer;

    Next, bind the buffer to it.  Note that chkImage contains pixel data and must be already filled.
    These patch of code is still in initializeGL.

    glGenBuffers(1, &pixelBuffer);
    glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer);
    glBufferData(GL_PIXEL_UNPACK_BUFFER, 3* m_nWidth * m_nHeight, chkImage,
            GL_STATIC_DRAW);

  5. Draw pixels in paintGL by

    GLubyte* nBuffOffset = 0;
    glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, nBuffOffset);


    Note that instead of providing a pointer to chkImage, we provide an offset in a buffer.  The zero offset means that we are going to use the first byte of the buffer and so on.

    To make this matter more understandable in terms of actual implementation, methods for initialization and painting are shown below.

    void GLImage2_2::initializeGL()
    {
        GLenum initStatus = glewInit();
        if (initStatus != GLEW_OK) {
            cout << "Cannot initialize glew." << endl;
            exit(1);
        } else
            cout << "glew is successfully initialized." << endl;

        // Set coordinate system
        glOrtho(0, m_nWidth, 0, m_nHeight, 0, 1);

        // Basic initialization
        glClearColor(0.0, 0.0, 0.0, 0.0);
        glShadeModel(GL_FLAT);
        glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

        // Using buffer objects
        glGenBuffers(1, &pixelBuffer);
        glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer);
        glBufferData(GL_PIXEL_UNPACK_BUFFER, 3* m_nWidth * m_nHeight, chkImage,
                GL_STATIC_DRAW);
    }




    void GLImage2_2::paintGL() {
        glClear(GL_COLOR_BUFFER_BIT);
        glRasterPos2i(0, 0);

        GLubyte* nBuffOffset = 0;
        glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, nBuffOffset);
    }


So, what if we have several buffers?  In that case, we have to prepare more buffer-ID variables, bind them, and populate them. 
At the painting time, we specify our target buffer.  For concreateness, see the code below.

void GLImage2_2::paintGL() {
    glClear(GL_COLOR_BUFFER_BIT);
    glRasterPos2i(0, 0);

    if (target = 0)
        
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer1);
    else
        
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pixelBuffer2);

    GLubyte* nBuffOffset = 0;
    glDrawPixels(m_nWidth, m_nHeight, GL_RGB, GL_UNSIGNED_BYTE, nBuffOffset);
}

===================================

An example Eclipse CDT / Qt project is stored at a public folder here.
The project file is valid for Eclipse CDT on Linux with Qt integration (Ubuntu 9.04 Juanty), but the main part should be applicable to most C++ environment.


Pinyo Taeprasartsit
September 2009

No comments: