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


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

Я много читал об интерполяциях Роберта Пеннера, и для этой цели я хотел бы оживить свою текстуру в самой простой линейной интерполяции, как описано выше. здесь: 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(...) функция, которая работает с двойными значениями, и визуализация im через значения int. Но я не могу передать, чтобы экран в двойном размере, потому что я, очевидно, не могу рисовать на 1.2 px, Мой вопрос таков:: знаете ли вы какую-нибудь технику или у вас есть совет, как сделать такие анимации (от, до, продолжительность) плавными без резкого эффекта? Я уверен, что это определенно возможно, потому что фреймворки, такие как WPF, WIN_RT, Cocos2DX, AndroidJava все они поддерживают такой вид анимации, и анимация текстур / объектов гладкая, заранее спасибо

Edit в соответствии с запросом @genpfault в комментариях im добавление кадра по значения положения кадра 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 IT self,
  2. я отметил ответ Рафаэля Бастоса как правильный, потому что он подтолкнул меня в правильном направлении, проблема вызвана конвейером визуализации SDL, который основан на значениях точности int
  3. как мы можем видеть выше, лог-заикание вызвано нерегулярными значениями X, которые округляются от плыть. Чтобы решить эту проблему, мне пришлось изменить SDL2 Render pipeline, чтобы использовать поплавки вместо целых чисел
  4. интересно то, что SDL2 внутренне для рендеров opengl,opengles2, d3d9 и d3d11 использует поплавки, но публичный SDL_RenderCopy / SDL_RenderCopyEx api основан на значениях SDL_Rect и int, что вызывает отрывистые эффекты анимации, когда анимация основана на функции интерполяции,

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

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

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

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

    float step = 0;
    float duration = some_float_value_in_milliseconds / MonitorRefreshRate
    

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

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

1 3

1 ответ:

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

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

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

(end - begin) gives you the total movement

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

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