Будут ли обновления OpenGL синхронизированы с вертикальным возвратом, будет сообщаться в зависимости от платформы, поскольку это связано с тем, как ваша платформа привязала OpenGL к себе.Так что в конструкторе интерфейсов на Mac установлен флажок, есть расширение WGL под названием WGL_EXT_swap_control over в Windows и расширение GLX в X11.
Предполагается, что у вас есть некоторые средства для установки обратного вызова, который запускается при вертикальном возврате(например, через CVDisplayLink
на Mac; к сожалению, я не знаком с Windows / Linux-эквивалентами), и вы хотите, чтобы ваша физика работала с постоянным числом шагов в секунду, вы могли бы написать что-то вроде:
void myRetraceFunction()
{
static unsigned int timeError = 0;
static unsigned int timeOfLastFrame = 0;
unsigned int timeNow = getTimeNow(); // assume this returns in ms
// Get time since last frame.
// Since we ignore overflow here, we don't care that the result of
// getTimeNow presumably overflows.
unsigned int timeSinceLastFrame = timeNow - timeOfLastFrame;
// Store this so that we get correct results next time around.
timeOfLastFrame = timeNow;
// Establish the number of times to update physics per second, as the
// name says. So supposing it has been 0.5 of a second since we last
// drew, we'd want to perform 0.5*200 physics updates
const unsigned int numberOfTimesToUpdatePhysicsPerSecond = 200;
// calculate updates as (updatesPerSecond * timeSinceLastFrame / 1000),
// because we're assuming time is in milliseconds and there are 1000
// of them in a second. What we'll do though is preserve any amount we'd
// lose by dividing here, so that errors balance themselves.
//
// We assume that timeSinceLastFrame will be small enough that we won't
// overflow here.
unsigned int timeSinceLastFrameNumerator =
timeSinceLastFrame * numberOfTimesToUpdatePhysicsPerSecond
+ timeError;
// calculate how much of the numerator we're going to lose to
// truncation, so that we add it next frame and don't lose time
timeError = timeSinceLastFrameNumerator % 1000;
// calculate how many physics updates to perform
unsigned int physicsUpdatesToPerform = timeSinceLastFrameNumerator / 1000;
// do the physics work...
while(physicsUpdatesToPerform--)
updatePhysics();
// ... and draw a frame
drawFrame();
}
То, что timeError, должно регулировать против проблем округления.Например, предположим, что вы работаете с постоянными 70 кадрами в секунду и хотите обновления физики 60 раз в секунду.Если вы игнорируете ошибку, которая накапливается, то вы фактически запускаете нулевые обновления физики в секунду, поскольку в каждый момент времени код приходит к выводу, что время, прошедшее с момента последнего отрисовки кадра, было недостаточно продолжительным для обновления физики.было необходимо.И в противном случае у вас возникнут проблемы с алиасами, если вы пойдете другим путем, с любой частотой кадров, которая не является целочисленным делителем частоты обновления физики.
Если у вас нет явной функции восстановления, новместо вечного цикла, который выталкивает кадры, но блокирует вертикальный обратный ход, тогда вы пишете то же самое.
Если у вас нет возможности вообще блокировать вертикальный обратный ход, вы можете установить таймер нацелевую частоту кадров и представьте, что это вертикальная синхронизация с возвратом.Вы можете просто выдвигать кадры настолько быстро, насколько это возможно, но, скорее всего, вы будете получать жалобы с кем-либо, у кого есть ноутбук или другая машина, и это довольно очевидно, когда появляются фанаты.