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

2024-01-06

我一直在读科恩·维特斯详细文章 http://www.koonsolo.com/news/dewitters-gameloop/关于不同的游戏循环解决方案,但我在使用 GLUT 实现最后一个解决方案时遇到了一些问题,这是推荐的解决方案。

在阅读了其他人关于如何实现恒定游戏速度的几篇文章、教程和代码后,我认为我目前已经实现的(我将在下面发布代码)就是 Koen Witters 所说的游戏速度取决于可变 FPS,他的文章中的第二个。

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

过剩工具包:

  • GLUT 是一个 OpenGL 工具包,可帮助完成 OpenGL 中的常见任务。
  • The glutDisplayFunc(renderScene)接受一个指向 a 的指针renderScene()函数回调,它将负责渲染所有内容。这renderScene()回调函数注册后只会被调用一次。
  • The glutTimerFunc(TIMER_MILLISECONDS, processAnimationTimer, 0)调用回调之前经过的毫秒数processAnimationTimer()。最后一个参数只是传递给计时器回调的值。这processAnimationTimer()不会被称为每个TIMER_MILLISECONDS但只有一次。
  • The glutPostRedisplay()函数请求 GLUT 渲染一个新帧,因此每次我们更改场景中的某些内容时都需要调用它。
  • The glutIdleFunc(renderScene)可用于注册回调renderScene()(这并不使glutDisplayFunc()不相关),但应避免使用此函数,因为在未接收到事件时会不断调用空闲回调,从而增加 CPU 负载。
  • The 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 点所需的时间都是相同的。然而,我相信我用这种方法限制了游戏帧率。 [EDIT:仅当调用时间回调时才会渲染每一帧,这意味着帧率将大致约为TICKS_PER_SECOND每秒帧数。这感觉不对,你不应该限制你强大的硬件,这是错误的。但据我了解,我仍然需要计算elapsedTime。只是因为我告诉 GLUT 调用计时器回调TIMER_MILLISECONDS,这并不意味着它总是会按时完成。]

我不知道如何解决这个问题,说实话,我不知道 GLUT 中的游戏循环是什么,你知道,while( game_is_running )Koen 的文章中循环。 [EDIT:我的理解是 GLUT 是事件驱动当我打电话时游戏循环就开始了glutMainLoop()(永远不会回来),是吗?]

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

Using glutTimerFunc()绝对不是基于此执行所有运动/动画的好方法,因为我将游戏限制为恒定的 FPS,这并不酷。或者也许我使用错误并且我的实现不正确?

究竟如何才能在可变 FPS 的情况下保持恒定的游戏速度?更准确地说,我如何正确实施 Koen 的恒定的游戏速度和最大 FPSGLUT 的解决方案(他文章中的第四个)?也许对于 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使用率会增加;这是正常行为。当我停止移动时,它会回落。

除非我遗漏了什么,否则目前看来这是一个不错的方法。我确实找到了这篇有趣的文章 http://www.idevgames.com/articles/timebasedanimation在 iDevGames 上,此实现可能受到该文章中描述的问题的影响。你对此有何看法?

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

你怎么认为?


过剩的目的是be游戏循环。当您调用 glutMainLoop() 时,它会执行一个“for 循环”,除了 exit() 信号外,没有终止条件。您可以像现在一样实现您的程序,但您需要一些细微的更改。首先,如果您想知道 FPS 是多少,您应该将该跟踪放入 renderScene() 函数中,而不是放在更新函数中。当然,您的更新函数会按照计时器指定的速度被调用,并且您将 elapsedTime 视为帧之间时间的度量。一般来说,这是正确的,因为您调用 glutPostRedisplay 的速度相当慢,并且 glut 在不需要时不会尝试更新屏幕(如果场景没有更改,则无需重绘)。然而,也有其他时候会调用 renderScene。例如,如果您将某些内容拖过窗口。如果您这样做,您会看到更高的 FPS(如果您在渲染函数中正确跟踪 FPS)。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

OpenGL 中的恒定游戏速度与 GLUT 中的可变 FPS 无关? 的相关文章

随机推荐

  • 为什么我的 React 组件没有随着状态更新而更新?

    我构建了一个地图应用程序 需要在按下按钮后显示 消失一些地图图标 但当我从其父组件传递新的运动属性时 我不知道如何将其设置为重新渲染组件 父加载组件
  • Mysql 中使用 select where 查询区分大小写

    嗨 我正在使用 Java 前端和 Mysql 后端 其实在tbl test包含 name value abc 22 xyz 14 ABC 32 xyZ 4 ABc 4 在java中我尝试检索abc的值于是写了一段代码 ResultSet r
  • pandas 将数据帧转为 3D 数据

    似乎有很多可能性可以将平面表数据转换为 3d 数组 但我不知何故找不到一种有效的方法 假设我有一些带有 columns name type date 的数据价值 当我尝试通过 pivot index name columns type da
  • CSS 截掉输入框的末尾

    老天爷 为什么我的输入框右侧被切掉了 我研究了 chrome 中的填充和边距 但看不出是什么原因造成的 我对此很陌生 但这仍然是一个谜 http jsfiddle net GCt3z 1 http jsfiddle net GCt3z 1
  • DoctrineExtensions 软删除

    我正在使用 Doctrine2 设置 symfony2 并且我想使用 DoctrineExtensions Gedmo 我遵循了每一步 大多数都在工作 但我无法找到需要更改的配置文件 SoftDeleteable 可以工作 https gi
  • hive 版本 0.13.1 中的性能问题

    I use AWS EMR http docs aws amazon com ElasticMapReduce latest DeveloperGuide UsingEMR SupportedHiveVersions html运行我的 Hi
  • SwiftUI 中文本的自定义字体大小

    我认为有一个标签 我想使用系统字体大小为medium 大小为21点 我创建了一个自定义扩展来重新使用创建的字体 extension Font static var primaryButton Font return Font custom
  • 鼠标滚动选中和取消选中 DataGrid 中的复选框

    private void OnChecked object sender RoutedEventArgs e try LAB TEST t new LAB TEST CheckBox chk CheckBox e OriginalSourc
  • 如何使用 Axis WSDL2Java 生成的文件?

    我使用 WSDL2Java 转换器从 WSDL 生成了 Java 文件 但我不知道如何将服务与这些文件一起使用 因为没有示例 我正在实施客户端 关于 Axis2 阅读这些链接 它们包含一些示例 http ws apache org axis
  • 如何输入自定义钩子 useStateWithCallback React TypeScript

    我在输入以下自定义 React 时遇到问题hook 我是 TypeScript 新手 这引起了一些混乱 const useStateCallback initialState any gt const state setState useR
  • 有什么方法可以确定 Sonata\AdminBundle\Admin\Admin::configureFormFields() 中的当前操作(创建或编辑)?

    我想为 Sonata Admin Bundle 中的创建和编辑操作创建不同的字段配置 除了检查还有什么方法可以确定吗 this gt getSubject gt getId in Sonata AdminBundle Admin Admin
  • 在临时表/变量中:将多行连接到只有 1 行的表

    Hi 我有一个将临时变量与临时表连接的小问题 任何意见将不胜感激 我按照我尝试解决问题的顺序来呈现问题 首先 我有一个从 select 语句创建的临时变量 变量 enhet 有 2 行 观察 在我的测试文件中 稍后会更多 declare e
  • 如何为所有延迟加载模块提供自定义提供程序

    我在我的应用程序中使用子组件的延迟加载策略 在应用程序的顶层 我有自定义 HTTP 提供程序来拦截所有 ajax 调用 providers provide Http useFactory backend XHRBackend default
  • 仅适用于字符 a-z、A-Z 的正则表达式

    我不知道如何在 JavaScript 或 jQuery 中创建正则表达式 我想创建一个正则表达式来检查字符串是否仅包含 a z 和 A Z 之间的任意排列的字符 EDIT 当我尝试制作正则表达式时 a zA Z s 也接受空格 它不起作用
  • 如何知道要使用哪个版本的类型包

    我知道一些 npm 包不包含类型 并且由于社区创建了 types packagename 来提供类型 由于两者都是包 如何知道哪个版本的类型包与所选版本的库包一起使用 主要版本号和次要版本号 types packagename包将匹配的主要
  • 我应该始终检查成员指针是否为 nullptr?

    做这样的事情是不是很糟糕 在对对象指针进行操作之前 不检查draw 函数内部是否有nullptr class SomeClass public SomeClass Object someValidObject object someVali
  • Scala:删除最后一次出现的字符

    我正在尝试删除字符串中最后一次出现的字符 我可以得到它的index str lastIndexOf 我已经尝试过使用split和replace字符串上的函数 你可以使用patch scala gt val s s dfkj w erw s
  • Javascript for...of 在 Safari 中不起作用

    目前 我正在尝试构建一个简单的侧面导航 只要单击 toggleSidenav 按钮之一 有多个 该导航就会出现 消失 在使用 Firefox 和 Chrome 进行测试时 它似乎工作正常 但今天当我尝试使用 Safari 桌面版和移动版 打
  • Docker 组合 |虚拟主机

    我的代码有什么问题吗 提前致谢 我正在尝试为我的 docker 容器设置虚拟主机 在 localhost 8000 上工作正常 但是当我尝试通过 http borgesmelo local 访问时 出现错误 ERR NAME NOT RES
  • OpenGL 中的恒定游戏速度与 GLUT 中的可变 FPS 无关?

    我一直在读科恩 维特斯详细文章 http www koonsolo com news dewitters gameloop 关于不同的游戏循环解决方案 但我在使用 GLUT 实现最后一个解决方案时遇到了一些问题 这是推荐的解决方案 在阅读了