Рендеринг изображений мерцает, хотя 120 кадров в секунду

1

Мой игровой движок рисует черепичную карту на холсте внутри JFrame. Я использую Bufferstrategy, чтобы получить более плавный рендеринг и попытался повысить производительность в нескольких точках внутри программы и в итоге с ок. 120 кадров в секунду, чтобы заполнить окно 1920 * 1080 черным цветом и нарисовать некоторые изометрические 512 * 256 плиток сверху. При перемещении карты вокруг между плитами есть маленькие черные линии. Я предполагаю, что они происходят из-за того, что некоторые плитки уже перемещены в новую позицию, но изображение еще не сделано, когда накладывается на экран. Но у меня действительно нет такого решения, и я тоже не уверен, прав ли я.
Я должен, вероятно, отметить, что я использую два потока в своей программе, один поток вызывает методы обновления и рендеринга, а другой в настоящее время просто вводит пользовательский ввод для перемещения положения камеры. Также в некоторых лачугах все работает плавно, а в других он мерцает очень плохо.

Вот код создания и рендеринга окна:

private final void createWindow(){
    frame = new JFrame(title);
    frame.setMinimumSize(size);
    frame.setMaximumSize(size);
    frame.setPreferredSize(size);

    display = new Canvas();
    display.setMinimumSize(size);
    display.setMaximumSize(size);
    display.setPreferredSize(size);

    image = ImageLoader.boostPerformance(new BufferedImage((int)size.getWidth(), (int)size.getHeight(), BufferedImage.TYPE_INT_RGB));

    graphics = (Graphics2D) image.getGraphics();

    frame.add(display);
    frame.pack();
    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
    frame.setLocationRelativeTo(null);
    frame.setVisible(true);
    frame.requestFocusInWindow();
}

public final void renderToScreen(){
    BufferStrategy bs = display.getBufferStrategy();
    if(bs==null){
        display.createBufferStrategy(bufferCount);
        return;
    }

    graphicsToScreen = (Graphics2D) bs.getDrawGraphics();

    graphicsToScreen.drawImage(image, 0, 0, display.getWidth(), display.getHeight(), null);
    graphicsToScreen.dispose();
    bs.show();
}

Я использую графический объект Graphics2D в других классах для рисования фрагментов.

public void render(Graphics2D g) {
    g.setColor(Color.black);
    g.fillRect(0, 0, getWidth(), getHeight());

    tilemap.render(g);

    g.setColor(Color.red);
    g.drawString(String.valueOf(Main.actualFps), 30, 30);

}

Выше представлен метод рендеринга в другом классе с использованием графического объекта, о котором я упоминал. И здесь вы можете увидеть способ отображения карты плитки:

private final void renderIsometric(Graphics2D g){

    for(int x = 0; x < 4; x++){
        for(int y = 3; y >= 0; y--){
            if(x > tilesX - 1|| y > tilesY - 1)break;
            if(x < 0 || y < 0)continue;
            if(map[x][y] == null)continue;

            g.drawImage(map[x][y].getImage(), (int)orthoToIso(x, y).x - cameraPosX, (int)orthoToIso(x, y).y - cameraPosY, null);
        }
    }
}

Наконец, вот основной цикл, чтобы убедиться, что счетчик fps работает, и значение 120 кадров в секунду правильное:

while(running){
        long now = System.nanoTime();
        double ns = 1000000000.0 / preferredUps;
        delta += (now - lastTime) / ns;
        lastTime = now;
        while(delta >= 1){
            //call the update method 
            update();
            updates++;
            delta--;
        }
        //call the render method 
        render();
        frames++;

        //refresh the actualUps and actualFps once every second
        if(System.currentTimeMillis() - timer > 1000){
            timer+=1000;
            actualUps = updates;
            actualFps = frames;
            updates = 0;
            frames = 0;
        }
    }

Как я могу избавиться от этих крошечных задержек и проблесков мерцания? И, кроме того, мне было интересно, будет ли лучше использовать OpenGL через LWJGL вместо Java2D?


Спасибо за вашу помощь!

Теги:
performance
rendering

2 ответа

0
Лучший ответ

То, что вы видите там, скорее всего, не мерцает. Его разрывающие и ткацкие артефакты. Это естественное следствие обновления экрана без синхронизации с вертикальной синхронизацией обновления экрана.

Оконные системы, как правило, не соблюдают v-sync (они предпочитают производительность, поскольку эти эффекты не очень ощутимы - или важны - в обычном пользовательском интерфейсе). Насколько я знаю, нет возможности синхронизировать с (J) фреймом (вы, вероятно, могли бы что-то взломать с помощью JNI, попросив ОС рассказать вам, когда растровый луч находится в верхней части кадра, но это не так),

Кроме того, рендеринг со скоростью 120 кадров в секунду может ухудшить проблему, если дисплейное устройство не обновится на 120 Гц - когда вы показываете больше кадров, чем на самом деле отображает устройство, вы вынуждаете устройство отображать графику из более чем одного кадра (например, кадр 1 в верхней половине экрана, кадр 2 в нижней половине). Это всегда приведет к перцептивным артефактам с движущимися объектами.

В чистом java есть один путь, и это полноэкранный режим (который будет ждать v-sync, в результате ваша скорость рендеринга ограничена и контролируется частотой обновления дисплея).

Вы можете попробовать openGL, но я понятия не имею, будет ли он синхронизироваться при рендеринге в оконном режиме.

Первым шагом для улучшения ситуации было бы не отображать быстрее, чем отображаемые устройства отображения (например, не более 60 кадров в секунду для экрана 60 Гц).

  • 0
    Спасибо, полноэкранный режим работает нормально и ограничивает частоту кадров до 60. Однако, поскольку у меня есть экран 2560 * 1080, полноэкранный режим масштабирует холст. Может кто-нибудь сказать мне, как получить, например, черные полосы по бокам от экрана, чтобы разрешение оставалось неизменным?
  • 0
    @leomoe Масштабирование происходит из вашей реализации в методе renderToScreen (): graphicsToScreen.drawImage (image, 0, 0, display.getWidth (), display.getHeight (), null) ;. Если вы используете drawImage (image, x, y, null) вместо этого, вы должны получить немасштабированное отображение (вычислите x / y, чтобы центрировать изображение на экране или 0,0, чтобы поместить его в верхний левый угол).
Показать ещё 3 комментария
0

Если ваше приложение не критично, вы можете разрешить мерцание, используя стратегию двойной буферизации.

По существу, изображение, которое вы компонуете, копируется во вторичный буфер за пределами экрана, который не отображается в этом кадре, а в следующем кадре. Есть несколько трюков, которые вы можете использовать для автоматического двойного буфера для Swing, см. Также:

https://gamedev.stackexchange.com/questions/30921/thread-safe-double-buffering

Пример двойного буферизованного изображения в Jpanel

Недостатком двойной буферизации является то, что вы вводите один кадр задержки, что означает, что ваш ввод приведет только к видимым изменениям на один кадр позже, чем без двойной буферизации. На 120hz это, вероятно, не будет большой проблемой, а просто ради полноты.

  • 0
    Я уже использую BufferStrategy. Разве это не значит, что я использую DoubleBuffering?
  • 0
    если buffercount> 1, то да.

Ещё вопросы

Сообщество Overcoder
Наверх
Меню