我正在使用LWJGL 2.8.5开发一个3D可视化应用程序。在阅读了项目主页上的第一个教程后,我还阅读了一本OpenGL书籍,深入分析了这一点。我看到OpenGL中的典型过程是在初始化函数中绘制场景,然后在循环中简单地调用显示的更新。
然而,当我尝试使用LWJGL时,我在显示器上得到了闪烁的效果。消除闪烁的唯一方法是在显示更新周期中重新绘制场景。为什么会发生这种情况?
为了更好地解释我的问题,我创建了一个简单的类来再现这个问题。它只是在屏幕中心绘制一个四边形,然后进入无休止的屏幕更新循环。
请注意,如果我在循环中取消对绘制调用的注释,则闪烁将消失所有工作。为什么?
我的期望是只画一次对象,然后移动相机来获得静态场景的不同视图,这有什么错吗?
下面是代码:
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);
}
}发布于 2013-01-24 10:08:41
默认情况下,OpenGL维护两个缓冲区:一个前台缓冲区和一个后台缓冲区。前台缓冲区是显示的内容,而后台缓冲区是您在其上绘制的内容。这称为双缓冲,可防止向用户显示部分渲染的场景。无论何时调用Display.update(),lwjgl都会告诉opengl将后台缓冲区的内容移到前台缓冲区进行显示。这可以通过复制数据来完成,或者通过将旧的前台缓冲区标记为新的后台缓冲区并将旧的后台缓冲区标记为新的前台缓冲区(缓冲区被交换)来完成。
这是针对正常情况的,例如,在游戏中,你想要每秒绘制60次新的东西。现在,如果你没有实际绘制任何东西,但仍然调用Display.update() (例如获取输入),缓冲区仍然是移动的。现在,根据实现方式,要么仍然看到旧图像(如果只是复制数据,因为后台缓冲区仍然包含渲染的数据),要么在已绘制的缓冲区和渲染时作为前台缓冲区的缓冲区(可能只是黑色)之间交替使用每个Display.update()。这会导致您看到的闪烁,因为图像每隔一帧(原始后台缓冲区)才显示一次,而黑色原始前端缓冲区则每隔一帧显示一次。
使用一些驱动程序实现(例如来自Nvidia),Windows似乎甚至可以在活动的前台缓冲区上绘制,因此如果您不重绘场景,对话框即使在关闭之后也可以显示出来。
这确实是OpenGL设计者为这类事情想出的解决方案。
编辑:要么在上面的代码中取消对draw方法的注释,要么在由于渲染时间而没有选项的情况下,必须渲染到纹理。使用OpenGL 3或更高版本时,您需要在初始化中添加类似以下内容:
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);发布于 2013-01-21 16:59:50
你应该在循环中使用Display.sync(60) (LWJGL命令,它将屏幕速率降低到60FPS),并在渲染结束时使用Display.update()。Display.update()将导致交换背景缓冲区和前景缓冲区,因此您看不到图片本身的构建(这可能是导致闪烁的原因)。
发布于 2017-05-26 04:08:02
在更新显示之前,您没有清除屏幕。在GL11.glBegin(GL11.GL_QUADS);之前添加GL11.glClear(GL11.GL_COLOR_BUFFER_BIT);
https://stackoverflow.com/questions/14435085
复制相似问题