LWJGL - Screen flicker on render loop

I am developing a 3D visualization application using LWJGL 2.8.5. After reading the first lessons on the project homepage, I also dived into the analysis by reading the OpenGL book. I see that a typical routine in OpenGL draws the scene in the init function and then just calls the display update in a loop.

However, when I try to do this with LWJGL, I get a flickering effect on the display. The only way to eliminate flickering is to redraw the scene in the display refresh loop. Why is this happening?

To better explain my problem, I created a simple class that reproduces the problem. It just draws a square in the center of the screen, then it goes into an infinite screen refresh loop.

Note that if I uncomment the draw call in the loop then the flickering disappears, everything works. Why?

Is there something wrong with my expectation to only draw objects once and just move the camera to get a different view of the static scene?

Here is the code:

package test;
import org.lwjgl.LWJGLException;
import org.lwjgl.opengl.Display;
import org.lwjgl.opengl.DisplayMode;
import org.lwjgl.opengl.GL11;
import org.lwjgl.util.glu.GLU;

import static org.lwjgl.opengl.GL11.*;

public class DisplayTest 
{



public static void initGL()
{
    GL11.glViewport(0, 0, 640, 480);
    glMatrixMode(GL_PROJECTION);
    GLU.gluPerspective(45.0f, 640f/480f,0.1f, 100.0f); 

    draw();


}

public static void draw()
{
    glMatrixMode(GL_MODELVIEW);

    GL11.glLoadIdentity(); // Reset The Current Modelview Matrix
    GL11.glTranslatef(0, 0, -6.0f);//Place at the center at -6 depth units


    //Start drawing a quad
    //--------------------------------------------------
    GL11.glBegin(GL11.GL_QUADS); 
    int size=1;
    GL11.glColor3f(.3f, .5f, .8f); 

    GL11.glVertex3f(-size/2f,-size/2f,+size/2f);
    GL11.glVertex3f(+size/2f,-size/2f,+size/2f);
    GL11.glVertex3f(+size/2f,+size/2f,+size/2f);
    GL11.glVertex3f(-size/2f,+size/2f,+size/2f);
    glEnd();
}


public static void main(String[] args) 
{
    try 
    {
        // Sets the width of the display to 640 and the height to 480
        Display.setDisplayMode(new DisplayMode(640, 480));
        // Sets the title of the display 
        Display.setTitle("Drawing a quad");
        // Creates and shows the display
        Display.create();
    } 
    catch (LWJGLException e) 
    {
        e.printStackTrace();
        Display.destroy();
        System.exit(1);
    }

    initGL();

    // While we aren't pressing the red button on the display
    while (!Display.isCloseRequested()) 
    {
        //draw();

        // Update the contents of the display and check for input
        Display.update();
        // Wait until we reach 60 frames-per-second
        Display.sync(60);
    }
    // Destroy the display and render it invisible
    Display.destroy();
    System.exit(0);
    }
}

      

+3


source to share


3 answers


By default, OpenGL supports two buffers: a front buffer and a back buffer. The front buffer is what is displayed when the back buffer is what you have drawn. This is called double buffering and prevents partially rendered scenes from being shown to the user. Whenever you call Display.update (), lwjgl tells opengl to move the contents of the back buffer to the front buffer for display. This is done either by copying the data, or by marking the old front buffer as both the new back buffer and the old back buffer as the new front buffer (buffers swapped).

This is focused on a normal situation, for example. in games where you want to draw something new, like 60 times per second. Now, if you don't actually draw anything, but still call Display.update () (for example, to get an Input), the buffers are still being moved. Now, depending on how this is implemented, you still see your old image (if the data is just copied, because your back buffer still contains the data you displayed), or you alternate with each Display.update () in between the buffer you drew and the buffer that was the front buffer at the time you were displaying (which is probably just black). This results in the flickering you see as the image is only displayed every second frame (source buffer) and every other frame is viewed by the black source front buffer.

With some driver implementations (from Nvidia, for example), it seems like Windows can even draw an active front buffer, so dialog boxes can be displayed and displayed even after closing if you don't redraw the scene.

The cleanest solution is to just render a texture (works with OpenGL 1.1) or a render buffer (introduced in OpenGL 3.0) in your init, then render that texture every frame. This is really the solution that the OpenGL developers had in mind for this type of thing.



Edit . Either you uncomment the draw method in the above code, or in the absence of an option due to render time, you need to render the texture. Using OpenGL 3 or higher, you need to add something like this to your init:

fbo = GL30.glGenFramebuffers();
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, fbo);

rbo = GL30.glGenRenderbuffers();
GL30.glBindRenderbuffer(GL30.GL_RENDERBUFFER, rbo);
GL30.glRenderbufferStorage(GL30.GL_RENDERBUFFER, GL11.GL_RGBA8, 640, 480);
GL30.glFramebufferRenderbuffer(GL30.GL_FRAMEBUFFER, GL30.GL_COLOR_ATTACHMENT0, GL30.GL_RENDERBUFFER, rbo);

assert(GL30.glCheckFramebufferStatus(GL30.GL_FRAMEBUFFER) == GL30.GL_FRAMEBUFFER_COMPLETE);

GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, fbo);
GL20.glDrawBuffers(GL30.GL_COLOR_ATTACHMENT0);
GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

drawScene(); //draw here

      

your paint method to call each frame will be simplified to a simple and quick copy of the fbo framebuffer:

GL30.glBindFramebuffer(GL30.GL_READ_FRAMEBUFFER, fbo);
GL11.glReadBuffer(GL30.GL_COLOR_ATTACHMENT0);
GL30.glBindFramebuffer(GL30.GL_DRAW_FRAMEBUFFER, 0);
GL20.glDrawBuffers(GL11.GL_BACK_LEFT);

GL30.glBlitFramebuffer(0, 0, 640, 480, 0, 0, 640, 480, GL11.GL_COLOR_BUFFER_BIT, GL11.GL_NEAREST);
GL30.glBindFramebuffer(GL30.GL_FRAMEBUFFER, 0);

      

+4


source


You have to use Display.sync (60) (the LWJGL command that reduces the screen speed to 60FPS) in a loop and Display.update () at the end of rendering. Display.update () will overwrite the background buffer and the foreground buffer so you don't see the picture itself being created (possibly caused by your flickering).



0


source


You are not clearing the screen before updating the display. Add GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);

upGL11.glBegin(GL11.GL_QUADS);

0


source







All Articles