В настоящее время я пытаюсь включить мерцание прямоугольников с определенной частотой, используя OpenGL, SDL2 и GLEW в Windows. Проблемы, с которыми я сталкиваюсь, заставляют меня сходить с ума, потому что я просто не могу понять, что я делаю неправильно - я все еще OpenGL noob.
Проблема в том, что на некоторых частотах (все частоты явно ниже, чем частота обновления экранов) мерцание прямоугольника начинает "спотыкаться", вызывая повторные неподвижные изображения для нескольких кадров, либо пустой экран, либо видимый прямоугольник. В двух словах, я просто не могу периодически периодически мерцать с постоянной частотой в течение более длительных периодов времени. Проблема сильно заметна с ростом частот.
Я попытался включить/отключить VSync, вручную ограничивая FPS с помощью SDL, используя различные реализации таймера. Я также измерил FPS - при ограничении я достигаю постоянной 60 FPS при случайном дрожании +-1, с неограниченным FPS я получаю> 3000 FPS. Я не уверен, что причиной этой проблемы является проблема синхронизации, или если я делаю что-то неправильно в OpenGL.
Мой сокращенный код ниже (я удалил ошибки, но ошибок нет). Я также сделал несколько аннотаций, где я попытался изменить переменную.
Я инициализирую свои системы следующим образом:
SDL_Init(SDL_INIT_EVERYTHING);
SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
_window = SDL_CreateWindow(...);
SDL_GLContext glContext = SDL_GL_CreateContext(_window);
GLenum error = glewInit();
SDL_GL_SetSwapInterval(1); //For vsync
glClearColor(0.0f, 0.0f, 1.0f, 1.0f);
_rect.init(-0.5,-0.5, 1.0f, 1.0f);
Мой игровой цикл выглядит так:
while (1)
{
logic(); // process game logic
processInput(); // process input using sdl
drawGame(); // draw the game on screen
}
Функция drawGame() измеряет время от рисования одного кадра до следующего кадра, так что я могу соответствующим образом переключать прямоугольники.
void drawGame()
{
/* some declarations and initialization calls */
double freq = 20; // frequency in hertz
double interval = 1.0/(2.0*freq)*1000.0; // half of the interval in ms,
// determines when to toggle
delta = SDL_GetTicks() - tick; // measurement of time delta
tick = SDL_GetTicks();
delta_acc += delta; // accumulate the measurements
if (delta_acc > interval)
{
act = !act;
delta_acc = 0; // I've also tried delta_acc-=interval here to account for slightly
// overdue toggling, but it didn't help
}
// Now the actual rendering
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if (act) _rect.draw();
glFinish(); // Tried with and without glFinish();
SDL_GL_SwapWindow(_window);
}
Объект _rect
имеет класс спрайтов, который в основном выглядит следующим образом:
Sprite::Sprite(void)
{
_vboID = 0;
}
void Sprite::init(float x, float y, float width, float height)
{
_x = x;
_y = y;
_height = height;
_width = width;
if (_vboID == 0)
{
glGenBuffers(1, &_vboID);
}
float vertexData[12];
/*
... setting vertices here
*/
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glBufferData(GL_ARRAY_BUFFER, sizeof(vertexData), vertexData, GL_STATIC_DRAW);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
void Sprite::draw()
{
glBindBuffer(GL_ARRAY_BUFFER, _vboID);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, 0);
glDrawArrays(GL_TRIANGLES, 0, 6);
glDisableVertexAttribArray(0);
glBindBuffer(GL_ARRAY_BUFFER, 0);
}
Для меня это звучит как проблема выборки: Предполагая частоту обновления экрана 60 Гц и скорость мерцания 20 Гц. Это означает, что мы хотим, чтобы объект был видимым в течение 1/40 секунд, а затем был невидим для 1/40 секунд. Теперь рассмотрим следующую диаграмму:
on 1/60 on 1/60 off 1/60
|---------------------|---------------------|---------------------| Screen
|--------------------------------|--------------------------------| Flicker
1/40 (on) 1/40 (off)
Мы видим, что в случае, мы получим 2 кадра, показывающие объект, и только один кадр не покажет его. Я предполагаю, что ваш алгоритм должен работать всякий раз, когда частота /2 кратно вашей частоте обновления экрана. (например, частота = [30, 15,....])
Примечание. Отключение vsynch не отображает больше изображений на экране, оно просто отображает больше изображений в фоновом режиме.