问题
Using GLFW3, I currently made a bvh file parser which reads the file in and converts it to a human model I made in opengl. However, whenever I launch it, the movement is so fast that the animation cannot be seen in the eye. So I want to tone the animation speed a little down. Here is my render loop
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();
}
The function inside the if statement bvh.setMotionPerFrame(bvh.root, 0)
is where the animation reads in the JOINT configurations per Frame data inside the file, and sets each joint's rotation matrix and translation matrix accordingly. (moveFlag
and resetMatrices
are flags that are set if spacebar and r button are hit respectively)
Since reading the channel data per frame in each render loop unchangeable, I want to come up with a way that I can slower the render loop speed itself. Any suggestions?
回答1:
You need to add timing to your code. while you are parsing BVH MOTION like this:
MOTION
Frames: 20
Frame Time: 0.033333
you should extract the Frame Time
value which is the time between frames in seconds. Now depending on the code architecture of yours you need to properly time your rendering. I am used to have some
bool _redraw=false;
telling whole app to redraw on next possible occasion (like timer) which is set by anything from mouse/keyboard events changing view or scene to resizing of window etc...
For time changing stuff I also usually have a function:
void update(double dt);
periodically called from app handling interpolation/simulation and stuff for dt
time elapsed from last call. Now the dt
can be timer interval that calls this or in case I need better accuracy I measure dt
directly using windows PerformanceCounter
. In case you just got endless loop you still can use:
Sleep(dt*1000.0);
which is not precise but will work.
More on animation and timing topic can be found in here and the sublinks:
- Sprites sequence control through DeltaTime
Now getting back to BVH here is how my C++ update method for it looks like:
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);
}
}
here some selected stuff from my BVH class:
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
And tper
function measures time between its subsequent calls taken from my timing lib:
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;
}
Now in main app loop/timer just call periodicaly the update
and if _redraw
is true set it to false and repaint your app. Beware my bvh::dt
was converted to [ms] !!!
来源:https://stackoverflow.com/questions/52804160/how-to-slow-down-opengl-animation-read-from-bvh-files