如何减慢从BVH文件读取的opengl动画?

时间:2018-10-14 15:19:33

标签: c++ opengl glfw glm-math skeletal-animation

我现在使用GLFW3制作了一个bvh文件解析器,该文件解析器读取文件并将其转换为在opengl中创建的人体模型。但是,每当我启动它时,运动速度都非常快,以至于看不到动画。所以我想调低动画速度。这是我的渲染循环

while (!glfwWindowShouldClose(window))
    {
        // per-frame time logic
        // --------------------
        float currentFrame = glfwGetTime();
        deltaTime = currentFrame - lastFrame;
        lastFrame = currentFrame;

        // input
        // -----
        processInput(window);

        // render
        // ------
        glClearColor(0.9f, 0.9f, 0.9f, 1.0f);
        glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

        (Some Shader Settings)

        glm::mat4 model = glm::mat4(1.0f);

        if (moveFlag == true) {
            bvh.clearVISITED();
            bvh.setMotionPerFrame(bvh.root, 0);
        }
        if (resetMatrices == true) {
            bvh.clearVISITED();
            bvh.resetMatrices(bvh.root);
        }

        drawBones(shader, bvh.root, model);

        glfwSwapBuffers(window);
        glfwPollEvents();
    }

if语句bvh.setMotionPerFrame(bvh.root, 0)内的函数是动画读取文件内每帧数据的JOINT配置的位置,并相应地设置每个关节的旋转矩阵和平移矩阵。 ({moveFlagresetMatrices是分别按下空格键和r按钮时设置的标志)

由于读取每个渲染循环中每帧的通道数据是不可更改的,因此我想提出一种可以降低渲染循环速度本身的方法。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

您需要为代码添加计时。像这样解析BVH MOTION时:

MOTION
Frames:     20
Frame Time: 0.033333

您应该提取Frame Time值,该值是帧之间的时间(以秒为单位)。现在,根据您的代码架构,您需要适当地安排渲染时间。我曾经有一些

bool _redraw=false;

告诉整个应用在下一次可能的情况下重绘(例如计时器),该设置由鼠标/键盘事件更改视图或场景到调整窗口大小等设置。

对于时变的东西,我通常也有一个功能:

void update(double dt);
从应用程序处理内插/模拟中定期调用

,从上次调用开始经过dt时间。现在dt可以是调用此间隔的计时器间隔,或者如果我需要更高的精度,可以直接使用窗口dt测量PerformanceCounter。万一您遇到了无限循环,仍然可以使用:

Sleep(dt*1000.0);

这不精确,但可以使用。

有关动画和定时主题的更多信息,请参见此处和子链接:

现在回到BVH,这是我的C ++更新方法如下:

void bvh::update() // call periodically to animate
 {
 if ((_play)&&(dt>1e-6))
  { 
  int f=frame;
  t+=tper(&t0);
  while (t>=dt)
   {
   t-=dt;
   frame++;
   if (frame>=frames)
    {
    frame=0;
    if (!_loop){ stop(); break; }
   }
  }
  if (f!=frame) setframe(frame);
 }
}

这里是我BVH班上的一些精选内容:

List<int> root;             // root bones ix
List<bvh_bone> bone;        // HIERARCHY bvh
List<double> data;          // MOTION data
int frames;                 // MOTION frames
double dt;                  // MOTION delta time [ms]
int channels;               // channels/frame
// render
bool _redraw;               // out redraw needed?
// animation
bool _loop;                 // in loop playback?
bool _play,_pause,_stop;    // out animation buttons state?
int frame;                  // actual set frame
double t,t0;                // time elapsed from start of actual frame, measurement start time
void play() { tper(&t0); t=0.0; if (!_pause) frame=0; _play=1; _pause=0; _stop=0; setframe(frame); _redraw=true; }
void stop() { tper(&t0); t=0.0;                       _play=0; _pause=0; _stop=1; frame=0; setframe(frame); _redraw=true; }
void pause(){ tper(&t0); t=0.0; if (_stop) return;    _play=_pause; _pause=!_pause; }
void setframe(int frame);   // compute bones from frame data

tper函数测量从我的计时库获取的后续调用之间的时间:

const int   performance_max=64;                 // push levels
double      performance_Tms=-1.0,               // counter period [ms]
            performance_tms=0.0,                // measured time after tend [ms]
            performance_t0[performance_max];    // measured start times [ms]
int         performance_ix=-1;                  // actual time stack index

double tper(double *t0=NULL)    // return duration [ms] between tper() calls
    {
    double t,tt;
    LARGE_INTEGER i;
    if (performance_Tms<=0.0)
        {
        for (int j=0;j<performance_max;j++) performance_t0[j]=0.0;
        QueryPerformanceFrequency(&i); performance_Tms=1000.0/double(i.QuadPart);
        }
    QueryPerformanceCounter(&i); t=double(i.QuadPart); t*=performance_Tms;
    if (t0) { tt=t-t0[0]; t0[0]=t; performance_tms=tt; return tt; }
    performance_ix++;
    if ((performance_ix>=0)&&(performance_ix<performance_max))
        {
        tt=t-performance_t0[performance_ix];
        performance_t0[performance_ix]=t;
        }
    else { t=0.0; tt=0.0; };
    performance_ix--;
    performance_tms=tt;
    return tt;
    }

现在,在主应用程序循环/计时器中,只需定期调用update,如果_redraw为true,请将其设置为false并重新绘制应用程序。请注意,我的bvh::dt已转换为[ms] !!!