我正在尝试找到一种可靠的方法来准确设置我的 OpenGL 应用程序应渲染到屏幕上的 FPS。在某种程度上,我可以通过休眠 1000/fps 毫秒来做到这一点,但这没有考虑渲染所需的时间。 将 FPS 限制为所需量的最一致的方法是什么?
您可以使用 OpenGL 中的
wglSwapIntervalEXT
同步到 vblank。这不是很好的代码,但它确实有效。
http://www.gamedev.net/topic/360862-wglswapintervalext/#entry3371062
bool WGLExtensionSupported(const char *extension_name) {
PFNWGLGETEXTENSIONSSTRINGEXTPROC _wglGetExtensionsStringEXT = NULL;
_wglGetExtensionsStringEXT = (PFNWGLGETEXTENSIONSSTRINGEXTPROC)wglGetProcAddress("wglGetExtensionsStringEXT");
if (strstr(_wglGetExtensionsStringEXT(), extension_name) == NULL) {
return false;
}
return true;
}
和
PFNWGLSWAPINTERVALEXTPROC wglSwapIntervalEXT = NULL;
PFNWGLGETSWAPINTERVALEXTPROC wglGetSwapIntervalEXT = NULL;
if (WGLExtensionSupported("WGL_EXT_swap_control"))
{
// Extension is supported, init pointers.
wglSwapIntervalEXT = (PFNWGLSWAPINTERVALEXTPROC)wglGetProcAddress("wglSwapIntervalEXT");
// this is another function from WGL_EXT_swap_control extension
wglGetSwapIntervalEXT = (PFNWGLGETSWAPINTERVALEXTPROC)wglGetProcAddress("wglGetSwapIntervalEXT");
}
由于 OpenGL 只是一个低级图形 API,因此您不会在 OpenGL 中直接找到类似的内置内容。
不过,我觉得你的逻辑有点问题。而不是以下内容:
你应该这样做:
这样,如果您只等待应有的时间,那么您最终应该非常接近每秒 60 帧(或任何您想要的目标)。
不要使用睡眠。如果您这样做,那么您的申请的其余部分必须等待他们完成。
相反,跟踪已经过去了多少时间并仅在满足 1000/fps 时进行渲染。如果没有达到计时器,请跳过它并做其他事情。
在单线程环境中,很难确保您以精确的 1000/fps 进行绘制,除非这绝对是您所做的唯一事情。一种更通用和更可靠的方法是在单独的线程中完成所有渲染,并在计时器上启动/运行该线程。这是一个更复杂的问题,但会让您最接近您的要求。
此外,跟踪发出渲染所需的时间将有助于动态调整渲染的时间。
static unsigned int render_time=0;
now = timegettime();
elapsed_time = last_render_time - now - render_time;
if ( elapsed_time > 1000/fps ){
render(){
start_render = timegettime();
issue rendering commands...
end_render = timegettime();
render_time = end_render - start_render;
}
last_render_time = now;
}
OpenGL 本身没有任何允许限制帧速率的功能。期间。
然而,在现代 GPU 上有很多功能,涵盖帧率、帧预测等。John Carmack 提出了一个问题,他推动为其提供一些功能。还有 NVidia 的自适应同步。
这一切对你来说意味着什么?将其留给 GPU 处理。假设绘图是完全不可预测的(就像您只使用 OpenGL 时应该做的那样),请自行计时事件并将逻辑更新(例如物理)与绘图分开。这样用户将能够从所有这些先进技术中受益,而您不必再担心这一点。
一个简单的方法是使用 GLUT。这段代码可以大致完成这项工作。
static int redisplay_interval;
void timer(int) {
glutPostRedisplay();
glutTimerFunc(redisplay_interval, timer, 0);
}
void setFPS(int fps)
{
redisplay_interval = 1000 / fps;
glutTimerFunc(redisplay_interval, timer, 0);
}
将其放在绘制和调用交换缓冲区之后:
//calculate time taken to render last frame (and assume the next will be similar)
thisTime = getElapsedTimeOfChoice(); //the higher resolution this is the better
deltaTime = thisTime - lastTime;
lastTime = thisTime;
//limit framerate by sleeping. a sleep call is never really that accurate
if (minFrameTime > 0)
{
sleepTime += minFrameTime - deltaTime; //add difference to desired deltaTime
sleepTime = max(sleepTime, 0); //negative sleeping won't make it go faster :(
sleepFunctionOfChoice(sleepTime);
}
如果您想要 60fps,
minFrameTime = 1.0/60.0
(假设时间以秒为单位)。
这不会为您提供垂直同步,但意味着您的应用程序不会失控,这可能会影响物理计算(如果它们不是固定步长)、动画等。只需记住处理输入 睡眠后!我尝试过平均帧时间,但到目前为止效果最好。
对于
getElapsedTimeOfChoice()
,我使用这里提到的,即
另一个想法是使用 WaitableTimers(如果可能,例如在 Windows 上)
基本理念:
while (true)
{
SetWaitableTimer(myTimer, desired_frame_duration, ...);
PeekMsg(...)
if (quit....) break;
if (msg)
handle message;
else
{
Render();
SwapBuffers();
}
WaitForSingleObject(myTimer);
}
更多信息:如何限制 fps 信息