【问题标题】:Controlling Frame Rate控制帧速率
【发布时间】:2013-11-26 12:59:37
【问题描述】:

我在 VS 上使用 C++,使用 OpenGL 渲染/移动形状,使用 Win32 进行窗口显示等(从过剩显示移过来)

如何控制帧率?

我看到很多使用dt 和某种形式的帧刷新的示例,但我不确定如何实现这一点...有什么可以用作 Win32 的一部分,或者可以用更简单的方法完成吗?

另外,这可能是一个愚蠢的问题,但如果没有实现,默认帧速率是多少?还是没有?

【问题讨论】:

  • 我写了一个关于限制帧率的简短回答here

标签: c++ winapi opengl glut frame-rate


【解决方案1】:

我的 USB 驱动器上有一些非常旧的代码,它们使用旧的 OpenGL 和 glut,即使在更现代的版本中也适用相同的计时原则,但绘制代码会有所不同。该代码用于不精确的计时,但足以说明如何大致实现设定的 FPS:

// throttle the drawing rate to a fixed FPS
//compile with: g++ yourfilenamehere.cpp -lGL -lglut
#include <cstdlib>
#include <iostream>

#include <GL/gl.h>
#include <GL/glut.h>

GLint FPS = 0;

void FPS(void) {
  static GLint frameCounter = 0;         // frames averaged over 1000mS
  static GLuint currentClock;             // [milliSeconds]
  static GLuint previousClock = 0; // [milliSeconds]
  static GLuint nextClock = 0;     // [milliSeconds]

  ++frameCounter;
  currentClock = glutGet(GLUT_ELAPSED_TIME); //has limited resolution, so average over 1000mS
  if ( currentClock < nextClock ) return;

  FPS = frameCounter/1; // store the averaged number of frames per second

  previousClock = currentClock;
  nextClock = currentClock+1000; // set the next clock to aim for as 1 second in the future (1000 ms)
  frameCounter=0;
}

void idle() {
  static GLuint previousClock=glutGet(GLUT_ELAPSED_TIME);
  static GLuint currentClock=glutGet(GLUT_ELAPSED_TIME);
  static GLfloat deltaT;

  currentClock = glutGet(GLUT_ELAPSED_TIME);
  deltaT=currentClock-previousClock;
  if (deltaT < 35) {return;} else {previousClock=currentClock;}

  // put your idle code here, and it will run at the designated fps (or as close as the machine can get

  printf(".");
  //end your idle code here

  FPS(); //only call once per frame loop 
  glutPostRedisplay();
}

void display() {
  glClearColor(0.0, 0.0, 0.0, 0.0);
  glClear(GL_COLOR_BUFFER_BIT);

  // Set the drawing color (RGB: WHITE)
  printf("FPS %d\n",FPS);
  glColor3f(1.0,1.0,1.0);

  glBegin(GL_LINE_STRIP); {
     glVertex3f(0.25,0.25,0.0);
     glVertex3f(0.75,0.25,0.0);
     glVertex3f(0.75,0.75,0.0);
     glVertex3f(0.25,0.75,0.0);
     glVertex3f(0.25,0.25,0.0);
  }
  glEnd(); 

  glutSwapBuffers();
}

void init() {
   glMatrixMode(GL_PROJECTION);
   glLoadIdentity();
   glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0); 
}

void keyboard(unsigned char key, int x, int y)
{
   switch (key) {
      case 27:  // escape key
         exit(0);
         break;
      default:
         break;
   }
}

int main(int argc, char** argv) {
   glutInit(&amp;argc, argv);
   glutInitDisplayMode (GLUT_DOUBLE | GLUT_RGB);
   glutCreateWindow("FPS test");

   glutIdleFunc(idle);
   glutDisplayFunc(display);
   glutKeyboardFunc(keyboard);

   init();

   glutMainLoop();
   return 0;
}

希望这会有所帮助 :) 如果您需要更多信息,请告诉我。

【讨论】:

  • 太棒了,我会调查一下。感谢您的发布。非常友好:D
  • 如果您开始寻找更精细的计时解决方案,请告诉我您的进展情况,我会尽我所能提供帮助:)
  • 我认为这个答案即使在今天也很有帮助
【解决方案2】:

如果您不打算构建多用户系统,则无需固定帧速率(您可以使用 QueryPerformanceCounter 测量上一帧所用的时间,并假设下一帧大约需要同样长的时间)。然后你只根据帧时间移动对象。

如果您在此模型中应用力/加速度,您可能需要进行补偿,例如使用velocity Verlet integration

但是,固定帧速率可能会有点麻烦,尤其是在每帧 CPU/GPU 负载变化很大的情况下。

固定帧率的简单版本:

如果您只是在机器上平稳移动并想要固定帧速率,请实现以下伪代码:

fps = 30    # Pick something good for you, 30 and 60 are common values.
main_loop:
    t0 = time()
    update_and_render(1/fps)
    t1 = time()
    frame_time = t1-t0
    sleep(1/fps - frame_time)
    goto main_loop

frame_time 在此示例中是您的示例所指的dt(“增量时间”),但大多数示例没有固定的帧速率。相反,它们会根据上一帧花费的时间来移动您的精灵。

不同的帧速率也一样:

last_frame_rate = 1/100
main_loop:
    t0 = time()
    update_and_render(last_frame_rate)
    t1 = time()
    last_frame_rate = t1-t0
    goto main_loop

【讨论】:

  • 有趣...我将看看/阅读这些链接。我正在研究的唯一原因是因为即使我每次移动我的形状0.01f,有时它移动得比其他人更快,我把它归结为一个波动的帧速率?因此想知道我是否应该修复它。感谢发帖。
  • 是的,你的帧率在机器之间会有所不同,这是正确的(在快速计算机上,动画应该更流畅)。如果您愿意,可以在主循环中添加一些微小的睡眠,以不消耗所有 CPU,这会使系统的其余部分更具响应性。
  • 波动是当我在同一个系统上的同一个程序上时(什么都不改变)。比如我现在运行这个程序,可能会很流畅,但是如果我在一个小时内运行它,它可能会响应更慢或更快......我不知道是不是因为我在VS中执行它在 Macbook pro 上的 windows7 上,双启动...也许配置不同或什么...我一直认为它会因系统而异,但执行之间没有区别...
  • 这完全取决于您还运行什么。如果您在同一台计算机上托管 Web 服务器,或者某些防病毒软件正在检查您的驱动器,或者操作系统已经安排了某些任务,或者浏览器正在后台下载某些内容,或者......您会得到图片。
  • 嗯,好的。我懂了。我会看看我是否可以实现一个小的帧率检查器或其他东西。谢谢。
【解决方案3】:

回答您的问题:帧速率取决于显卡的性能。

dt 表示增量时间,即自上一帧或更新以来的时间。根据您想要实现的目标,您可以测量两帧之间的 dt 并使用此更新对象的位置。

您可以通过时间度量来控制帧速率: 如果您想强制 30FPS 并且您知道 rt 它是渲染帧所需的时间,那么您可以在零时间渲染一帧,然后在时间 1/30 - rt 渲染一帧,依此类推开。

在概念更清晰的模型中,您可以将渲染与数据模型的更新分开,这样您就可以使用 1/30 秒之类的修复 dt 来更新您的位置等。在这个模型中渲染尽可能频繁地运行,因此您存储最后两个位置。然后,渲染帧函数可以使用介于 0-1 之间的 interpolation 参数来插入位置。使用此模型您可以获得以下几个优势:

  • 无需渲染即可模拟
  • 您的数据得到了确定性模拟,因为您的 dt 已修复
  • dt 可用于轻松实现渲染的慢动作或双快回放。

【讨论】:

  • 当我运行我的程序时,我可以将立方体移动0.01f,有时它的移动速度比其他时间快,即使程序中没有任何变化,我只能假设这是由于帧率波动...这就是为什么我想设置帧率...你同意吗?还是我将这种奇怪的行为与其他东西混淆了?
  • 是的,我认为你是对的,但不要尝试强制帧速率,而是尝试获取帧之间的时间 dt 并将立方体移动 0.1f*dt这意味着移动取决于程序渲染帧所需的时间
【解决方案4】:

使用 time_t 或 clock_t 通常会给出 0 增量,而不是使用精确的 windows profile.h API

#include<profil.h>

#define FRAMES_PER_SEC 60.0

void tickfbs();

LARGE_INTEGER frequency;        // used for ticks per second
LARGE_INTEGER t1, t2;           // used for storing ticks passed
double elapsedTime;

int main(void){
    QueryPerformanceFrequency(&frequency);
    for(;;){
        //do something(drawing)
        tickfbs();//wait for the delta time between each iteration and the fbs ratio
    }
    return 0;
}

//this will wait for the amount of delta time between this call of the function and the last call
void tickfbs(){
    QueryPerformanceCounter(&t2);
    elapsedTime = (t2.QuadPart - t1.QuadPart) / frequency.QuadPart * 1000;
    if(elapsedTime < FRAMES_PER_SEC){
        delay(FRAMES_PER_SEC-elapsedTime);
    }
    t1 = t2;
}

【讨论】:

    猜你喜欢
    • 2011-09-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-16
    • 1970-01-01
    • 1970-01-01
    • 2015-10-10
    • 1970-01-01
    相关资源
    最近更新 更多