首页 文章

SDL2时间点功能之间的平滑纹理(精灵)动画

提问于
浏览
3

目前我试图通过硬件加速技术(DirectX或OpenGL)开发平滑的动画效果,我目前的目标很简单,我想在给定的持续时间内将纹理从A点移动到B点,这是动画对象的经典方式,

我读了很多关于Robert Penner插值的内容,为此我想在最简单的线性插值中设置我的纹理动画,如下所述:http://upshots.org/actionscript/jsas-understanding-easing

一切正常,除了我的动画不流畅,它是生涩的 . 原因不是帧丢失,它是int rounding方面的一些双重,

我在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这样的框架都支持这种动画,纹理/对象动画很流畅,提前谢谢

edit 根据评论中的@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

final update/solve:

  • 我将问题 Headers 从DirectX / OpenGL更改为SDL2,因为问题与SDL2有关,

  • 我将Rafael Bastos的回答标记为正确,因为他将我推向了正确的方向,问题是由基于int精度值的SDL渲染管道引起的

  • 正如我们在上面的日志中看到的那样 - 口吃是由不规则的X值引起的,这些值是从浮点舍入的 . 要解决这个问题,我必须更改SDL2渲染管道以使用浮点数而不是整数

  • 有趣的是,内部用于opengl,opengles2,d3d9和d3d11渲染器的SDL2使用浮点数,但公共SDL_RenderCopy / SDL_RenderCopyEx api基于SDL_Rect和int值,当动画基于插值函数时,这会导致生涩的动画效果,

我在SDL2中究竟改变了什么远远超出了堆栈溢出范围,但是在接下来的步骤中我写了一些要点,以避免动画卡顿:

  • 我将SDL_FRect和SDL_FPoint结构从内部sys_render api移动到render.h api以使它们公开

  • i扩展了rect.h / rect.c中的当前SDL方法以支持SDL_FRect和SDL_FPoint,例如SDL_HasIntersectionF(...),SDL_IsEmptyF(...)SDL_IntersectRectF(...)

  • 我添加了基于GetRenderViewPort的新方法GerRenderViewPortF以支持浮点精度

  • 我添加了2个新方法SDL_RenderCopyF和SDL_RenderCopyFEx以避免任何数字舍入并将实际浮点值传递给内部渲染器,

  • 所有公共函数必须反映在dyn_proc SDL api中,它需要一些SDL架构知识来做到这一点,

  • 为了避免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为ON时才有效,当vblank为时它是无用的关,

而现在我有非常流畅和生涩的免费动画,有时间轴功能,
@genpfault和@RafaelBastos感谢您的时间和建议,

1 回答

  • 1

    好像你需要从 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调用来渲染它,这允许浮动精度 .

相关问题