Страницы

Поиск по вопросам

четверг, 19 декабря 2019 г.

Зачем нужна двойная буферизация

#cpp #opengl


Во-первых. Я хочу увидеть самый простой пример двойной буферизации в OpenGL freeglut,
на C++. С комментариями, пожалуйста. 

Во-вторых. Какова цель применения двойной буферизации? Из википедии я понял, что
двойная буферизация обеспечивает плавное изображение и убирает мерцание, т.к. буферы
меняются. Один рисует изображение — другой буфер наблюдает пользователь. Рекомендуете
ли вы постоянно использовать двойную буферизацию в OpenGL? Если нет, то почему? 
    


Ответы

Ответ 1



Двойная буферизация - одна из реализаций вертикальной синхронизации - метода борьбы с так называемым screen tearing. Это явление заключается в следующем: если видеокарта отрисовывает больше кадров в секунду, чем может отрисовать монитор (например, частота развёртки монитора - 60 Герц, то есть 60 кадров в секунду, а видеокарта рисует 100), то следующий кадр начинает выводиться на монитор до окончания отрисовки предыдущего. В результате при быстром движении камеры получается картинка вроде этой: Двойная буферизация (и другие алгоритмы вертикальной синхронизации) позволяет с этим бороться. Для этого изображение, отрисованное видеокартой, подаётся не сразу на экран, а в специальный буфер (область памяти). Как только монитор отрисует следующий кадр, содержимое этого буфера передаётся на монитор, а в буфер пишется следующий кадр. С технической точки зрения это реализованно как смена указателя на первичный буфер (тот, из которого изображение поступает на монитор): два буфера меняются местами, и, пока изображение из нового буфера выводится на экран, следующий кадр пишется в старый буфер. Затем эта операция повторяется. Таким образом, исключается ситуация, когда на монитор одновременно выводится несколько кадров. Однако, такой подход хорошо работает только когда частота кадров, отрисовываемых видеокартой значительно превышает частоту развёртки монитора. Если же эти частоты близки или частота отрисовки кадров видеокартой ниже частоты развёртки монитора, периодически будут случаться ситуации, когда монитор уже отрисовал кадр, а видеокарта - ещё нет. В этом случае (при включённой двойной буферизации) на монитор будет повторно выведен предыдущий кадр, даже если видеокарта не успела на миллисекунду. Это вызывает заметные подтормаживания изображения, что может раздражать пользователя. Поэтому всегда стоит предоставлять выбор: выключенная вертикальная синхронизация и разрывы изображения или включённая вертикальная синхронизация и потенциальное проседание частоты кадров. Простейший способ включить двойную буферизацию в GLUT - при инициализации вместо glutInitDisplayMode(GLUT_SINGLE); использовать glutInitDisplayMode(GLUT_DOUBLE); а при отрисовке вместо glFlush(); использовать glutSwapBuffers(); Полный код примера можно посмотреть здесь, документацию можно почитать по этим ссылкам: glutInitDisplayMode, glutSwapBuffers (привожу ссылки на документацию GLUT, так как документация FreeGLUT утверждает, что отдичий между реализациями этих функций в GLUT и FreeGLUT нет). P. S. Если интересна тема технологий вертикальной синхронизайии, можете посмотреть видео с презентацией технологий AMD FreeSync и Nvidia G-Sync. Помимо собственно презентации довольно перспективной технологии, в них наглядно демонстрируются причины проседания частоты кадров при включении вертикальной синхронизации.

Ответ 2



Вот вам пример для GLUT: #include #include static GLfloat spin = 0.0; void init(void) { glClearColor(0.0, 0.0, 0.0, 0.0); glShadeModel(GL_FLAT); } // колбек для отрисовки сцены void display(void) { // рисуем в задний буфер glClear(GL_COLOR_BUFFER_BIT); glPushMatrix(); glRotatef(spin, 0.0, 0.0, 1.0); glColor3f(1.0, 1.0, 1.0); glRectf(-25.0, -25.0, 25.0, 25.0); glPopMatrix(); glutSwapBuffers(); // переключаем буферы } void spinDisplay(void) { spin = spin + 2.0; if (spin > 360.0) spin = spin -360.0; glutPostRedisplay(); } // колбэк при изменении размеров окна void reshape(int w, int h) { glViewport(0, 0, (GLsizei) w, (GLsizei) h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-50.0, 50.0, -50.0, 50.0, -1.0, -1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); } // колбэк для событий мыши void mouse(int button, int state, int x, int y) { switch(button) { case GLUT_LEFT_BUTTON: // вращаем сцену if (state == GLUT_DOWN) glutIdleFunc(spinDisplay); break; case GLUT_RIGHT_BUTTON: // не вращаем сцену if (state == GLUT_DOWN) glutIdleFunc(NULL); break; default: break; } } int main(int argc, char** argv) { // настриваем GLUT glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB); // хотим двойную буферизацию glutInitWindowSize(250, 250); glutInitWindowPosition(100, 100); glutCreateWindow(argv[0]); init(); // чистим экран // регистрируем колбэки glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(mouse); glutMainLoop(); // стартуем главный цикл return 0; }

Комментариев нет:

Отправить комментарий