0
\$\begingroup\$

I'm new to OpenGL and LWJGL & I have a question about rendering using VBOs.

I rendering 50*50 objects using VBOs. Each object has its own render method where binding of the position, rotation.

The problem is I have a low frame rate (~10fps) even though my objects (for now) consist of simple quads. Why is my framerate so low?

Later I want to use much more complex objects and other computations...

Code:

public class NGINE2 {
static int w = 640;
static int h = 480;
static float oldX = 0;
static float oldY = 0;
static float oldZ = 0;
static List<Triangle> triangles = new ArrayList<Triangle>();

public static void main(String[] args) throws Exception {
    // System.out.println("Hello world from OpenGL!");

    Random rand = new Random();
    for (int x = 0; x < 50; x++) {
        for (int z = 0; z < 50; z++) {
            float rgb[] = new float[3];
            rgb[0] = rand.nextFloat();
            rgb[1] = rand.nextFloat();
            rgb[2] = rand.nextFloat();
            triangles.add(new Triangle(new Vector3f(x, 1, z), new Vector3f(+0.5f, 1f, -0.5f),
                    new Vector3f(-0.5f, 1f, -0.5f), new Vector3f(+0.5f, 1f, +0.5f), rgb, rgb, rgb));
            triangles.add(new Triangle(new Vector3f(x, 1, z), new Vector3f(-0.5f, 1f, +0.5f),
                    new Vector3f(-0.5f, 1f, -0.5f), new Vector3f(+0.5f, 1f, +0.5f), rgb, rgb, rgb));
        }
    }

    initContext();
    renderLoop();

    int height = 500;
    int width = 500;
    double value[][] = new double[500][500];
    for (int y = 0; y < height; y++) {
        for (int x = 0; x < width; x++) {
            double nx = x / width - 0.5, ny = y / height - 0.5;
            value[y][x] = SimplexNoise.noise(nx, ny);
        }
    }
}

static void initContext() throws Exception {
    Camera.create();

    w = 640;
    h = 480;

    Display.setDisplayMode(new DisplayMode(w, h));
    Display.setFullscreen(false);
    Display.create();
    glViewport(0, 0, w, h);
}

static void renderLoop() {
    while (!Display.isCloseRequested()) {
        preRender();
        render();

        Display.update();
        Display.sync(300 /* desired fps */);
    }

    Display.destroy();
}

static void preRender() {

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    GLU.gluPerspective(45.0f, ((float) w / (float) h), 0.1f, 100.0f); // fix na błąd z kamerą

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}



static void drawVBOtriangle(float s1[], float s2[], float s3[]) {
    // create geometry buffers
    FloatBuffer cBuffer = BufferUtils.createFloatBuffer(9);
    cBuffer.put(s1[3]).put(s1[4]).put(s1[5]);
    cBuffer.put(s2[3]).put(s2[4]).put(s2[5]);
    cBuffer.put(s3[3]).put(s3[4]).put(s3[5]);
    cBuffer.flip();

    FloatBuffer vBuffer = BufferUtils.createFloatBuffer(9);
    vBuffer.put(s1[0]).put(s1[1]).put(s1[2]);
    vBuffer.put(s2[0]).put(s2[1]).put(s2[2]);
    vBuffer.put(s3[0]).put(s3[1]).put(s3[2]);
    vBuffer.flip();

    IntBuffer ib = BufferUtils.createIntBuffer(2);

    glGenBuffersARB(ib);
    int vHandle = ib.get(0);
    int cHandle = ib.get(1);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vHandle);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, vBuffer, GL_STATIC_DRAW_ARB);
    glVertexPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, cHandle);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, cBuffer, GL_STATIC_DRAW_ARB);
    glColorPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

    glDrawArrays(GL_TRIANGLES, 0, 3 /* elements */);

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);

    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);

    // cleanup VBO handles
    ib.put(0, vHandle);
    ib.put(1, cHandle);
    glDeleteBuffersARB(ib);
}

static void render() {
    glClearColor(1, 1, 1, 1);

    Camera.acceptInput(0.6f);
    Camera.apply();

    for (int i = 0; i < triangles.size(); i++) {
        // rysowanie trójkąta z modów...
        float s1[] = new float[6];
        s1[0] = triangles.get(i).getFirstModPoint().getX();
        s1[1] = triangles.get(i).getFirstModPoint().getY();
        s1[2] = triangles.get(i).getFirstModPoint().getZ();
        s1[3] = triangles.get(i).getFirstColor()[0];
        s1[4] = triangles.get(i).getFirstColor()[1];
        s1[5] = triangles.get(i).getFirstColor()[2];
        float s2[] = new float[6];
        s2[0] = triangles.get(i).getSecondModPoint().getX();
        s2[1] = triangles.get(i).getSecondModPoint().getY();
        s2[2] = triangles.get(i).getSecondModPoint().getZ();
        s2[3] = triangles.get(i).getSecondColor()[0];
        s2[4] = triangles.get(i).getSecondColor()[1];
        s2[5] = triangles.get(i).getSecondColor()[2];
        float s3[] = new float[6];
        s3[0] = triangles.get(i).getThirdModPoint().getX();
        s3[1] = triangles.get(i).getThirdModPoint().getY();
        s3[2] = triangles.get(i).getThirdModPoint().getZ();
        s3[3] = triangles.get(i).getThirdColor()[0];
        s3[4] = triangles.get(i).getThirdColor()[1];
        s3[5] = triangles.get(i).getThirdColor()[2];
        drawVBOtriangle(s1, s2, s3);
        glTranslatef(triangles.get(i).getStartPoint().x - oldX, triangles.get(i).getStartPoint().y - oldY, triangles.get(i).getStartPoint().z - oldZ);
        oldX = triangles.get(i).getStartPoint().x;
        oldY = triangles.get(i).getStartPoint().y;
        oldZ = triangles.get(i).getStartPoint().z;
    }
}

public static Vector3f getDirection() {
    Vector3f vector = new Vector3f();

    float rotX = Camera.getRotationX(); // pitch
    float rotY = Camera.getRotationY(); // yaw

    vector.y = (float) -Math.sin(Math.toRadians(rotY));

    float h = (float) Math.cos(Math.toRadians(rotY));

    vector.x = (float) (h * Math.sin(Math.toRadians(rotX)));
    vector.z = (float) (-h * Math.cos(Math.toRadians(rotX)));

    return vector;
}

} enter image description here

EDIT: I rewrited the code and now i have ~250fps in 100x100 terrain. What should i did with coide to get more fps?

New code:

public class NGINE2 {
public static int MAX_PICKING_DISTANCE = 20;
public static int RAY_RADIUS = 5;
static int w = 640;
static int h = 480;
static float oldX = 0;
static float oldY = 0;
static float oldZ = 0;
static List<Triangle> triangles = new ArrayList<Triangle>();
static double[][]map;

static FloatBuffer cBuffer = BufferUtils.createFloatBuffer(9*(100*100*2));
static FloatBuffer vBuffer = BufferUtils.createFloatBuffer(9*(100*100*2));

public static void main(String[] args) throws Exception {
    NoiseHeightMap nsh = new NoiseHeightMap(100, 23495876);
    nsh.HeightmapPNG(nsh.getHeightmap(), "C:\\Users\\Przemo\\Desktop\\mapka.png");
    map = nsh.getHeightmap();
    Random rand = new Random();
    for (int x = 0; x < 100; x++) {
        for (int z = 0; z < 100; z++) {
            float rgb[] = new float[3];
            rgb[0] = rand.nextFloat();
            rgb[1] = rand.nextFloat();
            rgb[2] = rand.nextFloat();
            triangles.add(new Triangle(new Vector3f(x, (float) map[x][z]*100, z), new Vector3f(+0.5f, 1f, -0.5f),
                    new Vector3f(-0.5f, 1f, -0.5f), new Vector3f(+0.5f, 1f, +0.5f), rgb, rgb, rgb));
            triangles.add(new Triangle(new Vector3f(x, (float) map[x][z]*100, z), new Vector3f(-0.5f, 1f, +0.5f),
                    new Vector3f(-0.5f, 1f, -0.5f), new Vector3f(+0.5f, 1f, +0.5f), rgb, rgb, rgb));
        }
    }

    initContext();
    renderLoop();
}

static void initContext() throws Exception {
    Camera.create();

    w = 640;
    h = 480;

    Display.setDisplayMode(new DisplayMode(w, h));
    Display.setFullscreen(false);
    Display.create();
    glViewport(0, 0, w, h);
}

static void renderLoop() {
    long time = 0;
    int frames  = 0;

    for (int i = 0; i < triangles.size(); i++) {
        cBuffer.put(triangles.get(i).firstColor[0]).put(triangles.get(i).firstColor[1]).put(triangles.get(i).firstColor[2]);
        cBuffer.put(triangles.get(i).secondColor[0]).put(triangles.get(i).secondColor[1]).put(triangles.get(i).secondColor[2]);
        cBuffer.put(triangles.get(i).thirdColor[0]).put(triangles.get(i).thirdColor[1]).put(triangles.get(i).thirdColor[2]);

        vBuffer.put(triangles.get(i).firstModPoint.x).put(triangles.get(i).firstModPoint.y).put(triangles.get(i).firstModPoint.z);
        vBuffer.put(triangles.get(i).secondModPoint.x).put(triangles.get(i).secondModPoint.y).put(triangles.get(i).secondModPoint.z);
        vBuffer.put(triangles.get(i).thirdModPoint.x).put(triangles.get(i).thirdModPoint.y).put(triangles.get(i).thirdModPoint.z);
    }
    cBuffer.flip();
    vBuffer.flip();
    IntBuffer ib = BufferUtils.createIntBuffer(2);

    glGenBuffersARB(ib);
    int vHandle = ib.get(0);
    int cHandle = ib.get(1);

    glEnableClientState(GL_VERTEX_ARRAY);
    glEnableClientState(GL_COLOR_ARRAY);

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, vHandle);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, vBuffer, GL_STATIC_DRAW_ARB);
    glVertexPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

    glBindBufferARB(GL_ARRAY_BUFFER_ARB, cHandle);
    glBufferDataARB(GL_ARRAY_BUFFER_ARB, cBuffer, GL_STATIC_DRAW_ARB);
    glColorPointer(3, GL_FLOAT, /* stride */3 << 2, 0L);

    while (!Display.isCloseRequested()) {


        preRender();
        render();

        Display.update();
        Display.sync(6000 /* desired fps */);

        // Calc FPS
         frames++;
         if( ( System.currentTimeMillis() - time ) >= 1000 ) {
            Display.setTitle(" FPS: " + (int)((frames * 1000)/( System.currentTimeMillis() - time )) );
            time = System.currentTimeMillis();
            frames = 0;
         }
    }

    glDeleteBuffersARB(ib);
    glBindBufferARB(GL_ARRAY_BUFFER_ARB, 0);
    glDisableClientState(GL_COLOR_ARRAY);
    glDisableClientState(GL_VERTEX_ARRAY);
    ib.put(0, vHandle);
    ib.put(1, cHandle);
    Display.destroy();
}

static void preRender() {

    glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);

    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();

    GLU.gluPerspective(45.0f, ((float) w / (float) h), 0.1f, 100.0f); // fix na błąd z kamerą

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
}

static void render() {
    glClearColor(1, 1, 1, 1);

    Camera.acceptInput(0.6f);
    Camera.apply();

    int Count = 2*100*100;
    for(int Index = 0; Index < Count; Index++) {
        glDrawArrays(GL_TRIANGLES, Index*3, 3);
        glTranslatef(triangles.get(Index).getStartPoint().x - oldX, triangles.get(Index).getStartPoint().y - oldY, triangles.get(Index).getStartPoint().z - oldZ);
        oldX = triangles.get(Index).getStartPoint().x;
        oldY = triangles.get(Index).getStartPoint().y;
        oldZ = triangles.get(Index).getStartPoint().z;
    }
}

}

\$\endgroup\$
  • \$\begingroup\$ Did you profile and made sure your rendering was the issue? \$\endgroup\$ – Vaillancourt Oct 11 '17 at 10:59
  • \$\begingroup\$ Regarding your edit: SE sites are not discussion forums. If you wish to ask a separate question, post it as a new question. \$\endgroup\$ – Maximus Minimus Oct 11 '17 at 13:50
2
\$\begingroup\$

This is not how you are supposed to use VBOs and you will likely get better performance from immediate mode (glBegin/glEnd).

There are a number of things seriously wrong with your code, including:

  • You have a separate VBO for each triangle.
  • You update the VBOs each frame whether they need it or not.
  • You create and destroy the VBOs each frame.
  • You unbind and disable state after each draw.

All of this has the end result of pushing the full data set to the GPU each frame, as well as dealing with object creation/destruction, as well as dealing with a large number of state changes.

Hence the fact that it's running slow.

Unfortunately you do sometimes see VBOs advertised as a general case performance optimization without making mention of the fact that you need to design your usage of them correctly otherwise it will likely end up worse.

Fixing this is going to require something of a rewrite. I can't do that for you, so here are some pointers in the correct direction.

  • Stop creating objects at run time. This includes Java objects as well as OpenGL objects. Create them one-time-only at startup time instead, then reuse them at run time.

  • Instead of a separate small VBO for each triangle create a single big VBO and put all of your triangles into it. Yes, it uses more memory, but that's a good thing.

  • Use the first and count parameters of glDrawArrays to select a subrange of the VBO to draw - that's what they're there for. Alternatively if drawing the entire VBO at once is what you need, then do so - fewer draw calls gives higher performance.

  • Use glBufferSubData to change data in an existing VBO rather than destroying it and creating a new one.

  • Stop unbinding and disabling state after drawing. You're destroying any state change filtering or batching that the driver might be able to do to help you run fast.

The general theme here is that instead of generating and drawing triangles at run time, doing a lot of work each frame, you instead shift the work to startup time and do it one-time-only. This lets you reuse previously generated data and make fewer, more lightweight OpenGL calls at run time, and this is where performance comes from.

Good luck!

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.