Анимация с двойной буферизацией
Создание движущихся изображений – это одна из наиболее впечатляющих возможностей компьютерной графики. С помощью системы объемного проектирования инженер может с различных сторон рассмотреть спроектированный механизм и увидеть его в движении на экране компьютера. Компьютерные авиационные тренажеры применяются при подготовке летчиков. В области развлечений очень распространены компьютерные игры с мультипликацией. Во всех перечисленных примерах анимация является необходимой частью компьютерной графики. При демонстрации фильма в кинотеатре на экран проектируются неподвижные изображения, которые быстро сменяют друг друга (24 кадра в секунду). В киноаппарате каждый кадр кинопленки останавливается перед объективом, затем открывается затвор и кадр проектируется на экран. Затвор очень быстро закрывается и пленка перемещается на следующий кадр. Затем этот кадр показывается на экране, и т.д. Хотя каждую секунду зритель видит 24 различных кадра, человеческий мозг не в состоянии различить отдельные изображения и видит плавное движение (старые киноленты с Чарли Чаплином снимались со скоростью 16 кадров в секунду и поэтому персонажи выглядят заметно дергающимися). Большинство современных кинопроекторов показывают каждый кадр дважды, так что изображения меняются с частотой 48 Гц и за счет этого уменьшается мерцание. Компьютерные видеоадаптеры обновляют изображение с кадровой частотой от 60 до 120 Гц. Разница между 60 Гц и 30 Гц на глаз видна, а между 120 Гц и 60 Гц практически незаметна. Повышение кадровой частоты выше 120 Гц из-за инерционности зрения практического смысла не имеет. Главная особенность кинопроекторов, обеспечивающая естественность движения на киноэкране, заключается в том, что каждый кадр проектируется на экран сразу целиком. Допустим, что есть программа для создания анимации из миллиона кадров:
open_window(); // Открытие окна на экране for (i = 0; i < 1000000; i++) { clear_the_window(); // Очистка окна draw_frame(i); // Рисование i-го кадра wait_24th(); // Ожидание завершения промежутка в 1/24 с }
Очистка окна и рисование кадра требует некоторого времени. Чем ближе это время к 1/24 с, тем хуже выглядит анимация. Допустим, рисование кадра занимает почти 1/24 с. Получается, что элементы кадра, которые рисуются первыми, присутствуют на экране весь промежуток 1/24 с и выглядят нормально. Но элементы, которые рисуются последними, удаляются практически сразу и программа приступает к рисованию следующего кадра. В результате изображение мерцает, т.к.. большую часть от 1/24 с на месте элементов кадра, которые рисуются последними, виден очищенный фон. Проблема заключается в том, что программа не сразу отображает целый кадр, а рисует его постепенно на глазах у наблюдателя. Простейшее решение этой проблемы – двойная буферизация. Программа работает с двумя буферами кадра. Пока один буфер показывается на экране, в другом буфере программа рисует следующий кадр. Когда рисование кадра заканчивается, программа обменивает буфера местами, так что тот, который показывался на экране, будет использоваться для рисования, и наоборот. Этот способ компьютерной мультипликации напоминает кинопроектор с пленкой из двух кадров: пока один кадр показывается на экране, художник отчаянно стирает содержимое другого кадра и рисует на нем новую картинку. Если художник рисует достаточно быстро, то зритель не заметит разницы между таким фильмом и фильмом, в котором все кадры были нарисованы заранее и проектор показывает их один за другим. При двойной буферизации, каждый кадр показывается только после того, как его рисование будет полностью завершено, так что зритель никогда не увидит частично нарисованный кадр.
Улучшенный вариант приведенной выше программы для демонстрации плавной анимации может выглядеть примерно так:
open_win_dbl_buf(); // Открытие окна в режиме двойной буферизации for (i = 0; i < 1000000; i++) { clear_the_window(); // Очистка окна draw_frame(i); // Рисование i-го кадра swap_the_buffers(); // Обмен буферов }
Функция swap_the_buffers() не просто обменивает видимый и невидимый буфера, перед этим она дожидается завершения текущей операция обновления экрана для показа предыдущего буфера. Эта функция гарантирует, что новый буфер будет показан полностью. Допустим, что кадровая частота видеоадаптера 60 Гц. Значит, вы можете достичь скорости максимум 60 кадров в секунду, если успеете очистить и нарисовать каждый кадр менее, чем за 1/60 с. Однако часто бывает, что кадр слишком сложен и его не удается нарисовать за 1/60 с. Тогда каждый кадр выводится на экран несколько раз. Если, например, кадра прорисовывается около 1/45 с, то возможна анимация с частотой 30 кадров в секунду, а время простоя составит 1/30-1/45=1/90 с на кадр. Хотя 1/90 с кажется небольшим промежутком времени, но оно теряется на каждом промежутке в 1/30 с, так что в действительности время простоя составляет 1/3 всего времени работы программы. Тот факт, что кадровая частота является константой, может неожиданным образом повлиять на производительность программы. Например, с дисплеем 60 Гц вы можете выводить анимацию с частотой 60 Гц, 30 Гц, 20 Гц, 15 Гц, 12 Гц и т.д. (60/1, 60/2, 60/3, 60/4, 60/5,...). Допустим, вы пишете программу и постепенно добавляете к ней новые возможности (например, в авиационный симулятор добавляете усложненные наземные сцены). Сначала эти новые возможности не сказываются на общей производительности программы – вы продолжаете показывать 60 кадров в секунду. Внезапно, после очередного усовершенствования, производительность программы падает в 2 раза, т.к. программа не успевает нарисовать весь кадр за 1/60 с и пропускает момент, когда можно поменять местами два буфера. Похожая вещь происходит. когда время рисования кадра начинает превышать 1/30 с – тогда производительность падает с 30 до 20 кадров в секунду, т.е. на треть.
Еще одна проблема заключается в том, что если время рисования кадров непостоянно и близко к магическим числам (в нашем случае 1/60 с, 2/60 с, 3/60 с и т.д.), причем это время меняется случайно, то и скорость анимации непостоянна и в изображение может выглядеть дергающимся. В данном случае, если никак не удается упростить содержимое кадров, то можно добавить маленькую задержку и кадры всегда будут прорисовываться более, чем за 1/60 с и получится постоянная, хотя и меньшая, кадровая частота анимации. Структура настоящих анимационных программ не слишком сильно отличается от приведенного описания. Обычно для каждого кадра заново прорисовывается целый буфер, т.к. это сделать проще, чем вычислить, какие именно части кадра требуют перерисовки. Это особенно верно для таких приложений, как авиационные симуляторы, где совсем небольшое изменение ориентации самолета изменяет положение всех объектов, попадающих в кадр. В OpenGL нет встроенной функции swap_the_buffers(), т.к. ее устройство зависит от конкретного видеоадаптера и от операционной системы с графическим интерфейсом. Поэтому эта функция реализована во вспомогательных библиотеках, например, в библиотеке GLAUX: void auxSwapBuffers(void); Чтобы циклически выполнять рисование кадров, можно зарегистрировать функцию рисования кадра как фоновую функцию обратной связи. GLAUX вызывает фоновую функцию, когда нет команд от пользователя. Регистрация фоновой функции выполняется с помощью функции: void auxIdleFunc(void (*)(void)); Применение фоновой функции рисования кадра в режиме двойной буферизации показано в программе 2.1, которая рисует изображение вращающегося чайника.
#include <windows.h> #include <GL/gl.h> #include <GL/glu.h> #include <GL/glaux.h>
void CALLBACK resize(int width, int height); void CALLBACK display(void);
void main() { auxInitDisplayMode(AUX_RGBA | AUX_DEPTH | AUX_DOUBLE); auxInitPosition(50, 10, 400, 400); auxInitWindow("Лекция 2, Программа 2.1"); auxReshapeFunc(resize);
glEnable(GL_ALPHA_TEST); glEnable(GL_DEPTH_TEST); glEnable(GL_COLOR_MATERIAL); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glEnable(GL_LIGHTING); glEnable(GL_LIGHT0);
float pos[4] = { 0, 5, 5, 1 }; float dir[3] = { 0, -1, -1 }; glLightfv(GL_LIGHT0, GL_POSITION, pos); glLightfv(GL_LIGHT0, GL_SPOT_DIRECTION, dir);
auxIdleFunc(display); auxMainLoop(display); }
void CALLBACK resize(int width, int height) { glViewport(0, 0, width, height);
glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(-5, 5, -5, 5, 2, 12); gluLookAt(3,0,5, 0,0,0, 0,1,0);
glMatrixMode(GL_MODELVIEW); glLoadIdentity(); }
void CALLBACK display(void) { // Очистка буфера кадра glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
static float angle = 0;
glColor3f(0.5, 0.5, 0); glPushMatrix(); glRotatef(angle, 0, 1, 0); auxWireTeapot(1); glPopMatrix();
angle += 5; if (angle > 360) angle -= 360;
// Копирование содержимого буфера кадра на экран glFlush(); auxSwapBuffers(); } Программа 2.1. Анимация с двойной буферизацией.
Воспользуйтесь поиском по сайту: ©2015 - 2024 megalektsii.ru Все авторские права принадлежат авторам лекционных материалов. Обратная связь с нами...
|