本文介绍了SDL2时间点之间的平滑纹理(精灵)动画的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

目前,我正在尝试通过硬件加速技术(DirectX或OpenGL)开发出流畅的动画效果,我当前的目标非常简单,我想在给定的持续时间内将纹理从A点移动到B点,这是动画对象的经典方法,

我了解了很多有关Robert Penner插值的知识,为此,我想用最简单的线性插值为我的纹理设置动画,如下所示: http://upshots.org/actionscript/jsas-understanding-easing

一切正常,除了我的动画不流畅之外,它很生涩.原因不是丢帧,这是int舍入方面的两倍,

我在C ++和SDL2 lib中准备了非常简短的示例来显示这种行为:

  #include"SDL.h"//我的动画线性插值函数两次GetPos(两次开始,两次开始,两次结束,两次持续时间){返回(结束-开始)*(双精度)(SDL_GetTicks()-开始)/持续时间+开始;}int main(int argc,char * argv []){//初始化SDL系统SDL_Init(SDL_INIT_EVERYTHING);//创建窗口SDL_Window * wnd = SDL_CreateWindow(我的窗口",0,0,1920,1080,SDL_WINDOW_SHOWN | SDL_WINDOW_BORDERLESS);//在我的情况下,创建渲染器是D3D9渲染器,但是此行为与D3D11和OPENGL相同SDL_Renderer *渲染器= SDL_CreateRenderer(wnd,0,SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE | SDL_RENDERER_PRESENTVSYNC);//加载图像并创建纹理SDL_Surface * surf = SDL_LoadBMP("sample_path_to_bmp_file");SDL_Texture * tex = SDL_CreateTextureFromSurface(renderer,surf);//摆脱表面,我们不再需要表面SDL_FreeSurface(surf);SDL_Event事件;int动作= 0;布尔完成=假;//动画时间的开始和持续时间double time_start =(double)SDL_GetTicks();双倍持续时间= 15000;//循环渲染同时(!完成){行动= 0;while(SDL_PollEvent(& event)){开关(event.type){情况SDL_QUIT:完成= 1;休息;情况SDL_KEYDOWN:动作= event.key.keysym.sym;休息;}}开关(动作){情况SDLK_q:完成= 1;默认:休息;}//清除萤幕SDL_SetRenderDrawColor(renderer,0,0,0,255);SDL_RenderClear(renderer);//计算新位置double myX = GetPos(time_start,10,1000,持续时间);SDL_Rect r;//尊严的位置r.x =(int)轮(myX);r.y = 10;r.w = 600;r.h = 400;//渲染到rendertargetSDL_RenderCopy(renderer,tex,0,& r);//展示SDL_RenderPresent(renderer);}//清理SDL_DestroyTexture(tex);SDL_DestroyRenderer(renderer);SDL_DestroyWindow(wnd);SDL_Quit();返回0;} 

我认为生涩的动画效果与我的GetPos(...)函数有关,该函数可使用double值,并通过int值进行im渲染.但是我无法以两倍的比例渲染到屏幕,因为我显然无法以1.2px进行绘制,我的问题是:您知道任何技术吗?或者您有一些建议如何使这种动画(从,到,持续)平滑而又没有生涩的效果?我肯定这绝对是可能的,因为WPF,WIN_RT,Cocos2DX,AndroidJava之类的框架都支持这种动画,并且纹理/对象动画很流畅,预先感谢

修改根据注释中的@genpfault请求,即逐帧添加x位置值(如int和double):

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

最终更新/解决:

  1. 我将问题标题从DirectX/OpenGL更改为SDL2,因为问题本身与SDL2有关,
  2. 我将Rafael Bastos答案标记为正确,因为他将我推向正确的方向,问题是由基于int精度值的SDL渲染管道引起的
  3. 如我们在上面的日志中所见-口吃是由不规则的X值引起的,该值从float取整.为了解决该问题,我不得不将SDL2渲染管道更改为使用浮点数而不是整数
  4. 有趣的是,内部用于opengl,opengles2,d3d9和d3d11渲染器的SDL2使用浮点数,但是公共SDL_RenderCopy/SDL_RenderCopyEx api基于SDL_Rect和int值,当动画基于插值函数时,这会导致抖动的动画效果,

我在SDL2中所做的确切更改远远超出了stackoverflow的范围,但是在接下来的步骤中,我写了一些要点来避免动画卡顿:

  1. 我将SDL_FRect和SDL_FPoint结构从内部sys_render api移至render.h api以使其公开
  2. i在rect.h/rect.c中扩展了当前的SDL方法以支持SDL_FRect和SDL_FPoint,例如SDL_HasIntersectionF(...),SDL_IsEmptyF(...)SDL_IntersectRectF(...)
  3. 我添加了基于GetRenderViewPort的新方法GerRenderViewPortF以支持浮点精度
  4. 我添加了2个新方法SDL_RenderCopyF和SDL_RenderCopyFEx以避免任何数字舍入并将真实浮点值传递给内部渲染器,
  5. 所有公共功能必须反映在dyn_proc SDL api中,它需要一些SDL体系结构知识才能做到这一点,
  6. 为避免SDL_GetTick()和任何其他计时精度问题,我决定将插值步骤从时间更改为帧依赖性.例如,不使用以下方法计算动画持续时间:

      float start = SDL_GetTicks();浮动持续时间= some_float_value_in_milliseconds; 

    我将其替换为:

     浮点步长= 0;浮动持续时间= some_float_value_in_milliseconds/MonitorRefreshRate 

    ,现在在每帧渲染后即时递增step ++
    当然,这会有一些副作用,如果我的引擎会丢帧,那么我的动画时间就不等于持续时间,因为它更多地取决于帧,当然,此持续时间计算仅在VSYNC启用时有效,在vblank禁用时则无效,

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

解决方案

似乎您需要从 SDL_GetTicks()

开始减去

类似这样的东西:

 (结束-开始)*((双精度)SDL_GetTicks()-开始)/持续时间+开始(结束-开始)为您提供整体运动 

(SDL_GetTicks()-起始)/持续时间为您提供了插值比率,将其乘以总移动量将得出插值的数量,需要将其求和到开始部分,所以您可以具有绝对插值位置

如果不是这样,那可能是一个舍入问题,但是如果您只能以int精度进行渲染,那么我认为您需要绕过sdl并使用普通的opengl或Directx调用进行渲染,这允许浮动精度./p>

currently im trying to develop smooth animation effect via hardware accelerated technique (DirectX or OpenGL),my current goal is very simple, i would like to move texture from point A to point B in given duration,this is classic way to animate objects,

i read a lot about Robert Penner interpolations, and for this purpose i would like to animate my texture in simpliest linear interpolation as described here:http://upshots.org/actionscript/jsas-understanding-easing

Everything works, except that my animation is not smooth, it is jerky.The reason is not frame dropping, it is some double to int rounding aspects,

i prepared very short sample in C++ and SDL2 lib to show that behavior:

#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;
}

i suppose that jerky animation effect is related to my GetPos(...) function which works with doubles values, and im rendering via int values. But i cant render to screen in double because i obviously can't draw at 1.2px,My question is:do you know any technique or do you have some advice how to make that kind of animations (from, to, duration) smooth without jerky effect?Im sure that's definitely possible because frameworks like WPF, WIN_RT, Cocos2DX, AndroidJava all them supports that kind of animations, and texture/object animation is smooth,thanks in advance

editas per @genpfault request in comments im adding frame by frame x position values, as int and 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:

  1. I changed question title from DirectX/OpenGL to SDL2 because issue is related to SDL2 it self,
  2. I marked Rafael Bastos answer as correct because he pushed me into right direction, issue is caused by SDL render pipeline which is based on int precision values
  3. As we can see in above log - stuttering is caused by irregular X values which are rounded from float. To solve that issue i had to change SDL2 render pipeline to use floats instead of integers
  4. Interesting is that, SDL2 internally for opengl,opengles2, d3d9 and d3d11 renderers uses floats, but public SDL_RenderCopy/SDL_RenderCopyEx api is based on SDL_Rect and int values, this causing jerky animation effects when animation is based on interpolation function,

What exactly i changed in SDL2 is far far beyound stackoverflow scope, but in next steps i writed some main points what should be done to avoid animation stuttering:

  1. i moved SDL_FRect and SDL_FPoint structs from internal sys_render api to render.h api to make them public
  2. i extended current SDL methods in rect.h/rect.c to support SDL_FRect and SDL_FPoint, such SDL_HasIntersectionF(...), SDL_IsEmptyF(...) SDL_IntersectRectF(...)
  3. i added new method GerRenderViewPortF based on GetRenderViewPort to support float precision
  4. i added 2 new method SDL_RenderCopyF and SDL_RenderCopyFEx to avoid any figures rounding and pass real floats values to internal renderers,
  5. all public functions must be reflected in dyn_proc SDL api, it requires some SDL architecture knowledge to do that,
  6. to avoid SDL_GetTick() and any other timing precision issues, i decided to change my interpolation step from time to frame dependency. For example to calculate animation duration im not using:

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

    i replaced that to:

    float step = 0;
    float duration = some_float_value_in_milliseconds / MonitorRefreshRate
    

    and now im incrementing step++ after each frame render
    of course it has some side effect, if my engine will drop some frames, then my animation time is not equal to duration because is more frame dependent,of course this duration calculations are valid only when VSYNC is ON, it is useless when vblank is off,

and now i have really smooth and jerky free animations, with timeline functions,
@genpfault and @RafaelBastos thanks for your time and for your advices,

解决方案

seems you need to subtract started from SDL_GetTicks()

Something like this:

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

(end - begin) gives you the total movement

(SDL_GetTicks() - started) / duration gives you the interpolation ratio, which multiplied by the total movement will give you the amount interpolated, which needs to be summed to the begin portion, so you can have the absolute interpolated position

if that's not it, then it is probably a rounding issue, but if you can only render with int precision, then I think you need to bypass sdl and render it using plain opengl or directx calls, which allow floating precision.

这篇关于SDL2时间点之间的平滑纹理(精灵)动画的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-20 17:11