C 使用GLUT的OpenGL中与可变FPS无关的恒定游戏速度?
我一直在阅读Koen Witters关于不同游戏循环解决方案的文章,但是我在用GLUT实现最后一个解决方案时遇到了一些问题,这是推荐的解决方案 在阅读了其他人关于如何实现恒定游戏速度的几篇文章、教程和代码后,我认为我目前实现的(我将在下面发布代码)是Koen Witters所说的依赖于可变FPS的游戏速度,这是他文章中的第二个 首先,根据我的搜索经验,可能有一些人对此有帮助,但不知道GLUT是什么,我将尝试解释(请随意更正)我的OpenGL工具包问题的相关函数。如果您知道什么是过剩以及如何使用过剩,请跳过本节 GLUT工具包:C 使用GLUT的OpenGL中与可变FPS无关的恒定游戏速度?,c,opengl,glut,game-loop,C,Opengl,Glut,Game Loop,我一直在阅读Koen Witters关于不同游戏循环解决方案的文章,但是我在用GLUT实现最后一个解决方案时遇到了一些问题,这是推荐的解决方案 在阅读了其他人关于如何实现恒定游戏速度的几篇文章、教程和代码后,我认为我目前实现的(我将在下面发布代码)是Koen Witters所说的依赖于可变FPS的游戏速度,这是他文章中的第二个 首先,根据我的搜索经验,可能有一些人对此有帮助,但不知道GLUT是什么,我将尝试解释(请随意更正)我的OpenGL工具包问题的相关函数。如果您知道什么是过剩以及如何使用过
- GLUT是一个OpenGL工具包,帮助处理OpenGL中的常见任务
获取指向glutDisplayFunc(renderScene)
函数回调的指针,该回调将负责呈现所有内容。renderScene()
函数仅在回调注册后调用一次renderScene()
在调用回调glutTimerFunc(TIMER\u毫秒,processAnimationTimer,0)
之前需要经过的毫秒数。最后一个参数只是传递给计时器回调的值。processAnimationTimer()
不会在每个processAnimationTimer()
TIMER\u毫秒调用一次,而只调用一次
函数请求GLUT渲染一个新帧,因此我们需要在每次更改场景中的某些内容时调用它glutPostRedisplay()
可用于注册对glutIdleFunc(renderScene)
的回调(这不会使renderScene()
不相关),但应避免使用此函数,因为在未接收到事件时会连续调用空闲回调,从而增加CPU负载glutDisplayFunc()
函数返回调用glutGet(GLUT经过的时间)
后的毫秒数(或第一次调用glutInit
)。这就是我们与过剩的计时器。我知道有更好的高分辨率定时器替代品,但现在让我们继续使用这个glutGet(GLUT经过的时间)
#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\u
帧。这感觉不对,你不应该限制你强大的硬件,这是错误的。但我的理解是,我仍然需要计算elapsedTime
。仅仅因为我告诉GLUT每隔timer\u毫秒调用一次timer回调,并不意味着它总是按时调用。]
我不知道如何解决这个问题,老实说,我不知道GLUT中的游戏循环是什么,你知道,Koen文章中的while(game\u正在运行)
循环。[编辑:我的理解是,GLUT是事件驱动的,当我调用glutMainLoop()
(永远不会返回)时,游戏循环开始,是吗?]
我原以为我可以用gluidlefunc()
注册一个空闲回调,并用它代替glutTimerFunc()
,只在必要时渲染(而不是像往常一样一直渲染),但当我用空回调(如void gameLoop(){}
)测试时,它基本上什么都没做,只是一个黑屏,CPU峰值达到25%,一直保持在那里,直到我结束游戏,它恢复正常。所以我不认为这是应该走的路
使用glutTimerFunc()
绝对不是执行所有基于此的动作/动画的好方法,因为我将我的游戏限制为恒定的FPS,这并不酷。或者我用错了,我的实现不对
我怎样才能在可变FPS下保持恒定的游戏速度?更确切地说,我如何正确地实现Koen的恒定游戏速度和最大FPS解决方案(他文章中的第四个)和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)。当我开始移动摄影机时,场景开始重新绘制自身。如果我一直按mov
int xSt, ySt, xCr, yCr, msM = 0, msOld = 0;
bool dragging = false, spin = false, moving = false;
glm::mat4 mouseRot(1.0f), continRot(1.0f);
float twoOvHght; // Set in reshape()
glm::mat4 mouseRotate(bool slow) {
glm::vec3 axis(twoOvHght * (yCr - ySt), twoOvHght * (xCr - xSt), 0); // Perpendicular to mouse motion
float len = glm::length(axis);
if (slow) { // Slow rotation; divide angle by mouse-delay in milliseconds; it is multiplied by frame delay to speed it up later
int msP = msM - msOld;
len /= (msP != 0 ? msP : 1);
}
if (len != 0) axis = glm::normalize(axis); else axis = glm::vec3(0.0f, 0.0f, 1.0f);
return rotate(axis, cosf(len), sinf(len));
}
void mouseMotion(int x, int y) {
moving = (xCr != x) | (yCr != y);
if (dragging & moving) {
xSt = xCr; xCr = x; ySt = yCr; yCr = y; msOld = msM; msM = glutGet(GLUT_ELAPSED_TIME);
mouseRot = mouseRotate(false) * mouseRot;
}
}
void mouseButton(int button, int state, int x, int y) {
if (button == 0) {
if (state == 0) {
dragging = true; moving = false; spin = false;
xCr = x; yCr = y; msM = glutGet(GLUT_ELAPSED_TIME);
glutPostRedisplay();
} else {
dragging = false; spin = moving;
if (spin) continRot = mouseRotate(true);
}
}
}
bool fxFPS = false;
int T = 0, ms = 0;
const int fDel = 20;
void display() {
ms = glutGet(GLUT_ELAPSED_TIME);
if (T <= ms) { T = ms + fDel;
for (int lp = 0; lp < fDel; lp++) {
orient = rotY * orient; orientCu = rotX * rotY * orientCu; // Auto-rotate two orientation quaternions
if (spin) mouseRot = continRot * mouseRot; // Track rotation from thowing action by mouse
}
orient1 = glm::mat4_cast(orient); orient2 = glm::mat4_cast(orientCu);
}
// Top secret animation code that will make me rich goes here
glutSwapBuffers();
if (spin | dragging) { if (fxFPS) while (glutGet(GLUT_ELAPSED_TIME) < T); glutPostRedisplay(); } // Fast, repeated updates of the screen
}