1

I am trying to display a mathematical surface f(x,y) defined on a XY regular mesh using OpenGL and C++ in an effective manner:

struct XYRegularSurface {
    double x0, y0;
    double dx, dy;
    int    nx, ny;
    XYRegularSurface(int nx_, int ny_) : nx(nx_), ny(ny_) {
       z = new float[nx*ny];
    }
    ~XYRegularSurface() {
       delete [] z;
    }
    float& operator()(int ix, int iy) {
       return z[ix*ny + iy];
    }

    float x(int ix, int iy) {
       return x0 + ix*dx;
    }
    float y(int ix, int iy) {
       return y0 + iy*dy;
    }
    float zmin(); 
    float zmax(); 
    float* z; 
 };

Here is my OpenGL paint code so far:

void color(QColor & col) {
    float r = col.red()/255.0f;
    float g = col.green()/255.0f;
    float b = col.blue()/255.0f;
    glColor3f(r,g,b);
}

void paintGL_XYRegularSurface(XYRegularSurface &surface, float zmin, float zmax) {
    float x, y, z;

    QColor col;
    glBegin(GL_QUADS);
    for(int ix = 0; ix < surface.nx - 1; ix++) {
        for(int iy = 0; iy < surface.ny - 1; iy++) {
            x = surface.x(ix,iy);
            y = surface.y(ix,iy);
            z = surface(ix,iy);
            col = rainbow(zmin, zmax, z);color(col);
            glVertex3f(x, y, z);

            x = surface.x(ix + 1, iy);
            y = surface.y(ix + 1, iy);
            z = surface(ix + 1,iy);
            col = rainbow(zmin, zmax, z);color(col);
            glVertex3f(x, y, z);

            x = surface.x(ix + 1, iy + 1);
            y = surface.y(ix + 1, iy + 1);
            z = surface(ix + 1,iy + 1);
            col = rainbow(zmin, zmax, z);color(col);
            glVertex3f(x, y, z);

            x = surface.x(ix, iy + 1);
            y = surface.y(ix, iy + 1);
            z = surface(ix,iy + 1);
            col = rainbow(zmin, zmax, z);color(col);
            glVertex3f(x, y, z);

        }
    }
    glEnd();
}

The problem is that this is slow, nx=ny=1000 and fps ~= 1. How do I optimize this to be faster?

EDIT: following your suggestion (thanks!) regarding VBO I added:

float* XYRegularSurface::xyz() {
    float* data = new float[3*nx*ny];
    long i = 0;
    for(int ix = 0; ix < nx; ix++) {
        for(int iy = 0; iy < ny; iy++) {
            data[i++] = x(ix,iy);
            data[i++] = y(ix,iy);
            data[i] = z[i]; i++;
        }
    }
    return data;
}

I think I understand how I can create a VBO, initialize it to xyz() and send it to the GPU in one go, but how do I use the VBO when drawing. I understand that this can either be done in the vertex shader or by glDrawElements? I assume the latter is easier? If so: I do not see any QUAD mode in the documentation for glDrawElements!?

Edit2: So I can loop trough all nx*ny quads and draw each by:

GL_UNSIGNED_INT indices[4];
// ... set indices
glDrawElements(GL_QUADS, 1, GL_UNSIGNED_INT, indices);

?

4

3 に答える 3

2

1/. Use display lists, to cache GL commands - avoiding recalculation of the vertices and the expensive per-vertex call overhead. If the data is updated, you need to look at client-side vertex arrays (not to be confused with VAOs). Now ignore this option...

2/. Use vertex buffer objects. Available as of GL 1.5.

Since you need VBOs for core profile anyway (i.e., modern GL), you can at least get to grips with this first.

于 2012-12-14T22:54:54.137 に答える
1

Well, you've asked a rather open ended question. I'd suggest using modern (3.0+) OpenGL for everything. The point of just about any new OpenGL feature is to provide a faster way to do things. Like everyone else is suggesting, use array (vertex) buffer objects and vertex array objects. Use an element array (index) buffer object too. Most GPUs have a 'post-transform cache', which stores the last few transformed vertices, but this can only be used when you call the glDraw*Elements family of functions. I also suggest you store a flat mesh in your VBO, where y=0 for each vertex. Sample the y from a heightmap texture in your vertex shader. If you do this, whenever the surface changes you will only need to update the heightmap texture, which is easier than updating the VBO. Use one of the floating point or integer texture formats for a heightmap, so you aren't restricted to having your values be between 0 and 1.

于 2012-12-14T23:21:54.363 に答える
1

If so: I do not see any QUAD mode in the documentation for glDrawElements!?

If you want quads make sure you're looking at the GL 2.1-era docs, not the new stuff.

于 2012-12-14T23:28:29.433 に答える