Введение в Opengl 1 1 Что такое OpenGL? 1 2 Немного Opengl кода 2 3 Синтаксис команд Opengl 4



бет6/6
Дата29.05.2016
өлшемі222 Kb.
#100722
түріГлава
1   2   3   4   5   6

1.9.3 Загрузка палитры


Если вы работаете в индексном режиме, то можете к своему удивлению обнаружить, что в OpenGL нет команд для загрузки цвета в цветовую таблицу. Дело в том, что процесс загрузки палитры целиком зависит от оконной системы. В GLUT существует обобщенная функция для загрузки одного цветового индекса с соответствующим RGB значением.

void glutSetColor (Glint index, GLfloat red, GLfloat green, GLfloat blue);

Загружает в палитру по индексу index, RGB-значение, определенное параметрами red, green и blue. Последние три параметра нормализуются до диапазона [0.0, 1.0].

1.9.4 Рисование трехмерных объектов


Многие программы примеры используют простые трехмерные объекты для иллюстрации различных методов и техник визуализации изображения. GLUT содержит несколько функций для рисования таких объектов. Все эти функции работают в непосредственном режиме. Каждая из них имеет два варианта: первый рисует объект в виде проволочного каркаса и не генерирует нормалей, второй рисует объект сплошным и генерирует нормали поверхности (для чайника помимо этого генерируются координаты текстуры). Если используется освещение, следует выбирать сплошную версию объекта. Все объекты рисуются с учетом текущих параметров, например, цвета и характеристик материала. Кроме того, все объекты рисуются центрированными относительно текущих модельных координат.

void glutWireSphere (GLdouble radius, GLint slices, GLint stacks);
void glutSolidSphere (GLdouble radius, GLint slices, GLint stacks);

Рисуют проволочную или сплошную сферу с радиусом radius, количеством частей (полигонов из которых состоит сфера) slices – вокруг оси z и stacks – вдоль оси z. Для того, чтобы понять, что означает вокруг оси z и вдоль нее, представьте себе, что вы смотрите в длинную трубу. В данном случае направление вашего обзора совпадает с осью z трубы. Она может быть мысленно разделена как вдоль (на длинные фрагменты), так и поперек (на кольца). После таких разбиений труба фактически состоит из множества мелких кусочков. В случае сферы количество разбиений поперек задается параметром stacks, а количество разбиений вдоль – параметром slices. Из этого следует, что чем больше разбиений, тем более гладкой выглядит сфера на экране, но тем больше вычислений требуется для ее рисования.

void glutWireCube (GLdouble size);
void glutSolidCube (GLdouble size);

Рисуют проволочный или сплошной куб с длиной ребра size.

void glutWireTorus (GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings);
void glutSolidTorus (GLdouble innerRadius, GLdouble outerRadius, GLint nsides, GLint rings);

Рисуют проволочный или сплошной торус (бублик) с внешним радиусом outerRadius и внутренним радиусом innerRadius. Параметр rings задает желаемое число колец из которых будет состоять торус, параметр nsides – из скольких частей будет состоять каждое кольцо.

void glutWireCone (GLdouble radius, GLdouble height, GLint slices, GLint stacks);
void glutSolidCone (GLdouble radius, GLdouble height, GLint slices, GLint stacks);

Рисуют проволочный или сплошной конус радиусом radius, высотой height. Значение параметров slices и stacks аналогично таким же параметрам для сферы.

void glutWireIcosahedron (void);
void glutSolidIcosahedron (void);
void glutWireOctahedron (void);
void glutSolidOctahedron (void);
void glutWireTetrahedron (void);
void glutSolidTetrahedron (void);
void glutWireDodecahedron (GLdouble radius);
void glutSolidDodecahedron (GLdouble radius);

Рисуют проволочные или сплошные икосаэдр, октаэдр, тетраэдр и додекаэдр соответственно (единственный параметр последний пары функций задает радиус додекаэдра).

void glutWireTeapot (GLdouble size);
void glutSolidTeapot (GLdouble size);

Рисуют проволочный или сплошной чайник размера size.

1.9.5 Управление фоновым процессом


Вы можете указать функцию, которая будет вызываться в том случае, если нет других сообщений, то есть во время простоя приложения. Это может быть полезно для выполнения анимации или другой фоновой обработки.

void glutIdleFunc (void (*func)(void));

Задает функцию, выполняемую в случае, если больше приложению делать нечего (отсутствуют сообщения). Выполнение этой функции обратного вызова можно отменить передачей glutIdleFunc() аргумента NULL.

1.9.6 Запуск программы


После того, как все настройки выполнены, программы GLUT входят в цикл обработки сообщений функцией glutMainLoop().

void glutMainLoop (void);

Вводит программу в цикл обработки сообщений. Функции обратного вызова будут выполняться в случае наступления соответствующих событий.

Пример 1-2 показывает, как с помощью GLUT можно заставить работать программу, показанную в примере 1-1. Обратите внимание на реструктуризацию кода. Для увеличения эффективности операции, которые нужно выполнить только один раз (установка цвета фона и координатной системы), теперь помещены в функцию init(). Операции по визуализации (и пересчету) сцены находятся в функции display(), которая зарегистрирована в качестве дисплейной функции обратного вызова.

Пример 1-2. Простая OpenGL – программа с использованием GLUT: hello.cpp


#include

void init(void)


{
//Выбрать фоновый (очищающий) цвет
glClearColor(0.0,0.0,0.0,0.0);

//Установить проекцию


glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glOrtho(0.0,1.0,0.0,1.0,-1.0,1.0);
}

void display(void)


{
//Очистить экран glClear(GL_COLOR_BUFFER_BIT);

//Нарисовать белый полигон (квадрат) с углами //в (0.25, 0.25, 0.0) и (0.75, 0.75, 0.0)


glColor3f(1.0,1.0,1.0);
glBegin(GL_POLYGON);
glVertex3f(0.25,0.25,0.0);
glVertex3f(0.75,0.25,0.0);
glVertex3f(0.75,0.75,0.0);
glVertex3f(0.25,0.75,0.0);
glEnd();

//Не ждем. Начинаем выполнять буферизованные


//команды OpenGL
glFlush();
}

//Установить начальные характеристики окна,


//открыть окно с заголовком «hello».
//Зарегистрировать дисплейную функцию обратного вызова
//Войти в главный цикл
int main(int argc, char **argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB);
glutInitWindowSize(250,250);
glutInitWindowPosition(100,100);
glutCreateWindow(“hello”);
init();
glutDisplayFunc(display);
glutMainLoop();

return 0;


}

1.9.7 Типы проектов


Поскольку данное пособие предназначено в основном для тех, кто программирует приложения для среды Microsoft Windows, требуется сделать несколько важных замечаний.

Существует два основным типа приложения для этой операционной системы: консольные и оконные.

Консольные приложения по своему строению практически не отличаются от программ DOS запущенных под Windows (однако являются 32-разрядными приложениями защищенного режима). При их запуске система автоматически создает окно, в которое можно выводить текстовую информацию языковыми средствами C без привлечения функций API операционной системы. Тем не менее, само приложение может также создавать дополнительные окна, если разработчику это требуется. Именно так и происходит при создании консольных приложений с использованием GLUT и OpenGL. Система при запуске создает консоль, а GLUT своим методом glutCreateWindow() создает еще одно окно. В этом случае весь графический вывод OpenGL направляется в окно, созданное GLUT, а текстовый вывод функциями стандартной библиотеки C (например, printf(“текстовая строка”) или cout<<”текстовая строка”) будет осуществляться в консольное окно. Это может быть весьма удобно, если помимо графического вывода программа выполняет некоторые вычисления над полученным изображением (определение минимальных и максимальных значений компонент цвета, использование обратного режима OpenGL и так далее). Стартовой точкой выполнения консольной программы является функция main(), в которую системой передаются те самые параметры командной строки (если они есть), которые позже должны быть переданы в glutInit().

В случаях, когда лишнее консольное окно приложению не нужно, лучше изначально создавать оконное приложение. При его запуске вся ответственность по созданию окон ложится на программиста и средства, которыми он пользуется, в нашем случае опять-таки на GLUT. И вот здесь может возникнуть потенциальная проблема, которая происходит из разницы между форматами передачи параметров командной строки в функцию main() и WinMain(). В первом случае параметры передаются в функцию в виде массива строк, тогда как во втором – в виде одной единственной строки, причем в этом случае количество параметров неизвестно. В свою очередь glutInit() ожидает параметры в формате main() с указанием их числа. Существует несколько вариантов решения этой проблемы. Если параметры вам нужны, следует создать (или позаимствовать) функцию разборки единой командной строки на составляющие с выяснением их количества (это довольно просто). Если же у вас нет нужды в параметрах командной строки можно передать в glutInit() подделку следующего вида:



char* argv="";
int argc=0;
glutInit(&argc,&argv);

Конечно, существуют и другие варианты.

1.10 Анимация


Одна из самых впечатляющих вещей, которую вы можете делать на компьютере – это рисование движущихся картинок. Являетесь ли вы инженером, пытающимся увидеть разрабатываемую деталь со всех сторон, пилотом, обучающимся летать на самолете с помощью симулятора или профессионалом в области создания компьютерных игр, вам должно быть абсолютно ясно, что анимация – это важная часть компьютерной графики.

В кинотеатре движение достигается за счет проектирования серии картинок на экран со скоростью 24 кадра в секунду. Каждый кадр выдвигается на позицию перед лампой, открывается шторка и кадр показывается на экране. Шторка моментально закрывается, пленка передвигается к следующему кадру, он отображается на экране и так далее. Таким образом, когда вы просматриваете 24 кадра за одну секунду, ваш мозг объединяет их в непрерывную плавную анимацию. (Разрывы между кадрами были хорошо заметны в старых фильмах Чарли Чаплина, так как в то время фильмы снимались и показывались со скоростью 16 кадров в секунду.) Современные мониторы в среднем перерисовывают изображение от 60 до 76 раз в секунду (хотя скорость может достигать и 120 раз в секунду). Очевидно, что 60 кадров в секунду выглядят более гладко, чем 30, а 120 намного лучше, чем 60. Однако, частота обновления выше 120 кадров в секунду может находиться за гранью человеческого восприятия (это зависит, в основном, от индивидуальных особенностей).

Ключевая причина, по которой кинопроектор способен создать эффект движения заключается в том, что каждый кадр уже готов на момент его показа. Предположим, что вы пытаетесь воспроизвести анимацию, состоящую из миллиона кадров, с помощью программы вроде следующей:


Открыть_окно();
for(i=0;i<1000000;i++)
{
Очистить_окно();
Нарисовать_кадр(i);
Подождать_пока_пройдет_одна_24я_часть_секунды();
}

Если вы сложите время, которое требуется вашей программе, чтобы нарисовать кадр на экране и время, необходимое для очистки экрана, то выяснится, что программа выдает все более и более неприятные результаты по мере того, как это суммарное время приближается к 1/24 части секунды. Предположим, что рисование занимает практически всю 1/24 часть секунды. Тогда элементы изображения рисуемые первыми будут видны на экране практически постоянно, а те, что рисуются в конце, будут похожи на приведение, так как сразу после их отображения программа начнет стирать все изображение с экрана и, таким образом, большую часть времени наблюдатель будет видеть на месте этих элементов пустой фон. Проблема в том, что приведенная программа не воспроизводит готовые кадры, а рисует каждый из них и наблюдатель это видит.

Большинство реализаций OpenGL предоставляет двойную буферизацию – аппаратный или программный механизм, который обеспечивает наличие двух полноценных цветовых буферов. Один отображается на экране, пока второй отрисовывается. Когда рисование кадра окончено, два буфера переключаются: тот, что только что был на экране, теперь используется для рисования и наоборот. Этот процесс похож на отображение проектором в кинотеатре пленки всего с двумя кадрами: пока один проецируется на экран, художник стирает и перерисовывает другой – невидимый. До тех пор, пока художник достаточно быстр, наблюдатель не заметит разницы между подобной техникой и той, где все кадры уже готовы и просто показываются друг за другом. С двойной буферизацией каждый кадр показывается только после того, как он полностью готов. Наблюдатель никогда не увидит частично нарисованное изображение.

Модифицированная для использования двойной буферизации версия предыдущей программы может выглядеть следующим образом:


Открыть_окно_в_режиме_двойной_буферизации();
for(i=0;i<1000000;i++)
{
Очистить_окно();
Нарисовать_кадр(i);
Переключить_буферы();
}

1.11 Частота обновления экрана


В некоторых реализациях OpenGL, функция Переключить_буферы() помимо простого переключения буферов, ждет, пока закончится текущий период обновления экрана, чтобы предыдущий кадр был полностью отображен и следующий был нарисован с самого начала. Предположим, что ваша система обновляет экран 60 раз в секунду. Это означает, что наивысшая скорость смены кадров, которую вы можете достичь – 60 кадров в секунду (frames per second -- fps) и если все ваши кадры будут очищаться и перерисовываться менее чем за 1/60 долю секунды, ваша анимация будет на этой скорости гладкой.

Часто случается, что кадр слишком сложен, чтобы нарисовать его за 1/60 секунды, таким образом, каждый кадр показывается на экране более чем один раз. Если, например, рисование кадра занимает 1/45 секунды, вы получаете скорость 30 fps и ничем не занятое время в размере 1/30-1/45=1/90 секунды, то есть треть времени рисования кадра на каждый кадр.

Кроме того, частота обновления монитора постоянна, что может вызвать неожиданные последствия для быстродействия. Например, при частоте обновления монитора 60 раз в секунду, анимация может идти со скоростями 60 fps, 30, fps, 20 fps, 15 fps, 12 fps и так далее. Это означает, что если пишете приложение и постепенно добавляете детали, сначала каждая деталь, которую вы добавляете, не оказывает воздействия на общее быстродействие – анимация по-прежнему идет со скоростью 60 fps. Затем вы добавляете еще одну деталь, и, система уже не может рисовать кадр за 1/60 долю секунды, вследствие чего, неожиданно, быстродействие падает до 30 fps, поскольку не нельзя переключить буферы при первой возможности. То же происходит, если время рисования, затрачиваемое на каждый кадр, перешагивает порог в 1/30 долю секунды – быстродействие снижается до 20 fps.

Если сложность сцены близка к одному из волшебных чисел (1/60 секунды, 1/30 секунды, 1/20 секунды и так далее), то из-за некоторых вариаций некоторые кадры могут рисоваться чуть быстрее, а некоторые немного не укладываться в график. В этом случае скорость анимации непостоянна, что может вести к неприятному внешнему виду. В таких случаях, если не удается упростить медленные кадры, возможно, стоит добавить небольшую паузу в быстрые, чтобы сохранять скорость анимации постоянной. Если различные кадры вашей анимации драматически отличаются друг от друга по сложности, может потребоваться более изощренный подход.


1.12 Движение = Перерисовка + Переключение


Структура реальных программ анимации не слишком отличается от описания, приведенного в заголовке этого раздела. Обычно намного легче перерисовать весь буфер, чем выяснять какие его части нужно изменить. Это особенно верно для таких приложений как симуляторы полетов, где даже небольшое изменение в ориентации самолета ведет к масштабным изменениям вида за окнами кабины.

В большинстве анимационных программ объекты сцены просто перерисовываются с учетом различных трансформаций – изменяется точка обзора или положение наблюдателя, или машина немного проезжает по дороге, или объект немного поворачивается. Если требуются обширные вычисления не связанные с рисованием, то они могут ощутимо понизить быстродействие в целом и скорость анимации в частности. Имейте в виду однако, что часто пауза после вызова Переключить_буферы() может быть использована для таких операций.

В OpenGL нет функции Переключить_буферы(), так как данная возможность может не обеспечиваться отдельными аппаратными средствами и, в любом случае, она сильно зависит от оконной системы. Например, при использовании системы X Window (и работе без дополнительных библиотек) можно использовать следующую GLX функцию:


void glXSwapBuffers (Display *dpy, Window window);

В аналогичном случае для системы Microsoft Windows функция будет выглядеть следующим образом:

BOOL SwapBuffers (HDC hdc);

При использовании GLUT следует вызывать функцию:

void glutSwapBuffers (void);

Пример 1-3 иллюстрирует использование glutSwapBuffers() для рисования вращающегося квадрата, показанного на рисунке 1-3. Этот пример также показывает, как использовать GLUT для захвата пользовательского ввода и включения/выключения функции фоновой обработки. В данном примере левая и правая кнопки мыши соответственно включают и выключают вращение.

Рисунок 1.3. Вращающийся квадрат в режиме двойной буферизации



Пример 1.3. Программа, использующая двойную буферизацию: double.cpp



#include

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+1.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;


}
}

//Запросить режим двойной буферизации


//Зарегистрировать функции обработки мышиного ввода
int main(int argc, char **argv)
{
glutInit(&argc,argv);
glutInitDisplayMode(GLUT_DOUBLE|GLUT_RGB);
glutInitWindowSize(250,250);
glutInitWindowPosition(100,100);
glutCreateWindow("Двойная буферизация");
init();
glutDisplayFunc(display);
glutReshapeFunc(reshape);
glutMouseFunc(mouse);
glutMainLoop();

return 0;


}

1.13 Ссылки на примеры главы


Приложение hello: простейшее приложение, отображающее белый квадрат на черном фоне.
Скачать

Приложение double: пример использования двойной буферизации. Левая кнопка мыши запускает вращение квадрата, правая – останавливает, средняя – меняет режим отображения квадрата (заливка, точки или линии).


Скачать

Исходный код библиотеки glut версии 3.7.


Скачать  
 

Достарыңызбен бөлісу:
1   2   3   4   5   6




©dereksiz.org 2024
әкімшілігінің қараңыз

    Басты бет