LINUX.ORG.RU

Как правильно реализовать анимацию

 , ,


0

3

Во многих туториалах приводится такой пример.

x += v*dt; Где v скорость в чем-то(пиксели например) за миллисекунду, а dt время между этим и прошлым кадром. Да вот только это неверно. Предположим фпс у нас не равно 60, а монитор максимально может отобразить 60 кадров. По сути настоящий dt(время между этим и прошлым кадром составляет всегда строго (1000/60)*N мс. Где N разумеется только целое число. Мы же по ошибке будем рисовать x чуть правее или чуть левее чем он должен быть на самом деле. Какие есть идеи решения этой проблемы?

П.С. а навеяно собственно багов в libGDX где даже на мощном оборудовании можно наблюдать дерганье спрайтов, одно из решений которое там предполагалось было принять dt всегда 1000/60 независимо от того сколько реально прошло время между кадрами, поскольку все равно(в случае быстрой производительности) реальные кадры на экране будут отображаться через 1000/60

★★★

Ну, формула то верная, из физики. Бери, пиши, грубо говоря, два модуля - один для подсчета «физики» (скорости в м/c, например), второй - для отображения тела с координатами X,Y метров в координатах X,Y пикселей. В OpenGL это будет вершинным шейдером, например, который твои метры на отрезок [0,1] отображать будет. Потом просто подсчитываешь координаты тела в метров, передаешь их в модуль, который тела рисует, и все - считаешь ты все в метрах, а рисуешь в пикселях. Как то так.

cherry-pick
()

А вдруг твой монитор отображает 90 фпс? Или 30.

Используй dt и не выпендривайся. А дёрганье спрайтов обычно случается от того, что кто-то где-то округляет координаты.

PolarFox ★★★★★
()
Ответ на: комментарий от cherry-pick

Ты вообще пост читал? Проблема в том что dt не соответствует действительности, ибо монитор все равно свои 60 кадров рисует.

А вот пример dt(получено libGDX, их средствами)

dt: 0.017019551
dt: 0.01697891
dt: 0.016000275
dt: 0.017002841
dt: 0.017000848
dt: 0.015997887
dt: 0.017004903
dt: 0.016993895
dt: 0.0160003

abs ★★★
() автор топика

Ты сессию уже сдал, прежде чем за анимацию хвататься?

peregrine ★★★★★
()
Ответ на: комментарий от abs

Ты вот в ОП-посте формулу подсчета координат из скорости написал. Вот нафига ты в эту формулу пиксели и кадры пихаешь? Совсем поехал что-ли? Считай время в секундах, а расстояние в метрах, скорость в метрах в секунду. В каждом кадре ты запрашиваешь у твоей физики текущие координаты объекта в метрах, и отображаешь их на пиксели. Все. В итоге рисуешь ты пиксели, а все подсчеты делаешь в нормальных физических величинах. Уж отобразить координаты из отрезка [a,b] на отрезок [x,y] ты сможешь - простейшая геометрия же. То, что ты делаешь - голый идиотизм. Не надо так программировать.

cherry-pick
()

А я вот через храндр узнавал на чем мы щас работаем и тогда высчитывал задержку, щас поискал и не могу найти сорцы, вобщем тип того: http://stackoverflow.com/questions/17797636/c-linux-get-the-refresh-rate-of-a...

Правда тут есть небольшая визуальная неприятность, примерно на секунду (в момент разового тестирования) весь экран становится черным. Так же ведет себя кстати и Totem. Но для игоря же это вполне нормально, да?

deep-purple ★★★★★
()
Ответ на: комментарий от cherry-pick

А вот, ты умный. Расскажи как синхрить по времени то, что идет в расчетах мира, и то что показывается на экране.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

О хосспаде, а что, в иксах разве нельзя как-то так анимацию рисовать?

...
while (!glfwWindowShouldClose(this->mainWindow)) {
        float deltaT = std::chrono::duration_cast<std::chrono::duration<float>>(now - start).count();
        float xCoord = startXCoord + speed * deltaT;
        glUniform1f(xUniform, xCoord):
        glDrawElements(...);
        glfwSwapBuffers(this->mainWindow);
        glfwPollEvents();
    }
...

И все будет рисоваться нормально вне зависимости от FPS, безо всякой камасутры.

cherry-pick
()
Ответ на: комментарий от cherry-pick

Вот мне кааца, тут дело еще в карте и дровине. Иначе откуда тогда все эти бугурты за тиринг?

deep-purple ★★★★★
()
Ответ на: комментарий от cherry-pick

Но вообще да — иксы же сами принимают решение когда обновить область (если ты только сам не попросил насильно).

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

Эмм, чего? В OpenGL у тебя есть евентлуп, в котором ты вызываешь glDraw/glSwapBuffers, как быстро оно будет отрисовываться (FPS) зависит от того, что ты там накодил, если тебе какую-то анимацию рисовать надо, подсчитываешь разницу во времени между двумя отрисовками, вычисляешь координаты, передаешь их в вершинный шейдер, который все уже отрисовывает, как надо. И никакой камасутры с мигающими спрайтами я не наблюдал.

cherry-pick
()
Ответ на: комментарий от cherry-pick

И всетаки. Представим что есть мир, часть которого сейчас на экране не видна, и в этой части мира происходит какой-то расчет. На основании чего его считать? Ок, на основании «реального времени» — в секундах. Но, тогда эти состояния нужно будет как-то забирать на экран. И вот тут разве не потребуется знать какой у нас фпс?

deep-purple ★★★★★
()
Ответ на: комментарий от cherry-pick

А я вот наблюдал всякие мигания и не плавности, пока не узнал фпс и не рассчитал задержку. И да — там были голые иксы, без гл.

deep-purple ★★★★★
()
Ответ на: комментарий от deep-purple

И вот тут разве не потребуется знать какой у нас фпс?

Зачем? Вот смотри - допустим у нас есть какой-то объект, с координатами, например (100 метров, 200 метров). Перед вызовом glDrawElements мы передаем эти координаты, прямо в метрах, в вершинный шейдер, через Uniform. В вершинном шейдере мы берем эти координаты в метрах, и отображаем их на отрезок [0,1]. Для этого нам надо знать только максимальную ширину и высоту мира в метрах. Допустим, наш мир имеет размер 200x200 метров. Тогда координаты объекта (100, 200) отображаются в OpenGL-координаты (0.5,1.0). Все это можно делать прямо в вершинном шейдере, они для этого и создавались собственно.

cherry-pick
()
Ответ на: комментарий от cherry-pick

А, ну в таком случае там все под капотом рассчитывается без нашего участия. Но вот у ТС почему-то криво. И он грешит на разницу меж мониторным и «программерским» фпс. Ну, я понаблюдаю что у него получится.

deep-purple ★★★★★
()
Последнее исправление: deep-purple (всего исправлений: 1)
Ответ на: комментарий от deep-purple

Вот поэтому я и агитирую за программирование графики сложнее простенького GUI на OpenGL - там графику реально проще делать. Надо просто свой мозг немножко перестроить, и концепцию шейдеров / конвеера освоить. Поначалу мозгодробительно и непонятно, зато потом сплошная нирвана.

cherry-pick
()

dt - это, в общем то, время между тиками физики, а не время между кадрами, просто у тебя так сложилось, что это одно и то же. Кадров (1000/60)*N только если включен VSync, а он нужен не всегда. Вынеси графику в отдельный поток, это решит твои проблемы.

anonymous
()
Ответ на: комментарий от cherry-pick

графики сложнее простенького GUI на OpenGL

Оно и используется в libGDX.

float deltaT = std::chrono::duration_cast<std::chrono::duration<float>>(now - start).count();

Вот не знаю как оно реализовано в libGDX но данные приходят не совсем верные, как уже писал выше

dt: 0.017019551
dt: 0.01697891
dt: 0.016000275
dt: 0.017002841
dt: 0.017000848
dt: 0.015997887
dt: 0.017004903
dt: 0.016993895
dt: 0.0160003

Вот пусть x += 60*dt; В результате мы получим в некоторых кадрах сдвиг на два пикселя, в некоторых вообще будет стоять на месте. Хотя он должен четко сдвигаться на один пиксель за кадр(фпс то как видим 60).

Вот нафига ты в эту формулу пиксели и кадры пихаешь? Совсем поехал что-ли?

Дак я для примера написал, у меня box2d там координаты таки в метрах, а потом я при рисовании перевожу их в экранные.

abs ★★★
() автор топика
Ответ на: комментарий от deep-purple

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

anonymous
()
Ответ на: комментарий от cherry-pick

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

anonymous
()
Ответ на: комментарий от anonymous

VSync включенный по умолчанию в libGDX и на Android.

abs ★★★
() автор топика
Ответ на: комментарий от anonymous

Не для простенького GUI, а для простенькой анимации/графики (GUI == Graphical User Interface, а не «анимация»). Шейдеры же проще гораздо, чем такие вот костыли. Там самое сложное - бойлерплэйт для загрузки шейдеров из файликов/компиляции/линковки, но на то он и бойлерплэйт, что его один раз написать можно, а дальше, как библиотечку юзать. А сама концепция шейдеров легка же до безобразия.

cherry-pick
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.