3

LWJGL 2.8.5 を使用して 3D 視覚化アプリケーションを開発しています。プロジェクトのホームページから最初のチュートリアルを読んだ後、OpenGL の本を読んで分析を深めました。OpenGL での一般的な手順は、init 関数でシーンを描画し、表示の更新をループで呼び出すだけです。

ただし、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);
    }
}
4

3 に答える 3

5

デフォルトでは、OpenGLはフロントバッファとバックバッファの2つのバッファを維持します。フロントバッファは表示されるものであり、バックバッファは描画するものです。これはダブルバッファリングと呼ばれ、部分的にレンダリングされたシーンがユーザーに表示されないようにします。Display.update()を呼び出すたびに、lwjglはopenglに、表示のためにバックバッファーの内容をフロントバッファーに移動するように指示します。これは、データをコピーするか、古いフロントバッファーを新しいバックバッファーとしてマークし、古いバックバッファーを新しいフロントバッファーとしてマークすることによって行われます(バッファーは交換されます)。

これは通常の状況を対象としています。たとえば、1秒間に60回のような新しいものを描きたいゲームなどです。これで、実際には何も描画せずにDisplay.update()を呼び出した場合(たとえば、Inputを取得するため)、バッファーは移動されます。これがどのように実装されているかに応じて、古い画像が表示されるか(データが単純にコピーされた場合、バックバッファーにはレンダリングしたデータが含まれているため)、バッファー間で各Display.update()を交互に使用します。描画され、レンダリング時にフロントバッファであったバッファ(おそらくちょうど黒)。これにより、画像が1フレームおき(元のバックバッファ)にのみ表示され、1フレームおきに黒の元のフロントバッファが表示されるため、ちらつきが発生します。

一部のドライバー実装(Nvidiaなど)では、Windowsはアクティブなフロントバッファーを描画することもできるようです。そのため、シーンを再描画しない場合は、閉じた後でもダイアログボックスが表示されます。

これに対する最もクリーンな解決策は、initでテクスチャ(OpenGL 1.1以降で機能)またはレンダーバッファ(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

すべてのフレームを呼び出すdrawメソッドは、フレームバッファ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-24T02:08:40.660 に答える
0

ループ内で Display.sync(60) (画面レートを 60FPS に下げる LWJGL コマンド) を使用し、レンダリングの最後で Display.update() を使用する必要があります。Display.update() により、バックグラウンド バッファーとフォアグラウンド バッファーがスワップされるため、画像自体が構築されていることがわかりません (これが原因でちらつきが発生する可能性があります)。

于 2013-01-21T08:59:49.917 に答える