c - 恒定的游戏速度与GLUT在OpenGL中与可变FPS无关吗?

标签 c opengl glut game-loop

我一直在阅读有关不同游戏循环解决方案的Koen Witters detailed article,但是我在使用GLUT实现最后一个方面遇到了一些问题,这是推荐的解决方案。

在阅读过其他一些关于如何实现恒定游戏速度的文章,教程和代码后,我认为我目前实现的(我将在下面的代码中发布)就是Koen Witters所说的依赖于可变FPS的游戏速度。 ,他文章的第二篇。

首先,根据我的搜索经验,可能会有一些人对此有帮助的知识,但是他们不知道GLUT是什么,因此我将尽力解释(随时纠正我)与之相关的功能我的OpenGL工具包的问题。如果您知道什么是GLUT以及如何使用它,请跳过本节。

GLUT工具包:


GLUT是OpenGL工具包,可帮助完成OpenGL中的常见任务。
glutDisplayFunc(renderScene)带有指向renderScene()函数回调的指针,该函数负责呈现所有内容。 renderScene()函数将仅在回调注册后被调用一次。
glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0)在调用回调processAnimationTimer()之前花费了毫秒数。最后一个参数只是传递给计时器回调的值。 processAnimationTimer()不会被每个TIMER_MILLISECONDS调用,而只会被调用一次。
glutPostRedisplay()函数请求GLUT渲染新帧,因此每次我们更改场景中的内容时都需要调用此帧。
glutIdleFunc(renderScene)可以用于注册到renderScene()的回调(这不会使glutDisplayFunc()无关紧要),但应避免使用此函数,因为在未接收到事件时会连续调用空闲回调,从而增加了CPU负载。
glutGet(GLUT_ELAPSED_TIME)函数返回自调用glutInit(或首次调用glutGet(GLUT_ELAPSED_TIME))以来的毫秒数。那就是我们使用GLUT的计时器。我知道高分辨率计时器还有更好的选择,但是现在让我们继续使用它。


我认为这是有关GLUT如何渲染帧的足够信息,因此不知道它的人也可以提出这个问题,以尝试帮助他们,如果他们喜欢它。

当前实施:

现在,我不确定我是否已经正确实施了Koen提出的第二种解决方案,即依赖可变FPS的游戏速度。与此相关的代码如下所示:

#define TICKS_PER_SECOND 30
#define MOVEMENT_SPEED 2.0f

const int TIMER_MILLISECONDS = 1000 / TICKS_PER_SECOND;

int previousTime;
int currentTime;
int elapsedTime;

void renderScene(void) {
    (...)

    // Setup the camera position and looking point
    SceneCamera.LookAt();

    // Do all drawing below...

    (...)
}

void processAnimationTimer(int value) {
    // setups the timer to be called again
    glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0);

    // Get the time when the previous frame was rendered
    previousTime = currentTime;

    // Get the current time (in milliseconds) and calculate the elapsed time
    currentTime = glutGet(GLUT_ELAPSED_TIME);
    elapsedTime = currentTime - previousTime;

    /* Multiply the camera direction vector by constant speed then by the
       elapsed time (in seconds) and then move the camera */
    SceneCamera.Move(cameraDirection * MOVEMENT_SPEED * (elapsedTime / 1000.0f));

    // Requests to render a new frame (this will call my renderScene() once)
    glutPostRedisplay();
}

void main(int argc, char **argv) {
    glutInit(&argc, argv);

    (...)

    glutDisplayFunc(renderScene);

    (...)

    // Setup the timer to be called one first time
    glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0);
    // Read the current time since glutInit was called
    currentTime = glutGet(GLUT_ELAPSED_TIME);

    glutMainLoop();
}


此实现不正确。从某种意义上说,它可以帮助游戏速度始终取决于FPS。因此,无论高帧率还是低帧率,从点A到点B都需要花费相同的时间。但是,我相信我用这种方法限制了游戏的帧率。 [编辑:仅在调用时间回调时才渲染每个帧,这意味着帧速率大约为每秒TICKS_PER_SECOND个帧。这感觉不对,您不应该限制强大的硬件,这是错误的。据我了解,我仍然需要计算elapsedTime。只是因为我要告诉GLUT每个TIMER_MILLISECONDS调用计时器回调,但这并不意味着它将始终按时执行。]

我不确定如何解决这个问题,说实话,我不知道GLUT中的游戏循环是什么,你知道Koen文章中的while( game_is_running )循环。 [编辑:据我了解,GLUT是事件驱动的,当我调用glutMainLoop()(永不返回)时,游戏循环便开始了,是吗?]

我以为我可以向glutIdleFunc()注册一个空闲的回调并将其用作glutTimerFunc()的替换,仅在必要时渲染(而不是像往常一样),但是当我使用空回调(例如void gameLoop() {})对其进行测试时基本上什么也没做,只有黑屏,CPU飙升到25%并保持在那里,直到我杀死了游戏,然后它恢复了正常。所以我不认为这是可以遵循的道路。

因此,使用glutTimerFunc()绝对不是一种执行所有动作/动画的好方法,因为我将游戏限制为恒定的FPS,而不是炫酷。还是我使用错了,而我的实现不正确?

可变FPS时,我如何才能保持恒定的游戏速度?更确切地说,我该如何使用GLUT正确实现Koen的恒定游戏速度和最大FPS解决方案(他的文章中的第四个解决方案)?也许用GLUT根本不可能吗?如果没有,我有什么选择?使用GLUT解决此问题(恒定游戏速度)的最佳方法是什么?

[编辑]另一种方法:

我一直在尝试,这是我现在能够实现的目标。现在,不是在计时功能上计算经过的时间(这会限制游戏的帧速率),而是在renderScene()中进行计算。每当场景发生变化时,我都会调用glutPostRedisplay()(即:摄像机移动,某些对象动画等),这将调用renderScene()。例如,我可以在此功能中使用经过的时间来移动相机。

我的代码现在变成了:

int previousTime;
int currentTime;
int elapsedTime;

void renderScene(void) {
    (...)

    // Setup the camera position and looking point
    SceneCamera.LookAt();

    // Do all drawing below...

    (...)
}

void renderScene(void) {
    (...)

    // Get the time when the previous frame was rendered
    previousTime = currentTime;

    // Get the current time (in milliseconds) and calculate the elapsed time
    currentTime = glutGet(GLUT_ELAPSED_TIME);
    elapsedTime = currentTime - previousTime;

    /* Multiply the camera direction vector by constant speed then by the
       elapsed time (in seconds) and then move the camera */
    SceneCamera.Move(cameraDirection * MOVEMENT_SPEED * (elapsedTime / 1000.0f));

    // Setup the camera position and looking point
    SceneCamera.LookAt();

    // All drawing code goes inside this function
    drawCompleteScene();

    glutSwapBuffers();

    /* Redraw the frame ONLY if the user is moving the camera
       (similar code will be needed to redraw the frame for other events) */
    if(!IsTupleEmpty(cameraDirection)) {
        glutPostRedisplay();
    }
}

void main(int argc, char **argv) {
    glutInit(&argc, argv);

    (...)

    glutDisplayFunc(renderScene);

    (...)

    currentTime = glutGet(GLUT_ELAPSED_TIME);

    glutMainLoop();
}


结论,它正在起作用,或者看起来如此。如果我不移动相机,则CPU使用率很低,没有渲染任何内容(出于测试目的,我只有一个扩展为4000.0f的网格,而zFar设置为1000.0f)。当我开始移动相机时,场景开始重绘。如果我一直按移动键,则CPU使用率会增加;这是正常现象。当我停止移动时,它回落。

除非我缺少任何东西,否则目前看来是一个不错的方法。我确实在iDevGames上找到了this interesting article,并且此实现可能受到该文章中所述问题的影响。您对此有何看法?

请注意,我这样做只是出于娱乐目的,我无意创建一些要分发的游戏或类似的东西,至少在不久的将来不会。如果这样做的话,我可能会选择除GLUT之外的其他东西。但是,由于我使用的是GLUT,而不是iDevGames上描述的问题,因此您认为此最新实现对GLUT足够了吗?我现在能想到的唯一真正的问题是,每次场景变化时,我都需要继续调用glutPostRedisplay()并不断调用它,直到没有新内容可以重绘为止。我认为,为了更好的原因,在代码中添加了一些复杂性。

你怎么看?

最佳答案

过剩被设计为游戏循环。当您调用glutMainLoop()时,它将执行一个“ for循环”,除了exit()信号外,没有终止条件。您可以像现在一样执行程序,但是需要一些小的更改。首先,如果您想知道什么是FPS,则应将该跟踪放入renderScene()函数中,而不要放在update函数中。自然地,您的更新函数将按照计时器指定的速度被调用,并且您将elapsedTime视为帧之间时间的度量。通常,这是正确的,因为您调用glutPostRedisplay的速度很慢,并且在不需要时glut不会尝试更新屏幕(如果场景未更改,则无需重绘)。但是,有时还会调用renderScene。例如,如果您在窗口上拖动某些东西。如果这样做的话,您会看到更高的FPS(如果您在渲染功能中正确跟踪了FPS)。

关于c - 恒定的游戏速度与GLUT在OpenGL中与可变FPS无关吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5357119/

相关文章:

c - 如何使用指针到指针的指针在此链表中插入项

c - 使用 fbo 和纹理的离屏渲染得到黑屏

haskell - Haskell Opengl 的光泽度

c++ - 教程代码不显示 FreeType 库

c++ - OpenGL 对象没有正确渲染?

c++ - 多态性 : "A pointer to a bound function may only be used to call the function"

c - 我应该管理页面还是只依赖虚拟内存?

c++ - 指针声明基础(多维数组指针赋值)

c - 在C中访问txt文件表中的行/列

java - 3d vector 的全局角度