Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/71.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 使用GLUT的OpenGL中与可变FPS无关的恒定游戏速度?_C_Opengl_Glut_Game Loop - Fatal编程技术网

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

C 使用GLUT的OpenGL中与可变FPS无关的恒定游戏速度?,c,opengl,glut,game-loop,C,Opengl,Glut,Game Loop,我一直在阅读Koen Witters关于不同游戏循环解决方案的文章,但是我在用GLUT实现最后一个解决方案时遇到了一些问题,这是推荐的解决方案 在阅读了其他人关于如何实现恒定游戏速度的几篇文章、教程和代码后,我认为我目前实现的(我将在下面发布代码)是Koen Witters所说的依赖于可变FPS的游戏速度,这是他文章中的第二个 首先,根据我的搜索经验,可能有一些人对此有帮助,但不知道GLUT是什么,我将尝试解释(请随意更正)我的OpenGL工具包问题的相关函数。如果您知道什么是过剩以及如何使用过

我一直在阅读Koen Witters关于不同游戏循环解决方案的文章,但是我在用GLUT实现最后一个解决方案时遇到了一些问题,这是推荐的解决方案

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

首先,根据我的搜索经验,可能有一些人对此有帮助,但不知道GLUT是什么,我将尝试解释(请随意更正)我的OpenGL工具包问题的相关函数。如果您知道什么是过剩以及如何使用过剩,请跳过本节

GLUT工具包:

  • GLUT是一个OpenGL工具包,帮助处理OpenGL中的常见任务
  • glutDisplayFunc(renderScene)
    获取指向
    renderScene()
    函数回调的指针,该回调将负责呈现所有内容。
    renderScene()
    函数仅在回调注册后调用一次
  • glutTimerFunc(TIMER\u毫秒,processAnimationTimer,0)
    在调用回调
    processAnimationTimer()
    之前需要经过的毫秒数。最后一个参数只是传递给计时器回调的值。
    processAnimationTimer()
    不会在每个
    TIMER\u毫秒调用一次,而只调用一次
  • glutPostRedisplay()
    函数请求GLUT渲染一个新帧,因此我们需要在每次更改场景中的某些内容时调用它
  • glutIdleFunc(renderScene)
    可用于注册对
    renderScene()
    的回调(这不会使
    glutDisplayFunc()
    不相关),但应避免使用此函数,因为在未接收到事件时会连续调用空闲回调,从而增加CPU负载
  • glutGet(GLUT经过的时间)
    函数返回调用
    glutInit
    后的毫秒数(或第一次调用
    glutGet(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\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
}