当前位置: 首页 > 知识库问答 >
问题:

LWJGL - 渲染周期上的屏幕闪烁

皇甫逸清
2023-03-14

我正在使用LWJGL 2.8.5开发一个3D可视化应用程序。在阅读了项目主页上的第一个教程后,我还阅读了一本OpenGL书籍,进行了更深入的分析。我看到OpenGL中的典型过程是在init函数中绘制场景,然后简单地在一个循环中调用显示的更新。

但是,当我尝试使用LWJGL时,我在显示屏中得到了闪烁效果。消除闪烁的唯一方法是在显示更新周期中重绘场景。为什么会发生这种情况?

为了更好地解释我的问题,我创建了一个简单的类来重现这个问题。它只需在屏幕中心绘制一个四边形,然后进入无休止的屏幕更新循环。

注意,如果我在循环中取消对draw调用的注释,那么闪烁就会消失,一切正常。为什么?

我期望只画一次对象,然后移动相机以获得静态场景的不同视图,这有什么错吗?

这是代码:

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个答案

宫元徽
2023-03-14

在更新显示之前,您没有清除屏幕。添加< code>GL11.glClear(GL11。GL _ COLOR _ BUFFER _ BIT);在< code>GL11.glBegin(GL11。GL _ QUADS);

华俊贤
2023-03-14

您应该在循环中使用Display.sync(60) (LWJGL命令,将屏幕速率降低到60FPS)并在渲染结束时使用Display.update()。Display.update()将使交换背景缓冲区和前景缓冲区,这样你就看不到图片本身在累积(这可能是你的闪烁造成的)。

仲孙默
2023-03-14

默认情况下,OpenGL维护两个缓冲区:一个前缓冲区和一个后缓冲区。前缓冲区是显示的内容,而后缓冲区是您绘制的内容。这称为双缓冲区,可以防止部分渲染的场景显示给用户。每当您调用Display.update()时,lwjgl会告诉opengl将后缓冲区的内容移动到前缓冲区以供显示。这可以通过复制数据来完成,或者通过将旧的前缓冲区标记为新的后缓冲区,将旧的后缓冲区标记为新的前缓冲区(缓冲区被交换)。

这是针对正常情况的,例如在游戏中,你想画一些新的东西,比如每秒画60次。现在,如果您实际上没有绘制任何内容,但仍然调用 Display.update() (例如,获取输入),缓冲区仍会移动。现在,根据实现方式,您仍然可以看到旧图像(如果数据只是简单地复制,因为您的后台缓冲区仍然包含您呈现的数据),或者您在绘制的缓冲区和渲染时作为前部缓冲区的缓冲区(可能只是黑色)之间交替使用每个 Display.update()。这会导致您看到的闪烁,因为图像仅每隔一帧显示一次(原始后缓冲区),并且每隔一帧就会看到黑色原始前缓冲区。

对于某些驱动程序实现(例如来自Nvidia),Windows似乎甚至可以在活动的html" target="_blank">前端缓冲区上绘制,因此即使您不重绘场景,对话框也可以显示并显示,即使在关闭后也是如此。

最干净的解决方案是在init中简单地渲染到纹理(自OpenGL 1.1开始工作)或renderbuffer(在OpenGL 3.0中引入),然后在每个帧渲染该纹理。这正是OpenGL设计师为这类事情所考虑的解决方案。

编辑:要么取消上述代码中draw方法的注释,要么在因为渲染时间而没有选择的情况下,必须渲染到纹理。使用OpenGL 3或更高版本,您需要在您的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

然后,调用每个帧的绘制方法将简化为帧缓冲区fbo的简单快速复制:

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);
 类似资料:
  • 我有一个问题,我认为它与屏幕渲染及其生命周期有关。基本上我有两个屏幕(菜单和游戏)。在GameScreen渲染方法中,我称之为World。更新,然后我的渲染。在(GameScreen的)隐藏方法中,我从Redner类中处理SpriteBatch。 因此,当我将屏幕从游戏更改为菜单(intheworld.update)时,Java崩溃。据我所知,飞机失事了。所以我的问题是,当我在渲染周期中间设置一个

  • 下面的LWJGL代码将在屏幕中心呈现一个蓝色正方形。相反,我得到了一个空白的白色屏幕。就像渲染根本不工作,或者我在屏幕外渲染。 我是OpenGL和LWJGL的新手,所以我力不从心。我浏览了所有内容,但似乎找不到任何可能与代码有关的问题。 OpenGLTest.scala VertexArray.scala

  • 因此,我正在制作一个游戏,在60秒结束时,我想让屏幕改变颜色并显示一些结束文本。我这样设置计时器: 屏幕上显示的很好,但是当结束屏幕出现时,它会在原来的白色屏幕和结束屏幕之间闪烁。由于某种原因,如果我在屏幕上挥动鼠标,它不会闪烁。 “end”是我的游戏中的文本变量 如果这有帮助的话,这也是我在结尾的部分 有没有一种方法可以使它稳定地出现,而不必更改计时器?

  • 我试图使用opengl在lwjgl显示器上显示自定义字体的文本。目前,我正在使用自定义位图字体类从png文件加载字体,并以与tileset相同的方式显示它们。它工作正常,但当我将文本绘制到屏幕上时,字符之间的间距完全不同,因为字母M比字母i宽得多,并且所有字符的宽度都不同。有没有办法检测每个字符的宽度?或者是否有任何特定于lwjgl字体的库?有一种方法可以使用slick2d来实现,但加载整个库只是

  • 我试图弄清楚为什么我不能用LWJGL 3渲染任何纹理。我尝试了多种加载(PNGDecoder、STB、BufferedImage)和渲染纹理的方法。但结果总是一个白色的四边形。 主要类: 加载方法: 渲染方法: ModelTexture类只是存储一些现在不使用的信息,blue.png是16x16的png文件。 这是我在启动程序时得到的输出:

  • 实现屏幕闪烁效果,有点类似拍照闪烁时的flash light。 [Code4App.com]