Разрывы и дрожание в простой анимации обработки

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

Ниже приведен соответствующий код:

 final int MAX_BALLS = 50;
 final int DISPLAY_WIDTH = 800;
 final int DISPLAY_HEIGHT = 600;
 final float MIN_SPEED = 1;
 final float MAX_SPEED = 5;
 final float MIN_SIZE = 30;
 final float MAX_SIZE = 50;
 Ball[] balls = new Ball[MAX_BALLS];

 void setup() {
   size(800, 600);
   stroke(255);
   background(0, 0, 0);
   for (int i=0; i<balls.length; i++) {
     balls[i] = new Ball(random(MIN_SIZE, MAX_SIZE), random(0, DISPLAY_WIDTH), random(0, DISPLAY_HEIGHT), random(MIN_SPEED, MAX_SPEED), random(MIN_SPEED, MAX_SPEED), 0, DISPLAY_WIDTH, 0, DISPLAY_HEIGHT);
   }
 } 

 void draw() {
   clear();
   for (int i = 0; i<balls.length; i++)
     balls[i].draw();
 }

 class Ball {
   private float size;
   private float x;
   private float y;
   private float vx;
   private float vy;
   private float minx;
   private float maxx;
   private float miny;
   private float maxy;
   private float r;
   private float g;
   private float b;

   public Ball(float size,float x, float y, float vx, float vy, float minx, float maxx, float miny, float maxy) {
     this.size = size;
     this.x = x;
     this.y = y;
     this.vx = vx;
     this.vy = vy;
     this.minx = minx;
     this.maxx = maxx;
     this.miny = miny;
     this.maxy = maxy;
     r = (int) random(30, 255);
     g = (int) random(30, 255);
     b = (int) random(30, 255);
   }

   void draw() {
     x = x + vx;
     if (x + size/2 > maxx) {
       vx = -vx;
       x = 2 * maxx - (x + size);
     } else if (x - size/2 < minx) {
       vx = -vx;
       x = 2 * minx - (x - size);
     }

     y = y + vy;
     if (y + size/2 > maxy) {
       vy = -vy;
       y = 2 * maxy - (y + size);
     } else if (y -size/2 < miny) {
       vy = -vy;
       y = 2 * miny - (y - size);
     }
     stroke(r,g,b);
     fill(r,g,b);
     ellipse(x, y, size, size);

   }   

 }

Как можно избавиться от дрожания и разрывов? Как обеспечить оптимальное использование драйвера видеокарты. Обратите внимание, что я использую Linux Mint 17.3 с менеджером рабочего стола MATE. Та же ОС на всех протестированных ПК и одинаковое поведение.

[РЕДАКТИРОВАТЬ 01.05.2016] После создания кругов за пределами экрана и даже использования автономного изображения размером с экран я все еще получаю разрывы. Это обновленный код:

 final int MAX_BALLS = 50;
 final float MIN_SPEED = 1;
 final float MAX_SPEED = 5;
 final float MIN_SIZE = 30;
 final float MAX_SIZE = 50;
 Ball[] balls = new Ball[MAX_BALLS];
 PGraphics img;

 void setup() {
   frameRate(60);
   fullScreen();
   img = createGraphics(width, height);
   img.stroke(255);
   img.smooth();
   for (int i=0; i<balls.length; i++) {
     balls[i] = new Ball(random(MIN_SIZE, MAX_SIZE), random(0, width), random(0, height), random(MIN_SPEED, MAX_SPEED), random(MIN_SPEED, MAX_SPEED), 0, width, 0, height);
   }
 } 

 void draw() {
   img.beginDraw();
   img.background(0,0,0);
   img.clear();
   clear();
   for (int i = 0; i<balls.length; i++)
     balls[i].draw();

   img.text((int)frameRate+"fps",10,15);
   img.endDraw();
   image(img, 0, 0); // Put the whole image at once on the screen
 }

 class Ball {
   private float size;
   private float x;
   private float y;
   private float vx;
   private float vy;
   private float minx;
   private float maxx;
   private float miny;
   private float maxy;
   private PGraphics circle;
   private final int MARGIN = 10; // Margin to avoid circle to be drawn slightly outside the square

   public Ball(float size,float x, float y, float vx, float vy, float minx, float maxx, float miny, float maxy) {
     this.size = size;
     this.x = x;
     this.y = y;
     this.vx = vx;
     this.vy = vy;
     this.minx = minx;
     this.maxx = maxx;
     this.miny = miny;
     this.maxy = maxy;

     int r = (int) random(30, 255);
     int g = (int) random(30, 255);
     int b = (int) random(30, 255);

     circle = createGraphics((int) this.size + 2*MARGIN, (int) this.size + 2*MARGIN);
     circle.beginDraw();
     circle.background(0, 0);
     circle.fill(r, g, b);
     circle.ellipse(MARGIN + this.size/2, MARGIN + this.size/2, this.size, this.size);
     circle.endDraw();         
   }

   void draw() {
     x = x + vx;
     if (x + size/2 > maxx) {
       vx = -vx;
       x = 2 * maxx - (x + size);
     } else if (x - size/2 < minx) {
       vx = -vx;
       x = 2 * minx - (x - size);
     }

     y = y + vy;
     if (y + size/2 > maxy) {
       vy = -vy;
       y = 2 * maxy - (y + size);
     } else if (y -size/2 < miny) {
       vy = -vy;
       y = 2 * miny - (y - size);
     }

     img.image(circle, x - this.size/2 - MARGIN, y - this.size/2 - MARGIN);
   }   

 }

person Tarik    schedule 29.04.2016    source источник
comment
Этот код отлично работает для меня в Windows 10. Можете ли вы сделать снимок экрана именно того, о чем вы говорите? Возможно, измените свой код, чтобы использовать жестко закодированные значения, которые вызывают поведение, вместо использования функции random().   -  person Kevin Workman    schedule 29.04.2016
comment
@KevinWorkman Поведение одинаково независимо от размера шаров. Я получаю разрывы каждый раз, когда я запускаю приложение. Я не тестировал это на Windows, хотя. Я не мог сделать снимок экрана. Запись видео со скоростью 60 кадров в секунду с использованием VLC не смогла его захватить. Это горизонтальный разрыв со смещением нижней части шара.   -  person Tarik    schedule 30.04.2016
comment
Это будет довольно сложно отладить, если мы не сможем повторить поведение. Какой мяч? Случайное движение вообще связано, или вы можете просто жестко запрограммировать направление движения? Можете ли вы создать меньший пример всего с одним мячом? Здесь много лишнего кода. Все это связано с вашей проблемой? Если нет, можете ли вы вообще упростить пример?   -  person Kevin Workman    schedule 30.04.2016
comment
@KevinWorkman Это происходит случайно на некоторых шарах. См. также: stackoverflow.com/questions/20551224/, которые, похоже, имеют дело с проблемой вертикальной синхронизации. Однако решение не работает для обработки 3.   -  person Tarik    schedule 30.04.2016
comment
Это, кажется, другой вопрос. Можете ли вы опубликовать минимально воспроизводимый пример кода, который вы пытаетесь вызвать и который генерирует ошибку?   -  person Kevin Workman    schedule 30.04.2016
comment
@KevinWorkman Ошибки как таковой нет. Просто меня рвет из-за того, что кажется проблемой с синхронизацией, которая затрагивает только Linux. Я попробую запустить это в Windows и посмотреть. Я буду держать вас в курсе.   -  person Tarik    schedule 30.04.2016
comment
Я имел в виду решение, которое не работает для Processing 3. Можете ли вы опубликовать этот код здесь?   -  person Kevin Workman    schedule 30.04.2016
comment
@KevinWorkman Я пытался запустить его в Windows, и что примечательно, так это то, что примерно каждые 2-3 секунды шарики как бы замирают на долю секунды. Я проверил загрузку ЦП, и она близка к нулю.   -  person Tarik    schedule 02.05.2016
comment
@KevinWorkman Нашел решение. См. принятый ответ выше.   -  person Tarik    schedule 02.05.2016
comment
@KevinWorkman Обнаружил, что он работает с разрешением 800x600, но не на весь экран. Кроме того, отрисовка всего за пределами экрана, а затем отрисовка всего изображения обратно на экран также не помогла.   -  person Tarik    schedule 02.05.2016


Ответы (2)


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

 final int MAX_BALLS = 50;
 final int DISPLAY_WIDTH = 800;
 final int DISPLAY_HEIGHT = 600;
 final float MIN_SPEED = 1;
 final float MAX_SPEED = 5;
 final float MIN_SIZE = 30;
 final float MAX_SIZE = 50;
 Ball[] balls = new Ball[MAX_BALLS];

 void setup() {
   frameRate(60);
   size(800, 600, FX2D);
   stroke(255);
   background(0, 0, 0);
   smooth();
   for (int i=0; i<balls.length; i++) {
     balls[i] = new Ball(random(MIN_SIZE, MAX_SIZE), random(0, DISPLAY_WIDTH), random(0, DISPLAY_HEIGHT), random(MIN_SPEED, MAX_SPEED), random(MIN_SPEED, MAX_SPEED), 0, DISPLAY_WIDTH, 0, DISPLAY_HEIGHT);
   }
 } 

 void draw() {
   clear();
   for (int i = 0; i<balls.length; i++)
     balls[i].draw();

     text((int)frameRate+"fps",10,15);

 }

 class Ball {
   private float size;
   private float x;
   private float y;
   private float vx;
   private float vy;
   private float minx;
   private float maxx;
   private float miny;
   private float maxy;
   private PGraphics circle;
   private final int MARGIN = 10; // Margin to avoid circle to be drawn slightly outside the square

   public Ball(float size,float x, float y, float vx, float vy, float minx, float maxx, float miny, float maxy) {
     this.size = size;
     this.x = x;
     this.y = y;
     this.vx = vx;
     this.vy = vy;
     this.minx = minx;
     this.maxx = maxx;
     this.miny = miny;
     this.maxy = maxy;

     int r = (int) random(30, 255);
     int g = (int) random(30, 255);
     int b = (int) random(30, 255);

     circle = createGraphics((int) this.size + 2*MARGIN, (int) this.size + 2*MARGIN);
     circle.beginDraw();
     circle.background(0, 0);
     circle.fill(r, g, b);
     circle.ellipse(MARGIN + this.size/2, MARGIN + this.size/2, this.size, this.size);
     circle.endDraw();         
   }

   void draw() {
     x = x + vx;
     if (x + size/2 > maxx) {
       vx = -vx;
       x = 2 * maxx - (x + size);
     } else if (x - size/2 < minx) {
       vx = -vx;
       x = 2 * minx - (x - size);
     }

     y = y + vy;
     if (y + size/2 > maxy) {
       vy = -vy;
       y = 2 * maxy - (y + size);
     } else if (y -size/2 < miny) {
       vy = -vy;
       y = 2 * miny - (y - size);
     }

     image(circle, x - this.size/2 - MARGIN, y - this.size/2 - MARGIN);
   }   

 }
person Tarik    schedule 02.05.2016

Я не вижу никаких проблем с обновлением кода и рендерингом кругов.

Используя Processing 2, я уже вижу разницу в производительности с разными рендерерами.

Я добавил это в конце draw(), чтобы получить приблизительное представление о частоте кадров:

text((int)frameRate+"fps",10,15);

и это установка, которую я пробовал

size(800, 600,JAVA2D);
frameRate(60);

а также

size(800, 600,P2D);
frameRate(60);

и заметил, что с JAVA2D частота кадров довольно близка к 60 кадрам в секунду, а с P2D она падает до ~ 40-45 кадров в секунду.

Однако это на OSX, а не на Linux Mint. Попробуйте рендерер FX2D в Обработке 3 и посмотрите, как он сравнивается с двумя другими рендерерами.

Кроме того, если на компьютере с Linux Mint есть графический процессор, драйверы установлены правильно и у вас есть время, вы можете попробовать перенести код на GLSL и отобразить его в Processing, используя PSShader.

person George Profenza    schedule 30.04.2016
comment
Я попробовал запустить его в Windows, и что примечательно, так это то, что примерно каждые 2-3 секунды шары как бы зависают на долю секунды. Я проверил загрузку ЦП, и она близка к нулю. Я пробовал разные рендереры в Linux, но безрезультатно. - person Tarik; 02.05.2016
comment
Хорошо, я только что добавил флаг FX2D в Windows, и теперь он зависает реже, опять же, на долю секунды. - person Tarik; 02.05.2016
comment
Добавил строку частоты кадров в Linux и получаю 59-60 кадров в секунду. Установив частоту кадров на 100 кадров в секунду, я получаю 99 кадров в секунду-100 кадров в секунду. Таким образом, это, похоже, не связано с вычислительной мощностью, поскольку расчеты довольно просты. - person Tarik; 02.05.2016
comment
Хороший! (+1) (У меня был момент, когда я вспомнил этот ответ ) - person George Profenza; 02.05.2016
comment
Что ж, плохие новости: когда я перешел с 800x600 на полный экран, проблема снова вернулась. Затем я попытался нарисовать все за пределами экрана на отдельном объекте PGraphics, а затем нарисовать все это изображение на экране. Безрезультатно. Я вернулся к исходной точке. Я удалил свой ответ на вопрос, так как он больше не заслуживает :-) - person Tarik; 02.05.2016