SDL2 Плавная текстура (спрайт) анимация между точками во времени

В настоящее время я пытаюсь разработать эффект плавной анимации с помощью аппаратного ускорения (DirectX или OpenGL),
моя текущая цель очень проста, я хотел бы переместить текстуру из точки A в точку B в течение заданного времени,
это классический способ оживить объекты,

Я много читал об интерполяции Роберта Пеннера, и для этой цели я хотел бы анимировать мою текстуру в простой линейной интерполяции, как описано здесь:
http://upshots.org/actionscript/jsas-understanding-easing

Все работает, за исключением того, что моя анимация не плавная, она прерывистая.
Причина не в отбрасывании фреймов, а в некоторых аспектах, связанных с округлением,

я подготовил очень короткий пример в C ++ и SDL2 lib, чтобы показать такое поведение:

#include "SDL.h"
//my animation linear interpol function
double GetPos(double started, double begin, double end, double duration)
{
return (end - begin) * (double)(SDL_GetTicks() - started) / duration + begin;

}

int main(int argc, char* argv[])
{
//init SDL system
SDL_Init(SDL_INIT_EVERYTHING);

//create windows
SDL_Window* wnd = SDL_CreateWindow("My Window", 0, 0, 1920, 1080, SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS);

//create renderer in my case this is D3D9 renderer, but this behavior is the same with D3D11 and OPENGL

SDL_Renderer* renderer = SDL_CreateRenderer(wnd, 0, SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_PRESENTVSYNC);

//load image and create texture
SDL_Surface* surf = SDL_LoadBMP("sample_path_to_bmp_file");

SDL_Texture* tex = SDL_CreateTextureFromSurface(renderer, surf);

//get rid of surface we dont need surface anymore
SDL_FreeSurface(surf);

SDL_Event event;
int action = 0;
bool done = false;

//animation time start and duration
double time_start = (double) SDL_GetTicks();
double duration = 15000;

//loop render
while (!done)
{
action = 0;
while (SDL_PollEvent(&event))
{
switch (event.type)
{
case SDL_QUIT:
done = 1;
break;
case SDL_KEYDOWN:
action = event.key.keysym.sym;
break;
}
}

switch (action)
{
case SDLK_q:
done = 1;
default:
break;
}

//clear screen
SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
SDL_RenderClear(renderer);

//calculate new position
double myX = GetPos(time_start, 10, 1000, duration);

SDL_Rect r;

//assaign position
r.x = (int) round(myX);
r.y = 10;
r.w = 600;
r.h = 400;

//render to rendertarget
SDL_RenderCopy(renderer, tex, 0, &r);

//present
SDL_RenderPresent(renderer);}

//cleanup
SDL_DestroyTexture(tex);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(wnd);SDL_Quit();

return 0;
}

Я полагаю, что эффект отрывистой анимации связан с моей функцией GetPos (…), которая работает с двойными значениями, и им рендеринг через значения int. Но я не могу рендерить на экран в два раза, потому что я, очевидно, не могу рисовать в 1.2px,
Мой вопрос:
Вы знаете какую-либо технику или у вас есть какой-нибудь совет, как сделать анимацию такого типа (от, до, продолжительности) плавной без рывкового эффекта?
Я уверен, что это определенно возможно, потому что фреймворки, такие как WPF, WIN_RT, Cocos2DX, AndroidJava, все они поддерживают такую ​​анимацию, а анимация текстуры / объекта плавная,
заранее спасибо

редактировать
согласно запросу @genpfault в комментариях, я добавляю кадр за кадром значения позиции x, как int и double:

rx: 12    myX: 11.782
rx: 13    myX: 13.036
rx: 13    myX: 13.366
rx: 14    myX: 14.422
rx: 16    myX: 15.544
rx: 17    myX: 16.666
rx: 18    myX: 17.722
rx: 19    myX: 18.91
rx: 20    myX: 19.966
rx: 21    myX: 21.154
rx: 22    myX: 22.21
rx: 23    myX: 23.266
rx: 24    myX: 24.388
rx: 25    myX: 25.444
rx: 27    myX: 26.632
rx: 28    myX: 27.754
rx: 29    myX: 28.81
rx: 30    myX: 29.866
rx: 31    myX: 30.988
rx: 32    myX: 32.044
rx: 33    myX: 33.166
rx: 34    myX: 34.288
rx: 35    myX: 35.344
rx: 36    myX: 36.466
rx: 38    myX: 37.588
rx: 39    myX: 38.644

окончательное обновление / решение:

  1. Я изменил название вопроса с DirectX / OpenGL на SDL2, потому что проблема связана с SDL2 сама по себе,
  2. Я отметил ответ Рафаэля Бастоса как правильный, потому что он подтолкнул меня в правильном направлении, проблема вызвана конвейером рендеринга SDL, который основан на значениях точности int
  3. Как мы видим из вышеприведенного журнала, заикание вызвано нерегулярными значениями X, которые округляются от числа с плавающей точкой. Чтобы решить эту проблему, мне пришлось изменить конвейер рендеринга SDL2, чтобы использовать числа с плавающей точкой вместо целых чисел.
  4. Интересно то, что внутренне SDL2 для рендереров opengl, opengles2, d3d9 и d3d11 использует плавающие объекты, но публичные API SDL_RenderCopy / SDL_RenderCopyEx основаны на значениях SDL_Rect и int, что вызывает резкие эффекты анимации, когда анимация основана на функции интерполяции,

То, что именно я изменил в SDL2, намного превосходит возможности стекового потока стека, но на следующих шагах я написал несколько основных моментов, которые следует предпринять, чтобы избежать заикания анимации:

  1. я переместил структуры SDL_FRect и SDL_FPoint из внутреннего API sys_render в файл render.h, чтобы сделать их общедоступными
  2. я расширил текущие методы SDL в rect.h / rect.c для поддержки SDL_FRect и SDL_FPoint, таких как SDL_HasIntersectionF (…), SDL_IsEmptyF (…) SDL_IntersectRectF (…)
  3. я добавил новый метод GerRenderViewPortF на основе GetRenderViewPort для поддержки точности с плавающей точкой
  4. я добавил 2 новых метода SDL_RenderCopyF и SDL_RenderCopyFEx, чтобы избежать округления цифр и передачи реальных значений с плавающей точкой внутренним средствам визуализации,
  5. все общедоступные функции должны быть отражены в API dyn_proc SDL, для этого требуются некоторые знания архитектуры SDL,
  6. Чтобы избежать SDL_GetTick () и любых других проблем с точностью синхронизации, я решил изменить свой шаг интерполяции от времени к зависимости от кадра. Например, для расчета продолжительности анимации я не использую:

    float start = SDL_GetTicks();
    float duration = some_float_value_in_milliseconds;
    

    я заменил это на:

    float step = 0;
    float duration = some_float_value_in_milliseconds / MonitorRefreshRate
    

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

и теперь у меня есть действительно плавные и резкие бесплатные анимации с функциями временной шкалы,
@genpfault и @RafaelBastos спасибо за ваше время и за ваши советы,

3

Решение

Кажется, вам нужно вычесть начал с SDL_GetTicks()

Что-то вроде этого:

(end - begin) * ((double)SDL_GetTicks() - started) / duration + begin

(end - begin) gives you the total movement

(SDL_GetTicks() - started) / duration дает вам коэффициент интерполяции, который умножается на общее движение, даст вам интерполированную сумму, которую необходимо суммировать с начальной частью, чтобы вы могли получить абсолютную интерполированную позицию

если это не так, то это, вероятно, проблема округления, но если вы можете визуализировать только с точностью int, то я думаю, что вам нужно обойти sdl и визуализировать его с помощью простых вызовов opengl или directx, которые допускают плавающую точность.

1

Другие решения

Других решений пока нет …

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector