Opengl лекция №1 введение события сообщения контекст


Содержание

Использование OpenGL (стр. 1 из 6)

Министерство образования и науки Украины

Луганский национальный педагогический университет имени Тараса Шевченко

Институт экономики и бизнеса

Студент 3 курса

1.1. Программный код OpenGL.. 5

1.2. Синтаксис команд OpenGL.. 8

1.3 OpenGL как конечный автомат. 10

1.4. Конвейер визуализации OpenGL.. 11

1.4.1. Конвейер. 11

1.4.2 Списки вывода. 12

1.4.3 Вычислители. 13

1.4.4 Операции обработки вершин. 13

1.4.5 Сборка примитивов. 13

1.4.6 Операции обработки пикселей. 14

1.4.7 Сборка текстуры.. 15

1.4.8. Растеризация. 15

1.4.9 Операции обработки фрагментов. 16

2 Библиотеки, относящиеся к OpenGL.. 17

2.1 Библиотека OpenGL.. 17

2.2. Подключаемые файлы.. 19

2.3 GLUT, инструментарий утилит библиотеки OpenGL.. 20

2.3.1. Инструментарий библиотек. 20

2.3.2 Управление окнами. 21

2.3.3 Функция обратного вызова отображения. 22

2.3.4. Исполнение программы.. 23

2.3.4 Обработка событий ввода данных пользователем.. 25

2.3.5 Управление фоновым процессом.. 25

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

3.1. анимация компьютерной графики. 27

3.2 Обновление отображаемой информации во время паузы.. 30

Библиотека OpenGL представляет собой программный интерфейс для аппаратного обеспечения машинной графики. Этот интерфейс состоит приблизительно из 250 отдельных команд (почти 200 команд в ядре OpenGL и еще 50 команд в библиотеке утилит OpenGL), которые используются для того, чтобы определить объекты и операции, необходимые для создания интерактивных трехмерных прикладных программ.

Библиотека OpenGL разработана в качестве низкоуровневого, аппаратно-независимого интерфейса, допускающего реализацию на множестве различных аппаратных платформ. Для того чтобы достичь этих качеств, в состав библиотеки OpenGL не включены никакие команды для выполнения задач работы с окнами или для получения пользовательского ввода; вместо этого вы должны работать через любую систему управления окнами, которая работает с конкретными аппаратными средствами. Точно так же библиотека OpenGL не предоставляет команды высокого уровня для описания моделей трехмерных объектов. Такие команды могли бы позволить определять относительно сложные формы, например, автомобили, части тела, самолеты или молекулы. При использовании библиотеки OpenGL вы должны создавать нужную модель из ограниченного набора геометрических примитивов — точек, линий и многоугольников.

Более сложная библиотека, которая обеспечивает эти функциональные возможности, конечно, могла бы быть создана поверх библиотеки OpenGL. Библиотека утилит OpenGL (GLU — OpenGLUtilityLibrary) предоставляет множество возможностей моделирования, таких как поверхности второго порядка и NURBS-кривых и поверхностей (NURBS — Non-Uniform, RationalB-Spline — неравномерный рациональный В-сплайн). Библиотека GLU представляет собой стандартную часть каждой реализации OpenGL. Существуют также наборы инструментов более высокого уровня, такие как FSG (FahrenheitSceneGraph), которые являются надстройкой библиотеки OpenGL, и самостоятельно доступны для множества реализаций библиотеки OpenGL.

1.1. Программный код OpenGL

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

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

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

Информация о пикселях (например, какой цвет предполагается для этих пикселей) организована в памяти в виде битовых плоскостей. Битовая плоскость представляет собой область памяти, которая содержит один бит информации для каждого пикселя на экране; этот бит мог бы указывать, например, на то, что конкретный пиксель, как предполагается, является красным. Битовые плоскости, в свою очередь, организованы в буфер кадра, который содержит всю информацию, необходимую графическому дисплею для того, чтобы управлять цветом и яркостью всех пикселей на экране.

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

Пример 1. Фрагмент программного кода OpenGL

glClearColor(0.0, 0.0, 0.0, 0.0); glClear(GL_COLOR_BUFFER_BIT); glColor3f(1.0, 1.0, 1.0);

glOrtho(0.0, 1.0, 0.0, 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() ; glFlush () ;

Первая строка функции main() инициализирует определенное окно на экране: функция InitializeAWindowPlease() используется в данном случае в качестве метки-«заполнителя» для подпрограмм специфических оконных систем, которые в общем случае не являются вызовами OpenGL. Следующие две строки содержат команды OpenGL, которые устанавливают черный цвет фона для окна: функция glCIearCoIor() определяет то, какой цвет фона будет установлен для окна, а функция glClear() фактически устанавливает цвет окна. Как только цвет фона установлен, окно заливается этим цветом всякий раз, когда вызывается функция glClear(). Этот цвет фона может быть изменен с помощью второго вызова функции glClearColor(). Точно так же функция glColor3f() устанавливает то, какой цвет следует использовать для прорисовки объектов на экране — в данном случае этот цвет является белым. Все объекты, выводимые на экран после этого момента, используют данный цвет до тех пор, пока он не будет изменен с помощью следующего вызова команды установки цвета.

Следующая функция OpenGL, используемая в рассматриваемой программе, glOrtho(), определяет систему координат, которую OpenGL принимает для прорисовки окончательного изображения, и то, как это изображение отображается на экране. Вызовы, заключенные между функциями glBegin() и gl£nd(), определяют объект, который будет выведен на экран, в рассматриваемом примере это многоугольник с четырьмя вершинами. «Углы» многоугольника определяются с помощью функции glVertex3f(). Как вы, наверное, уже догадались, исходя из значений параметров этой функции, которые представляют собой координаты (х, у, z), данный многоугольник является прямоугольником, расположенным на плоскости z(z — 0).

Наконец, функция gIFlush() гарантирует, что команды прорисовки фактически выполняются, а не просто сохраняются в некотором буфере, ожидая дополнительных команд OpenGL. Подпрограмма-«заполнитель» UpdateTheWindowAndCheckForEvents() управляет содержимым окна и начинает обработку событий.

1.2. Синтаксис команд OpenGL

Как вы, вероятно, могли заметить из примера простой программы, приведенного в предшествующем разделе, команды библиотеки OpenGL используют префикс gl. Каждое слово, составляющее наименование команды, начинается с заглавной буквы (вспомните, например, функцию glClearColor()). Точно так же имена констант, определенных в библиотеке OpenGL, начинаются с префикса GL_, записываются целиком заглавными буквами и используют символы подчеркивания, чтобы разделить отдельные слова (например, GL__COLOR_BUFFER_BIT).

Вы, вероятно, также смогли заметить некоторые символы, которые показались вам посторонними, они добавляются в конец наименования некоторых команд (например, 3f в функциях glColor3f() и glVertex3f()). Действительно, часть Color в наименовании функции glColor3f() достаточна для того, чтобы определить данную команду как команду, устанавливающую текущий цвет. Однако были определены несколько таких команд, чтобы вы смогли использовать их с различными типами параметров. В частности, часть 3 суффикса указывает, что для этой команды задаются три параметра; другая версия команды Color использует четыре параметра. Часть f суффикса указывает на то, что параметры данной команды представляют собой числа с плавающей точкой. Наличие различных форматов позволяет библиотеке OpenGL принимать данные пользователя в его собственном формате данных.

Некоторые команды библиотеки OpenGL допускают использование 8 различных типов данных в качестве своих параметров. Буквы, используемые в качестве суффиксов для того, чтобы определить эти типы данных для реализации ISO С библиотеки OpenGL, представлены в Таблице 1.1; там же приведены соответствующие определения типов в библиотеке OpenGL. Конкретная реализация библиотеки OpenGL, которую вы используете, может не совпадать в точности с приведенной схемой; например, реализации для языков программирования C++ или Ada, не требуют этого.

тип данных языка

OpenGL

unsigned long

Тип данных
8-разрядное целое signed char GLbyte
16-разрядное целое short GLshort
32-разрядное целое Int или long GLint, GLsizei
32-разрядное число с плавающей точкой float GLfloat, GLclampf
64-разрядное число с плавающей точкой double GLdouble, GLclampd
8-разрядное беззнаковое целое unsigned char GLubyte, GLboolean
16-разрядное беззнаковое целое unsigned short GLushort
32-разрядное беззнаковое целое GLuint, GLenum, GLbitfield

Таблица 1.1 Суффиксы наименований команд и типы данных параметров

OpenGl в Delphi

Вместе с Delphi, начиная с третьей версии, поставляется файл помощи по OpenGL фирмы MicroSoft и заголовочный файл opengl.pas, позволяющий использовать эту графическую библиотеку в приложениях, написанных на Delphi.

Для понимания смысла этих действий желательно понимать смысл основных понятий операционной системы Windows — ссылка, контекст, сообщение, в проектах Delphi не всегда активно используемых программистами. Желательно иметь хотя бы минимальные знания о роли динамических библиотек в этой операционной системе. Хотя, конечно, можно успешно использовать OpenGL и без глубоких знаний в этой области, используя готовые шаблоны приложений и сосредоточившись собственно на функциях OpenGL.

Важно также отметить то, что чаще всего приложения, активно использующие графику, нуждаются от Delphi только в создании окна приложения, таймере и обработчике манипуляций с клавиатурой и мышью. Для таких приложений чаще всего и не требуется богатство библиотеки VCL. и крайне важны скорость работы и «профессиональная» миниатюрность откомпилированного модуля. Поскольку мы вынуждены с самого начала рассматривать и разбирать темы уровнем ниже RAD-технологий, то нам становится по силам и написание программ без визуальных средств вообще, программ, использующих только функции Windows API, стремительно компилируемых и занимающих после компиляции миниатюрные размеры (порядка двух десятков килобайт).

Событие. Сообщение. Контекст.

Начнем наш разговор с понятий «событие» и «сообщение».

Очень часто это синонимы одного и того же термина операционной системы, общающейся с приложениями посредством посылки сообщений. Код, написанный в проекте Delphi как обработчик события OnCreate, выполняется при получении приложением сообщения WM_CREATE, сообщению WM_PAINT соответствует событие OnPaint, и т.д..Такие события использует мнемонику, сходную с мнемоникой сообщений.

Как операционная система различает окна для осуществления диалога с ними? Все окна при своем создании регистрируются в операционной системе и получают уникальный идентификатор, называемый «ссылка на окно». Тип этой величины в Delphi — HWND (WiNDow Handle, ссылка на окно).

Ссылка на окно может использоваться не только операционной системой, но и приложениями для идентификации окна, с которым необходимо производить манипуляции. Свойство Handle формы и есть эта ссылка, значение которой форма получает при выполнении функции API CreateWindow — создании окна. Имея ссылку на окно, операционная система общается с окном путем посылки сообщений-сигналов о том, что произошло какое-либо событие, имеющее отношение именно к этому окну.


Если необходимо нарисовать что-либо на поверхности окна, необходимо получить ссылку на это окно.

Функции Windows для воспроизведения нуждаются в специальной величине типа HDC (Handle Device Context, ссылка на контекст воспроизведения), для задания значения которой необходимо иметь величину типа HWND — ссылка на окно, уникальный идентификатор всех зарегистрированных в системе окон.

Графическая система OpenGL, как и любое другое приложение Windows, также нуждается в ссылке на окно, на котором будет осуществляться воспроизведение — специальной ссылке на контекст воспроизведения — величина типа HGLRC (Handle openGL Rendering Context, ссылка на контекст воспроизведения OpenGL). Для получения этого контекста OpenGL нуждается в величине типа HDC (контекст воспроизведения) окна, на который будет осуществляться вывод.

Поэтому в разделе private описания формы:

А обработчик события OnCreate формы начинается со следующих строк:

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

Контекст воспроизведения Windows и контекст воспроизведения OpenGL обычно освобождаются приложением. То есть, команды вывода OpenGL обычно обрамляются следующими строками:

dc := BeginPaint(Window, ps);

ReleaseDC (Window, dc);

В наших примерах контекст воспроизведения OpenGL мы занимаем сразу же при его получении, в обработчике события OnCreate, а освобождаем в конце работы приложения, в обработчике события OnDestroy.

Ссылка на контекст устройства — величина типа HDC, для получения которой вызываем функцию GetDC. Ссылке на контекст устройства в Delphi соответствует свойство Canvas.Handle формы, принтера и некоторых компонентов.

Win32 Programmer’s Reference фирмы MicroSoft о контексте устройства сообщает следующее:

«Контекст устройства является структурой, которая определяет комплект графических объектов и связанных с ними атрибутов, и графические режимы, влияющие на вывод. Графический объект включает карандаш для изображения линии, щетку для краски и заполнения, растр для копирования или прокрутки частей экрана, палитру для определения комплекта доступных цветов, области для отсечения и других операций, и маршрута для операций рисования». Термин «структура», встретившийся здесь, соответствует записи в терминологии Delphi. Контекст устройства Windows содержит информацию, относящуюся к графическим компонентам GDI, контекст воспроизведения содержит информацию, относящуюся к OpenGL, то есть играет такую же роль, что и контекст устройства для GDI. В частности, эти контексты являются хранилищами состояния системы, например, хранят информацию о текущем цвете карандаша.

Ссылка на контекст устройства содержит характеристики устройства и средства отображения. Именно он знает, как выводить на конкретно это устройство. Упрощенно говоря, получив ссылку на контекст устройства, мы берем в руки простой либо цветной карандаш, или кисточку с палитрой в миллионы оттенков. Прежде чем получить контекст воспроизведения, сервер OpenGL должен получить детальные характеристики используемого оборудования. Эти характеристики хранятся в специальной структуре, тип которой — TPixelFormatDescriptor (описание формата пикселя). Формат пикселя определяет конфигурацию буфера цвета и вспомогательных буферов.

Смысл структуры PixelFormatDescriptor — детальное описание графической системы, на которой происходит работа.

В процедуре SetDCPixelFormat полям структуры присваиваются желаемые значения, затем вызовом функции ChoosePixelFormat осуществляется запрос системе, поддерживается ли на данном рабочем месте выбранный формат пикселя, и вызовом функции SetPixelFormat устанавливаем формат пикселя в контексте устройства. Функция ChoosePixelFormat возвращает индекс формата пикселя, который нам нужен в качестве аргумента функции SetPixelFormat. Заполнив поля структуры TPixelFormatDescriptor, мы определяемся со своими пожеланиями к графической системе, на которой будет происходить работа приложения, машина OpenGL подбирает наиболее подходящий к нашим пожеланиям формат, и устанавливает уже его в качестве формата пикселя для последующей работы. Наши пожелания корректируются применительно к реальным характеристикам системы. То, что машина OpenGL не позволит нам установить нереальный для конкретной машины формат пикселя, значительно облегчает нашу работу. Предполагая, что разработанное приложение будет работать на машинах разного класса, можно запросить «всего побольше», а уж OpenGL разберется на конкретной машине, каковы параметры и возможности оборудования, на котором сейчас будет происходить работа.

Обратим внимание на поле структуры «битовые флаги» — dwFlags. То, как мы зададим значение флагов, существенно может сказаться на работе нашего приложения, и наобум задавать эти значения не стоит. Тем более, что некоторые флаги совместно ужиться не могут, а некоторые могут присутствовать только в паре с другими. В этом примере флагам я присвоил значение PFD_DRAW_TO_WINDOW or PFD_SUPPORT_OPENGL, то есть сообщаю системе, что я собираюсь осуществлять вывод в окно, и что моя система в принципе поддерживает OpenGL. Я ограничился всего двумя константами из обширного списка, приведенного в модуле windows.pas, по каждой из которых в файле помощи приведено детальное описание. Так, константа PFD_DOUBLEBUFFER включает режим двойной буферизации, когда вывод осуществляется не на экран, а в память, затем содержимое буфера выводится на экран. Это очень полезный режим, если в любом примере на анимацию убрать режим двойной буферизации и все команды, связанные с этим режимом, хорошо будет видно мерцание при выводе кадра. Константу PFD_GENERIC_ACCELERATED имеет смысл устанавливать в случае, если компьютер оснащен графическим акселератором. Флаги, заканчивающиеся на «DONTCARE» , сообщают системе, что соответствующий режим может иметь оба значения, то есть PFD_DOUBLE_BUFFER_DONTCARE — запрашиваемый формат пикселя может иметь оба режима — одинарной и двойной буферизации. Со всеми остальными полями и константами я предоставляю Вам возможность разобраться самостоятельно, только замечу, что поле iLayerType, описанное в windows.pas типа Byte, может, согласно помощи, иметь три значения: PFD_MAIN_PLANE, PFD_OVERLAY_PLANE и PFD_UNDERLAY_PLANE, однако константа PFD_UNDERLAY_PLANE имеет значение -1, так что установить такое значение не удастся.

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

В примере битовым флагам задаем все возможные значения одновременно, числовым полям задаем заведомо нереальное значение 64, и смотрим на выбор формата пикселя, сделанным OpenGL. Результат, который Вы получите — выбранный формат пикселя, я предсказать не смогу — он индивидуален для каждой конкретной конфигурации машины и текущих настроек. Возможно, Вы получите в результате, что режим двойной буферизации не будет установлен — напоминаю, многие флаги устанавливаются только в комбинации с другими определенными. Наше приложение позволяет менять параметры формата пикселя и устанавливать его заново. Чтобы видеть, что происходит воспроизведение, небольшая площадка на экране при каждом тестировании окрашивается случайным цветом, используя функции OpenGL. Поэкспериментируйте с этим приложением, например, определите комбинацию флагов для установления режима двойной буферизации. Посмотрите значение числовых полей формата при различной палитре экрана — 16, 24, 32 бита, но не 256 цветов. О выводе при палитре экрана в 256 цветов — отдельный разговор. Это приложение, в частности, дает ответ на вопрос — как определить, оснащен ли компьютер графическим акселератором. Повозившись с этим приложением, Вы найдете ответ на вопрос, на который я Вам ответить не смогу — как надо заполнить структуру TPixelFormatDescriptor для Вашего компьютера. Обратите внимание, что в коде я установил несколько проверок на отсутствие контекста воспроизведения, который может быть потерян по ходу работы любого приложения, использующего OpenGL — редкая, но возможная ситуация в штатном режиме работы системы и очень вероятная ситуация если, например, по ходу работы приложения менять настройки экрана.

Минимальная программа OpenGL

Теперь мы знаем все, что необходимо для построения минимальной программы, использующей OpenGL. Я привел два варианта этой программы — одна построена исключительно на функциях Windows API, другая использует библиотеку классов Delphi (проекты каталогов Beginner/1 и Beginner/2 соответственно).

Взглянем на головной модуль второго проекта. При создании формы задаем формат пикселя, в качестве ссылки на контекст устройства используем значение Canvas.Handle формы. Создаем контекст воспроизведения OpenGL и храним в переменной типа HGLRC. При обработке события OnPaint устанавливаем контекст воспроизведения, вызываем функции OpenGL и освобождаем контекст. При завершении работы приложения удаляем контекст воспроизведения. Для полной академичности можно включить строки, проверяющие, получен ли контекст воспроизведения, и не теряется ли он по ходу работы. Признаком таких ситуаций является нулевое значение переменной hrc. В минимальной программе я просто окрашиваю окно в желтоватый оттенок. Получив помощь по команде glClearColor, Вы можете узнать, что аргументы ее — тройка вещественных чисел в интервале [0;1], задающих долю красного, зеленого и синего составляющих в цвете и еще один, четвертый аргумент, о котором мы поговорим чуть позднее. Этому аргументу я в примере задал значение 1.0. Вообще то, аргументы glClearColor, согласно помощи, имеют неведомый тип GLclampf. Для того, чтобы разобраться с этим типом, отсылаю к строке

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

Строку нашей программы

будем понимать как очистку экрана, фон при этом покрывается заданным цветом.

Проект, построенный только на функциях API, надеюсь, сейчас стал более понятным. Вместо Canvas.Handle используем собственную переменную dc, в обработчике события WM_PAINT реализуем действия, которые Delphi при обычном подходе выполняет за нас. Напоминаю, что для лучшей устойчивости работы обработчик WM_PAINT следовало бы написать так:

dc := BeginPaint (Window, MyPaint);

wglMakeCurrent (dc, hrc);

glClearColor (0.85, 0.75, 0.5, 1.0);

wglMakeCurrent (dc, 0);

EndPaint (Window, MyPaint);

ReleaseDC (Window, dc);

А в обработчике WM_DESTROY следует перед PostQuitMessage добавить строку:

То есть все используемые ссылки необходимо освобождать, а после того, как они стали не нужны — удалять.

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

Во всех своих примерах я приписал рекомендацию не запускать проекты, использующие OpenGL под управлением среды Delphi. Дело в том, что часто в таких ситуациях программа аварийно прерывается, выдавая сообщение «access violation -«. Это происходит и в случае самой аккуратной работы с контекстами, и не связано с небрежностью работы программы. Некоторые программисты вину за это возлагают на софтверные драйверы и рекомендуют обновить их. Некоторые утверждают, что дело в Windows 9X, и под NT этого не происходит. Возможно, Вы тоже ничего такого не замечали и не можете взять в толк, о чем я сейчас веду речь. У меня такие окошки вылетают через раз на одном и том же проекте, хотя откомпилированный модуль работает превосходно. Я полагаю, что если драйверы не «глюкуют», когда приложение работает без среды Delphi, дело не только в драйверах.

Вывод на поверхность компонентов

Теоретически функциями OpenGL возможно осуществлять вывод не только на поверхность формы, а и на поверхность любого компонента, если у него имеется свойство Canvas.Handle, для чего при получении контекста воспроизведения необходимо указывать именно его ссылку на контекст устройства, например, Image1.Canvas.Handle. Однако чаще всего это приводит к неустойчивой работе, вывод «то есть, то нет», хотя контекст воспроизведения присутствует и не теряется. Я советую Вам всегда пользоваться выводом исключительно на поверхность окна. OpenGL прекрасно уживается с визуальными компонентами, как видно из примера TestPFD, если же необходимо ограничить размер области вывода, для этого есть стандартные методы, о которых мы обязательно будем беседовать в будущем.

Просто ради интереса приведу пример, когда вывод OpenGL осуществляется на поверхность панели, то есть компонента, не имеющего свойства Canvas. Для этого мы пользуемся тем, что панель имеет отдельное окно, вызываем функцию GetDC с аргументом Panel1.Handle.

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

Для вывода на компонент класса TImage можете записать:

и удалить строки BeginPaint и EndPaint, поскольку TImage не имеет свойства Handle, то есть не создает отдельного окна. Однако вывод на такие компоненты как раз отличается полной неустойчивостью, так что я не гарантирую Вам надежного положительного результата.

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

В конце сегодняшнего разговора я хочу привести еще несколько проектов, появившихся за это время из под моего пера и дополняющих «ЖиЛистую Delphi».

Общий vao (vbo) для различных окон (контекстов)

У меня есть два GL окна, в которые я хочу выводить одни и те же объекты (модели). Если я создал и заполнил буферы vao и vbo для кого-то из объектов для одного из окон:

09.04.2020, 14:27

Не работает vao и vbo
Здравствуйте, собственно вопрос, а почему когда я нахожу примеры с vao и vbo, то много функций у.

Мультитекстуринг с VAO или VBO
Здравствуйте, проблема состоит в том , что не могу совместить мультитекстуринг и VAO , кто может .

VAO, VBO и их правильное применение
Всех с наступившем, в общем меня интересует вопрос как правильно использовать VBO и VAO чтобы.

Пример использования VAO + VBO
Добрый день. Поделитесь примером использования VAO + VBO, которые реализованы не на WinAPI.

Работа с VAO,VBO и glVertexAttribPointer
Здравствуйте, ЯП C++ Имеется функция рисующая все элементы из vector’a У каждого элемента.

09.04.2020, 14:51 2 09.04.2020, 15:34 [ТС] 3

Я работаю под Qt, использую QOpenGLWidget, у которых в функции инициализации initializeGL(), где заполняются буферы автоматически активизируется контекст текущего виджета. Т.е., как я понял у каждого окна он (контекст) будет свой. Как быть?

Добавлено через 22 минуты
И ещё вопрос. Если контекст общий для двух окон, то, как разделить, что и когда будет выводиться в одном окне, а что в другом?

09.04.2020, 15:35 4
09.04.2020, 15:35
09.04.2020, 15:51 5

Они «шарятся» т.е. несколько QOpenGLContext используют один контекст OpenGL. Cм QSurfaceFormat::setDefaultFormat

09.04.2020, 17:55 [ТС] 6

На сколько я понял из мануала:

When multiple QOpenGLWidgets are added as children to the same top-level widget, their contexts will share with each other. This does not apply for QOpenGLWidget instances that belong to different windows.

This means that all QOpenGLWidgets in the same window can access each other’s sharable resources, like textures, and there is no need for an extra «global share» context, as was the case with QGLWidget.

если виджеты создаются в одном и том же окне, то контекст у них автоматически расшаривается. Тогда остается не понятным, если vao не расшаривается (т.е. для каждого виджета должен быть свой vao), то как прицепить к разным vao созданные ранее vbo, ведь как я понял, vbo можно запихнуть в vao, только создав его заново после glBindVertexArray(vao); (см. мой пример выше). Т.е. по сути получается, что всякий раз нужно будет создавать новые буферы vbo, вместо того, чтобы расшаривать старые.

10.04.2020, 06:04 7

Решение


и установите QSurface::setDefaultFormat. Теперь все создаваемые контексты (во всех окнах) автоматом «шарятся», т.е. используют общие ресурсы.

По поводу VAO не знаю, оно мне без надобности. Но вот VBO шарятся точно. И еще

11.04.2020, 12:03 [ТС] 8

Да, спасибо, это мне поможет, если придется работать с разными окнами. А для виджетов, имеющих владельцем общее окно, как и написано в документации, контексты шарятся автоматически, я проверил, это действительно так, т.е. никаких дополнительных действий не требуется. Достаточно определить буфера/текстуры/шейдеры для одного виджета и они становятся доступны в другом.
А на счет vao, видимо, действительно, без вариантов (не шарятся). Пришлось от него отказаться, работаю только с vbo.

Спасибо за совет про QOpenGLBuffer, хотелось просто, для начала копнуть поглубже.

Добавлено через 17 часов 10 минут
С vao тоже получилось. Для каждого виджета создаем и биндим vao, но создание и заполнение данными объектов делаем только один раз. Для остальных виджетов биндим ранее созданные буферы, включаем буферы и расставляем указатели на начало данных. Таким образом сами буферы с даннми объектов, текстуры и шейдеры в памяти не дублируются. Вот код:

11.04.2020, 12:03
11.04.2020, 12:03

Отрисовка куба через VAO, VBO, EBO
Всем привет Относительно недавно начала изучать OpenGL. Около недели курил glut, теперь перешёл на.

Падение FPS при использовании VBO и VAO
Я модифицирую код движка одной старой игры 2001-ого года и решил добавить шейдеры для модификации.

Несколько vao в один vao
В общем, как объединить несколько уже существующих вао в один ? У них одинаковые атрибуты.

Структура приложений, использующих OpenGL

Цель лекции: Создать базовые приложения с использованием OpenGL на C# и на Object Pascal (Delphi).

Примеры

Пример № 3.1 Базовые приложения на C# (WinForms) и Object Pascal (Delphi), использующие библиотеку OpenGL .

В состав приложения, формирующего изображениe с помощью OpenGL , должно входить несколько программных блоков:

  • Инициализация библиотеки OpenGL.
  • Подготовительные операции для формирования изображения библиотекой OpenGL (при необходимости).
  • Определение области вывода изображения.
  • Основная функция формирования изображения.
  • Обработка событий мыши и клавиатуры для реализации интерактивности в программе (при необходимости).
  • Завершение работы c библиотекой OpenGL.

Инициализация OpenGL в библиотеке OpenTK

Инициализацию OpenGL в библиотеке OpenTK выполняет графический компонент GLControl , который размещается на одной из форм приложения. Вывод изображения, формируемого библиотекой OpenGL , выпоняется в области компонента GLControl . По умолчанию библиотека OpenTK инициализирует использование палитры RGBA , двойной буфер , устанавливает размер буфера глубины равным 16 бит .

Дополнительные настройки библиотеки OpenGL необходимо выполнять в обработчике события Load формы. В конструкторе формы этого делать не следует, так как еще не создан контекст изображения для компонента GLControl .

В следующем разделе рассмотрена инициализация библиотеки OpenGL в приложении на Object Pascal (Delphi). Аналогичную процедуру инициализации выполняет и библиотека OpenTK , чтобы в этом убедиться, достаточно заглянуть в исходный код этой библиотеки.

Инициализация библиотеки OpenGL в приложениях на Object Pascal (Delphi)

Перед началом использования команд OpenGL необходимо выполнить инициализацию библиотеки OpenGL . Основной задачей данного этапа является определение основных параметров работы библиотеки OpenGL , которые в дальнейшем не могут быть изменены без завершения работы с библиотекой OpenGL и её повторной инициализации. На этом этапе выполняется определение доступных возможностей аппаратуры для вывода изображения. Хотя библиотека OpenGL является кросплатформенной, но инициализация является специфичной для каждой платформы, поэтому на этом этапе используются функции операционной системы Windows . Инициализация библиотеки может выполняться в обработчике события OnCreate формы, на которой будет выводиться изображение. Для возможности использования функций и процедур библиотеки OpenGL необходимо указать модуль opengl.pas в разделе uses каждого модуля, в котором они будут использоваться. Ниже приведен пример инициализации библиотеки OpenGL .

В листинге 3.1 объявляются глобальные переменные hrc и dc . В дальнейшем они будут использоваться для освобождения ресурсов операционной системы при завершении работы с библиотекой OpenGL ( пример 3.5). Большинство процедур и функций в листинге 3.1 не входят в состав OpenGL и являются функциями операционной системы. В частности функции, названия которых начинаются с последовательности символов «wgl» , также являются системными и предназначены для поддержки возможностей OpenGL в операционной системе Windows .

При разработке программ на языке программирования Object Pascal c использованием библиотеки визуальных компонентов ( VCL ) системные функции использовать приходится довольно редко, так как набор классов библиотеки VCL позволяет этого избежать. При работе с библиотекой OpenGL потребуется несколько системных функций. В процедуре FormCreate с помощью системной функции GetDC выполняется получение дескриптора контекста устройства. Функция GetDC позволяет получить параметры вывода изображения, которые используются для формы. Информация о форме передается в функцию с помощью дескриптора формы, который возвращается свойством формы Handle .

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

Процедура имеет следующие параметры:

  • Dest — переменная, область памяти которой требуется заполнить значением Value ;
  • Count – размер заполняемой области памяти в байтах;
  • Value – значение байта, которое должно быть скопировано в каждый байт памяти, определяемой параметром Dest .

В листинге 3.1 для определения размера структуры PFD используется функция

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

Для работы с библиотекой OpenGL операционная система определяет наиболее подходящие параметры вывода изображения, основные из которых перечислены ниже:

  • Тип используемой цветовой палитры: индексная или RGBA. В примерах будет использована только RGBA палитра, которая используется по умолчанию.
  • Глубина цвета или количество бит, которое будет использоваться для хранения цвета каждого пиксела.
  • Использование вторичного буфера.
  • Число бит в буфере глубины.
  • Размер буфера трафарета.

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

Для запроса у операционной системы наиболее подходящей конфигурации библиотеки OpenGL для работы приложения используется функция

Назначение параметров функции:

  • DC – контекст устройства, полученный с помощью системной функции GetDC .
  • p2 – указатель на переменную типа TPixelFormatDescriptor . В переменной предварительно должны быть указаны параметры библиотеки OpenGL , которые требуются для работы приложения.

Функция возвращает номер наиболее подходящей конфигурации библиотеки OpenGL . Операционная система Windows предоставляет ограниченное количество возможных вариантов параметров работы библиотеки OpenGL . Каждое из таких сочетаний имеет свой уникальный целочисленный номер. Один из таких номеров и возвращает функция ChoosePixelFormat .

В выше приведенном примере ( пример 3.1) поля структуры TPixelFormatDescriptor в переменной PFD заполняются нулевыми значениями с помощью процедуры FillChar , и затем поле dwFlags инициализируется для использования библиотекой OpenGL вторичного буфера.

Для заданных значений структуры TPixelFormatDescriptor операционная система возвращает номер конфигурации для библиотеки OpenGL , в которой:

  • используется палитра RGBA;
  • глубина цвета соответствует текущим параметрам дисплея, на котором выводится форма;
  • используется вторичный буфер;
  • для буферов трафарета и глубины установлены максимальные значения.

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

При использовании вторичного буфера библиотека OpenGL формирует изображение сначала во вторичном буфере. Вторичный буфер представляет собой блок памяти и непосредственно не отображается. После завершения формирования изображения во вторичном буфере он должен быть скопирован в область вывода изображения. Основным назначением вторичного буфера является увеличение скорости формирования изображения.


Константы , используемые для задания значения поля dwFlags структуры TPixelFormatDescriptor , имеют следующие значения:

  • PFD_DRAW_TO_WINDOW – изображение формируется в окне (на форме). Значение должно быть указано обязательно.
  • PFD_SUPPORT_OPENGL – изображение формируется с библиотекой OpenGL . Значение должно быть указано обязательно.
  • PFD_DOUBLEBUFFER – для формирования изображения будет использоваться вторичный буфер.

Для отображения изображения, сформированного во вторичном буфере, используется системная функция :

Пример её использования приведен в листинге 3.3 .

Для установки выбранного значения формата пиксела используется функция :

Назначение параметров функции:

DC – контекст устройства, полученный с помощью системной функции GetDC .

PixelFormat – номер формата пиксела, полученный с помощью функции ChoosePixelFormat .

FormatDef – указатель на переменную типа TPixelFormatDescriptor , в которой должны быть значения, соответствующие выбранному номеру формата пиксела, который указан в качестве значения параметра PixelFormat .

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

Функции передается единственный параметр DC – контекст устройства, полученный с помощью системной функции GetDC . Функция возвращает дескриптор контекста воспроизведения.

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

Назначение параметров функции:

  • DC – контекст устройства, соответствующий форме, на которой располагается область вывода.
  • p2 – дескриптор контекста воспроизведения, который необходимо сделать текущим.

Функция возвращает логическое значение , информирующее об успешности выполнения операции .

В листинге 3.1 после инициализации библиотеки вызывается процедура glEnable со значением параметра GL_DEPTH_TEST , который включает тест глубины.

Тест глубины реализует отбор примитивов и их фрагментов при формировании изображения в зависимости от удаленности от наблюдателя и выполняет удаление фрагментов, которые заслоняют примитивы, находящиеся ближе к точке наблюдения. При отключенном тесте глубины ( по умолчанию после инициализации библиотеки) библиотека не выполняет анализ того, какой из примитивов не виден наблюдателю. Примитивы отображаются в порядке их формирования и поэтому примитивы, которые были сформированы последними, будут «заслонять» другие примитивы в не зависимости от их реальной удалённости от наблюдателя.

OpenGL. Урок 1. Введение.

Всем доброго дня!

Сегодняшней статьей я положу начало новому учебному курсу на нашем сайте и посвящен он будет программированию с использованием графической библиотеки OpenGL. Начнем с основных концепций OpenGL, затем плавно перейдем к 2D и 3D-графике. В общем, будем много интересного �� Постараюсь все объяснять максимально подробно и понятно, чтобы статьи оказались действительно полезными.

Начнем с инструментария. Я, будучи большим любителем Qt, буду использовать модуль QtOpenGL. В принципе, его функционал не сильно отличается от библиотеки GLUT, так что думаю особых ограничений на изучение материала использование именно Qt не наложит )

Подключается библиотека максимально просто – надо всего лишь добавить в .pro файл проекта строку:

Согласитесь, проще уже некуда �� Двигаемся дальше…

Базовым классом для работы с OpenGL в Qt является класс QGLWidget. Соответственно, класс виджета, в котором мы будем работать с OpenGL должен наследоваться от класса QGLWidget. Давайте сразу же параллельно с теорией рассматривать пример, чтобы лучше понимать как все устроено.

Итак, создаем проект, подключаем библиотеку и добавляем в наш проект новый класс. Дадим ему название MainScene. В проекте получаем два файла для нашего нового класса. Вот минимальный необходимый код для работы с OpenGL.

MainScene.h:

MainScene.cpp:

Итак, что мы тут имеем? А всего лишь пустой конструктор класса и три пока неизвестные нам функции. Сейчас будем разбираться, как эти функции работают и зачем они вообще нужны.

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

Рассмотрим немного подробнее каждую из этих функций в отдельности.

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

Эту функцию не надо вызывать явно в теле конструктора виджета или в любом другом месте, она вызывается автоматически при создании виджета. Как и положено функции инициализации она вызывается только один раз, как уже сказано выше – при создании виджета. На самом деле тут все очень логично, даже особо нечего обсудить )

Следующая на очереди – функция resizeGL().

Эта функция вызывается каждый раз при изменении размеров окна. То есть когда пользователь запустил приложение и решил, к примеру, увеличить окно программы сразу же выполнится код, содержащийся в функции resizeGL(). Кроме этого случая, функция вызывается один раз после функции initializeGL():
Создание виджета => initializeGL() => resizeGL().

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

Функция resizeGL() работает с двумя параметрами:

  • w – новое значение ширина окна виджета
  • h – новое значение высоты окна виджета

С resizeGL() вроде бы разобрались) Идем дальше!

Следующая на очереди функция paintGL(). Она вызывается каждый раз после вызова функции resizeGL() и запускает перерисовку сцены. Кроме этого случая, paintGL() вызывается каждый раз после вызова еще одной функции, а именно updateGL():
updateL() => paintGL()

Строго говоря это процесс протекает несколько иначе. На самом деле и при создании виджета, и при изменении размеров окна, и при вызове функции updateGL() вызывается функция glDraw(), принадлежащая классу QGLWidget. Но при написании программы непосредственно с этой функцией работать не надо, все действия связаны с функциями initializeGL(), resizeGL() и paintGL().

Таким образом, если мы хотим следить за каким то определенным событием и после его наступления выполнять перерисовку сцены мы должны использовать функцию updateGL(), вызов которой повлечет за собой вызов функции paintGL() и перерисовку сцены. Рассмотрим небольшой пример.

Пусть нам надо нарисовать фигуру и реализовать простейшею анимацию – перемещение нашей фигуры по экрану. Для того, чтобы получить нужный эффект будем периодически перерисовывать нашу фигуру, располагая ее в новом месте. Как все это сделать? А очень просто! Берем таймер, по сигналу таймера (допустим каждые 50 мс) вызывается слот, в котором мы изменяем координаты фигуры и вызываем функцию updateGL(). Эта функция в свою очередь вызовет paintGL(), которая уже перерисует всю сцену и нарисует нашу фигуру в соответствии с новыми координатами.

На словах вроде бы понятно, обязательно еще вернемся к этому практическому примеру и тогда уже поработаем с кодом ��

А на сегодня, пожалуй, на этом закончим, разобрались мы в общих чертах со структурой проекта, использующего OpenGL, а также рассмотрели механизмы прорисовки. Для вводной статьи думаю этого вполне достаточно, так что до скорых встреч на нашем сайте!

Реферат: «Применение OpenGL»

Государственное образовательное учреждение высшего профессионального образования
«ТОМСКИЙ ПОЛИТЕХНИЧЕСКИЙ УНИВЕРСИТЕТ»

Факультет автоматики и вычислительной техники

Студент группы 8В21 _________ Я.Е. Негрей

Ассистент _________ А.Е. Соколов

Содержание

1 Введение

Для ускорения выполнения этапов 3D – конвейера в ускоритель трехмерной графики должен обладать определенным наборам функций, т.е. аппаратно, без участия центрального процессора, производить операции, необходимые для построения 3D – изображения. Специальный пакет служебных команд и программ для выполнения этих функций называют интерфейсом прикладного программирования (Application Program Interface – API).

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

В настоящее время существуют различные пакеты API функций. Такие как DirectX, OpenGL. А также существуют частные разработки пакетов API функций некоторых фирм ориентированные только на свои 3D – акселераторы и Chipset.

DirectX – разработка фирмы Microsoft. Это пакет разработан в первую очередь для игровых приложений, работающих под операционной системой Windows NT/9X и более поздних версиях. Данный пакет является закрытой разработкой.

В отличии DirectX, OpenGL – это открытая разработка. Данный пакет состоит из двух частей. Первая часть – это базовый набор функций. Каждый производитель 3D – акселераторов, выпускающих свою продукцию с поддержкой OpenGL, должен их реализовать. И второй Части – функций расширения, реализующих более сложных преобразования.

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

2 Начало работы с OpenGL

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


procedure TMainWindow.FormCreate(Sender: TObject);

SaveDefaultMode(); // Сохранения текущих параметров экрана

SetFullscreenMode(1); // Установка нового режима работы экрана

SetDCPixelFormat(Canvas.Handle); // установка параметров отображения

hrc := wglCreateContext(Canvas.Handle); // создание контекста отображения

// для функций OpenGL

If hrc = 0 then begin

ShowMessage (‘Ошибка получения контекста воспроизведения!’);

wglMakeCurrent(Canvas.Handle, hrc); // выбираем текущем нашу форму

procedure TMainWindow.SetDCPixelFormat(DC : HDC);

FillChar(pfd, SizeOf(pfd), 0);

// устанавливаем параметры отображения пикселя

dwFlags := PFD_DRAW_TO_WINDOW or

nPixelFormat := ChoosePixelFormat(DC, @pfd);

SetPixelFormat(DC, nPixelFormat, @pfd);

Рассмотрим функции более подробнее:

С помощь функций SaveDefaultMode и SetFullscreenMode изменяют размер экрана используя функции ChangeDisplaySettings , но эта функция не из пакета OpenGL.

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

BOOL SetPixelFormat(HDC hdc,int iPixelFormat,CONST PIXELFORMATDESCRIPTOR * ppfd);

hdc – эта переменная отвечает за то к какому устройству применить установки.

iPixelFormat – номер формата пикселя, этот номер получают используя функцию ChoosePixelFormat

ppfd – это логической описания пикселя. PIXELFORMATDESCRIPTOR это специальная структура, описывающая все атрибуты выводимых пикселей. В виде основных элементов этой структуры можно выделить следующие:

· dwFlags – этот элемент описывает все свойства буфера пикселей, например такие как используется двойная буферизации или буферизация стерео изображения, или например куда выводить изображения на окно или в память в виде картинки.

· iPixelType – описывает как будет хранится информации о пикселе в буфере. Можно установить различные значения, например PFD_TYPE_RGBA или PFD_TYPE_COLORINDEX.

· cColorBits – устанавливает размер памяти отводимый под один пиксель в буфере

Как видно из кода программы мы установили двойную буферизацию, поддержку OpenGl и вывод на экан. Также установив формат хранения информации о пикселе в RGBA (Red Green Blue Alpha, где Alpha отвечает за прозрачность)

Далее необходимо создать контекст устройства отображения для OpenGL, это необходимо из-за того что OpenGL только со своим контекст и не может использовать контекст вывода Windows HDC. Данная операция осуществляется функцией wglCreateContext. В нашем случае мы создаем контекст на холст нашей формы. Но мало просто создать контекст его еще необходимо сделать текущим. Это используется для того, чтобы, создав несколько контекстов вывода, мы могли бы точно и быстро определить на какое устройство пойдет дальнейший вывод. Выбор контекста отображения осуществляется с помощью функции wglMakeCurrent.

HGLRC wglCreateContext( HDC hdc );

BOOL wglMakeCurrent( HDC hdc , HGLRC hglrc);

Для отмены выделения контекста отображения используется таже функция wglMakeCurrent, но с подачей констант NULL. А для удаления контекста отображения OpenGL необходимо использовать спезыальную функцию wglDeleteContext.

BOOL wglDeleteContext( HGLRC hglrc );

3 Об игре

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

Хотя на OpenGL трехмерные и двухмерные построения осуществляются с разной сложностью, но и возможности тоже различны, поэтому нужно рассмотреть плюсы и минусы более подробно.

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

При использовании двухмерной графики вся анимация и эффекты сводится к прицепу создания рисованных мультфильмов. Такая технология называется «Спрайтовой» в ней используется большая коллекция картинок, из которой выбираются различные картинки, соединяются в один кадр, затем это кадр выводится на экран. Для вывода следующего кадра необходимо повторить эту операцию, только заменив одну из картинок на другую или повернуть или сместить в другую точку на кадре. Такой вариант гораздо мене трудоемок и требую меньших затрат в программирования. Но данная технология обладает одним большим минусом, качество анимации полностью зависит от качества картинок в коллекции, поэтому для всех играх того плана требуется много памяти на жестком диске.

Так как это наш первый проект того плана, поэтому мы остановимся на варианте двухмерной графики.

Для этого нам понадобится функции вывода растрового изображения. Используя OpenGL эту проблему можно решить различными путями, например загрузить их в виде текстур а потом выводить прямоугольники с наложением на них этих текстур. Но есть способ проще в OpenGL есть специальная функция которая выводит растровое изображение в указанную точку на экране. Рассмотрим пример.

Opengl лекция №1: введение события сообщения контекст

Я начинаю это пособие непосредственного с кода, разбитого на секции, каждая из которых будет подробно комментироваться. Первое, что вы должны сделать — это создать проект в Visual C++. Если это для вас затруднительно, то вам стоит для начала изучить C++, а уже затем переходить к OpenGL.

После того как вы создадите новое приложение в Visual C++ (причем Win32 приложение, а не консольное), Вам надо будет добавить для сборки проекта библиотеки OpenGL. В меню Project/setting, выберите закладку LINK. В строке «Object/Library Modules» добавьте «OpenGL32.lib GLu32.lib GLaux.lib». Затем кликните по OK. Теперь все готово для создания программы с OpenGL.

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

OpenGL code

Далее, необходимо инициализировать все переменные, которые будут использованы в вашей программе. Эта программа будет создавать пустое OpenGL окно, поэтому мы не будем нуждаться в большом количестве переменных. То немногое, что мы устанавливаем — очень важно, и будет использоваться в каждой программе с OpenGL, которую вы напишите с использованием этого кода.

Первые две строки устанавливают Контексты Рендеринга, которые связывает вызовы OpenGL с окном Windows. Контекст Рендеринга OpenGL определен как hRC. Для того чтобы рисовать в окне, вам необходимо создать Контекст Устройства Windows, который определен как hDC. DC соединяет окно с GDI. RC соединяет OpenGL с DC.

OpenGL code

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

OpenGL code

Opengl Code

В следующей строке устанавливается цвет, которым будет очищен экран. Для тех, кто не знает, как устроены цвета, я постараюсь кратко объяснять. Все значения могут быть в диапазоне от 0.0f до 1.0f, при этом 0.0 самый темный, а 1.0 самый светлый. Первое число в glClearColor — это интенсивность красного, второе – зеленного, третье – синего. Наибольшее значение – 1.0f, является самым ярким значением данного цвета. Последнее число — для альфа значения. Когда начинается очистка экрана, я никогда не волнуюсь о четвертом числе. Пока оно будет 0.0f. Как его использовать, я объясню в другом уроке.

Поэтому, если вы вызвали glClearColor(0.0f,0.0f,1.0f,0.0f) вы произведете очистку экрана, с последующим закрашиванием его в ярко синий цвет. Если вы вызвали glClearColor(0.5f,0.0f,0.0f,0.0f) экран будет заполнен умеренно красным цветом. Не очень ярким (1.0f) и не темным (0.0f), а именно умеренно красным. Для того чтобы сделать белый фон, вы должны установить все цвета в (1.0f). Черный — как можно ниже (0.0f).

Opengl code

Следующие три строки создают Буфер Глубины. Думайте о буфере глубины как о слоях на экране. Буфер глубины указывает, как далеко объекты находятся от экрана. Мы не будем реально использовать буфер глубины в этой программе, но любая программа с OpenGL, которая рисует на экране в 3D будет его использовать. Он позволяет сортировать объекты для отрисовки, поэтому квадрат расположенный под кругом не изображен будет поверх круга. Буфер глубины очень важная часть OpenGL.

OpenGL code

Следующие пять строк разрешают плавное цветовое сглаживание (которое я буду объяснять позднее) и установку экрана для перспективного просмотра. Отдаленные предметы на экране кажутся меньшими, чем ближние. Это придает сцене реалистичный вид. Перспектива вычисляется c углом просмотра 45 градусов на основе ширины и высоты окна. 0.1f, 100.0f глубина экрана.

glMatrixMode(GL_PROJECTION) сообщает о том, что следующие команды будут воздействовать на матрицу проекции. glLoadIdentity() – это функция работает подобно сбросу. Раз сцена сброшена, перспектива вычисляется для сцены. glMatrixMode(GL_MODELVIEW) сообщает, что любые новые трансформации будут воздействовать на матрицу просмотра модели. Не волнуйтесь, если вы что-то не понимаете этот материал, я буду обучать всему этому в дальнейших уроках. Только запомините, что НАДО сделать, если вы хотите красивую перспективную сцену.

OpenGl code

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

OpenGL code

GLvoid ReSizeGLScene(GLsizei Width, GLsizei Height)
<
if (Height==0) // Предотвращение деления на ноль, если окно слишком мало
Height=1;

glViewport(0, 0, Width, Height);
// Сброс текущей области вывода и перспективных преобразований

glMatrixMode(GL_PROJECTION); // Выбор матрицы проекций
glLoadIdentity(); // Сброс матрицы проекции

gluPerspective(45.0f,(GLfloat)Width/(GLfloat)Height,0.1f,100.0f);
// Вычисление соотношения геометрических размеров для окна
glMatrixMode(GL_MODELVIEW); // Выбор матрицы просмотра модели
>

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

Opengl code

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

OpenGL code


Код между скобками устанавливает формат пикселей. Я предпочитаю не использовать режим индексации цвета. Если вы не знаете, что это означает, не заботьтесь об этом. Формат описания пикселя описывает, как OpenGL будет выводить в окно. Большинство кода игнорируется, но зачастую это необходимо. Я буду помещать короткий комментарий для каждой строки. Знак вопроса означает, что я не уверен, что это строка кода делает (я только человек!).

OpenGL code

OpenGl code

WM_CREATE сообщает программе, что оно должно быть создано. Вначале мы запросим DC (контекст устройства) для вашего окна. Помните, без него мы не можем рисовать в окно. Затем мы запрашиваем формат пикселя. Компьютер будет выбирать формат, который совпадает или наиболее близок к формату, который мы запрашиваем. Я не делаю здесь множества проверок на ошибки, чтобы сократить код, но это неправильно. Если что-то не работает, я просто добавляю необходимый код. Возможно, вы захотите посмотреть, как работают другие форматы пикселей.

OpenGL code

Если подходящий формат пикселя не может быть найден, будет выведено сообщение об ошибке с соответствующем уведомлением. Оно будет ждать, когда вы нажмете на OK, до выхода из программы.

OpenGL code

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

OpenGL code

Если код сделан, как показано выше, будет создан DC (контекст устройства), и установлен подходящий формат пикселя. Сейчас мы создадим Контекст Рендеринга, для этого OpenGL использует DC. wglCreateContext будет захватывать Контекст Рендеринга и сохранять его в переменной hRC. Если по какой-то причине Контекст Рендеринга не доступен, выскочит сообщение об ошибке. Нажмите OK для вызова программы.

OpenGL code

Сейчас мы имеем Контекст Рендеринга, и нам необходимо сделать его активным, для того чтобы OpenGL мог рисовать в окно. Снова, если по не которой причине это не может быть сделано, выскочит сообщение об ошибке. Кликните OK в окошке ошибки для выхода из программы.

OpenGl code

Если все прошло удачно, то у нас есть все для того, чтобы создать область рисования OpenGL. GetClientRect возвратит нам ширину и высоту окна. Мы запомним ширину справа, и высоту снизу. После того как мы получили ширину и высоту, инициализируем экран OpenGL. Мы делаем это при помощи вызова InitGL, передавая в параметрах право и низ (ширину и высоту).

OpenGL code

WM_DESTROY и WM_CLOSE очень похожи. Программа будет посылать это сообщение каждый раз, когда вы выходите из программы, нажав ALT-F4, или если вы послали PostQuitMessage(0) также как мы делали, когда происходила ошибка.

ChangeDisplaySettings(NULL,0) будет переключать разрешение рабочего стола обратно, делая его таким, каким мы переключались из него в полноэкранный режим. ReleaseDC(hWnd,hDC) уничтожает контекст устройства окна. По существу это уничтожает окно OpenGL.

OpenGl code

case WM_DESTROY:
case WM_CLOSE:
ChangeDisplaySettings(NULL, 0);

wglMakeCurrent(hDC,NULL);
wglDeleteContext(hRC);
ReleaseDC(hWnd,hDC);

WM_KEYDOWN вызывается всякий раз при нажатии клавиши. Клавиша, которая была нажата, сохраняется в переменной wParam. Итак, что же делает следующий код. Скажем, я нажал ‘A’. Буква фактически – это число, которое ее представляет. Поэтому в ячейку, которая представляет ‘A’ заносится TRUE. Позднее, в коде, если я проверю состояние ячейки и увижу TRUE, то я знаю, что клавиша ‘A’ действительно в этот момент нажата.

OpenGL code

WM_KEYUP вызывается всякий раз, когда клавиша отпускается. Клавиша, которая отжата, также сохраняется в переменной wParam. Поэтому, когда я отпускаю клавишу ‘A’, это делает ячейку для клавиши ‘A’ равной FALSE. Когда я проверю ячейку, для того чтобы увидеть нажата ли клавиша ‘A’, она вернет FALSE, что означает «нет, она не нажата».

OpenGL code

И последнее, что я сделаю — обработаю изменение размеров окна. Возможно, кажется, что бесмыслено добавлять этот код, когда программа запущена в полноэкранном режиме, но без этого кода, экран OpenGL не появится. Поверьте, мне это очень важно.

Всякий раз сообщение WM_SIZE посылается Windows с двумя параметрами — новая ширина, и новая высота экрана. Эти параметры сохранены в LOWORD(lParam) и HIWORD(lParam). Поэтому вызов ReSizeGLScene изменяет размеры экрана. Это передает высоту и ширину в эту секцию кода.

OpenGL code

Затем, дадим Windows обработать все сообщения, которые мы не обрабатываем и завершим процедуру.

OpenGL code

Это то место, где начинается программа, где создается окно, где делается практически все, кроме рисования. Мы начинаем с создания окна.

OpenGL code

Флаги стиля CS_HREDRAW и CS_VREDRAW принуждают перерисовать окно всякий раз, когда оно перемещается. CS_OWNDC создает скрытый DC для окна. Это означает, что DC не используется совместно нескольким приложениями. WndProc — процедура, которая перехватывает сообщения для программы. hIcon установлен равным нулю, это означает, что мы не хотим ICON в окне, и для мыши используем стандартный указатель. Фоновый цвет не имеет значения (мы установим его в GL). Мы не хотим меню в этом окне, поэтому мы используем установку его в NULL, и имя класса – это любое имя которое вы хотите.

OpenGL code

Сейчас мы регистрируем класс. Если произошла ошибка, появится соответствующее сообщение. Кликните на OK на окно с сообщение об ошибке и будете выкинуты из программы.

OpenGL code

Сейчас мы сделаем окно. Не смотря на то, что мы делаем окно здесь, это не вызовет OpenGL до тех пор, пока сообщение WM_CREATE не послано. Флаги WS_CLIPCHILDREN и WS_CLIPSIBLINGS требуются для OpenGL. Очень важно, чтобы вы добавили их здесь. Я люблю использовать всплывающее окно, оно хорошо работает в полноэкранном режиме.

OpenGl code

hWnd = CreateWindow(
«OpenGL WinClass»,
«Jeff Molofee’s GL Code Tutorial . NeHe ’99», // Заголовок вверху окна

WS_POPUP |
WS_CLIPCHILDREN |
WS_CLIPSIBLINGS,

0, 0, // Позиция окна на экране
640, 480, // Ширина и высота окна

NULL,
NULL,
hInstance,
NULL);

Далее — обычная проверка на ошибки. Если окно не было создано по какой-то причине, сообщение об ошибке выскочит на экран. Давите OK и завершайте программу.

OpenGL code

Следующая секция кода вызывает у многих людей массу проблем … переход в полноэкранный режим. Здесь важна одна вещь, которую вы должны запомнить, когда переключаетесь в полноэкранный режим — сделать ширину и высоту в полноэкранном режиме необходимо туже самую, что и ширина и высота, которую вы сделали в своем окне.

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

Важно отметить, что этот код не будет скомпилирован на Cи. Это файл должен быть сохранен как .CPP файл.

OpenGL code

DEVMODE dmScreenSettings; // Режим работы

memset(&dmScreenSettings, 0, sizeof(DEVMODE)); // Очистка для хранения установок
dmScreenSettings.dmSize = sizeof(DEVMODE); // Размер структуры Devmode
dmScreenSettings.dmPelsW >dmScreenSettings.dmPelsHeight = 480; // Высота экрана
dmScreenSettings.dmFields = DM_PELSWIDTH | DM_PELSHEIGHT; // Режим Пиксела
ChangeDisplaySettings(&dmScreenSettings, CDS_FULLSCREEN);
// Переключение в полный экран

ShowWindow название этой функции говорит само за себя — она показывает окно, которое вы создали на экране. Я люблю это делать, после того как я переключусь в полноэкранный режим, хотя я не уверен, что это имеет значения. UpdateWindow обновляет окно, SetFocus делает окно активным, и вызывает wglMakeCurrent(hDC,hRC) чтобы убедиться, что Контекст рендеринга не освобожден.

OpenGL code

Теперь мы создадим бесконечный цикл. Есть только один момент выхода из цикла, — когда нажали ESC. При этом программе будет отправлено сообщение о выходе, и она прервется.

OpenGL code

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

SwapBuffers(hDC) очень важная команда. Мы имеем окно с установленной двойной буферизацией. Это означает, что изображение рисуется на скрытом окне (называемым буфером). Затем, мы говорим компьютеру переключить буфера, скрытый буфер копируется на экран. При этом получается плавная анимация без рывков, и зритель не замечает отрисовку объектов.

OpenGL code

В этом уроке я попытался объяснить как можно больше деталей каждого шага запутанной установки, и создания ваших собственных полноэкранных OpenGL программ, которые будут завершаться при нажатии ESC. Я потратил 3 дня и 13 часов для написания этого урока. Если вы имеете любые комментарии или вопросы, пожалуйста, пошлите их мне по электронной почте. Если вы ощущаете, что я некорректно комментировал что-то или что код должен быть лучше в некоторых секциях по некоторым причинам, пожалуйста, дайте мне знать. Я хочу сделать уроки по OpenGL хорошими насколько смогу. Я заинтересован в обратной связи.

Примечание переводчика: исходные коды для этого урока и других уроков, есть на сайте NeHe. Чтобы найти их, Вам надо перейти на оригинальный урок по ссылке, которая расположена вначале текста любого переведенного урока. Затем, надо перейти в конец оригинального (англоязычного) урока и там найти ссылки на нужный архив с нужным исходным кодом, для своего компилятора, или платформы, или языка (например, Delphi). Поместить все исходные коды для уроков NeHe на этом сайте, просто невозможно!

Еще одно примечание переводчика: в связи с тем, что NeHe иногда изменяет уроки, но при этом нет возможности понять когда он это сделал (нет даты последнего изменения урока), то бывает так, что текст переведенных уроков незначительно отличается от текущих оригинальных уроков. Не пугайтесь этого, смысл урока при этом значительно не меняется.

Опыт изучения OpenGL — Часть 1 — Введение

С детства мечтал писать компьютерные игры, и вот, года три назад решил рискнуть — поизучать OpenGL. Почему взялся изучать не игровой движок, а низкоуровневый API (API — Application Programming Interface)? Потому что люблю изучать все с как можно более низкого уровня, чтобы разобраться как оно работает (например, изучение языка ассемблера Intel очень помогло моему пониманию языков Си и C++). А почему OpenGL, а не DirectX? — Просто по OpenGL я смог найти больше литературы, чем по DirectX. Изучать OpenGL — задачка очень непростая, а теперь появился еще более низкоуровневый API — Vulkan, пытаюсь и про него читать понемногу. Но сначала хочу поделиться с вами своим опытом изучения OpenGL. Штурмовал я его раза три c перерывами в год, и на третий раз наконец достиг чего-то более сложного, чем рисование разноцветных треугольников — объемные движущиеся объекты, движущаяся камера, освещение, текстуры и тени — уже, на мой взгляд, неплохо (рисунок 1).

Рисунок 1 — Солнце, Земля и Луна и Млечный Путь на заднем плане, отрисованные при помощи OpenGL. Луна вращается вокруг Земли, а Земля и Солнце — вокруг своих осей. Солнце является единственным точечным источником света. Используется модель освещения Phong lighting model плюс shadow mapping. Всё это отрисовывается программой, которую я написал сам, используя только OpenGL, стандартную библиотеку языка C++ (STL) и WinAPI.

Литература

Книги по компьютерной графике вообще и по OpenGL в частности (указаны в порядке субъективного убывания полезности):

  • Jason McKesson — Learning Modern 3D Graphics Programming
  • David Wolf — OpenGL 4 Shading Language Cookbook (2nd Edition)
  • Jason Gregory — Game Engine Architecture (2nd Edition)
  • Edward Angel, Dave Shreiner — Interactive Computer Graphics (6th Edition)
  • Dave Shreiner, Graham Sellers, John Kessenich, Bill Licea-Kane — OpenGL Programming Guide (8th Edition)
  • John Kessenich, Graham Sellers, Dave Shreiner — OpenGL Programming Guide (9th Edition)
  • Graham Sellers — OpenGL SuperBible (7th Edition)
  • OpenGL 4.5 API and Shading Language Reference Pages

Книги по C++ (все перечисленные, на мой взгляд, одинаково полезны):


  • Бьярн Страуструп — Программирование. Принципы и практика с использованием C++
  • Bjarne Stroustrup. The C++ Programming Language. Fourth Edition.
  • Скотт Мейерс — Эффективное использование C++
  • Скотт Мейерс — Наиболее эффективное использование C++
  • Scott Meyers — Effective Modern C++

OpenGL — это C API, т. е. пользоваться им проще всего в программах на языках C/C++. Авторы книг ставят перед собой цель продемонстрировать непосредственное использование этого API, поэтому в книгах по OpenGL весь код как правило написан на языке Си, причем он длинный, трудночитаемый и трудноизменяемый. Я хотел сравнительно легко и быстро писать графические программы, поэтому должен был написать свой API поверх OpenGL, и конечно, на C++ (далее в тексте я, говоря о своем проекте, могу называть его по-разному: API, движок, фреймворк). Когда создаешь свой API, хорошо бывает применить top-down approach, т. е. проектирование «сверху вниз». Я этот подход описываю так: представьте, как должна выглядеть ваша программа, когда ваш API уже полностью написан. Запишите эту программу — в ней будут конечно же создаваться объекты, вызываться методы классов… Вот, теперь вы знаете какие классы вам нужны и какие у них должны быть методы — напишите же их!

В написании собственного API мне очень помогла великолепная книжка [Gregory]. Что касается книг по API OpenGL… Официальное руководство по программированию (т. н. красная книга) [OpenGL Programming Guide], как мне кажется, не очень годится для изучения, оно скорее напоминает справочник, и читать его ужасно скучно. К счастью я нашел минимум две книги «с человеческим лицом». [McKesson] хорошо объясняет основы компьютерной графики (графический конвейер, камера, перспективная проекция, матрицы, шейдеры, освещение, текстуры) — эту книгу приятно и интересно читать (правда в ней используется OpenGL 3.3, и некоторые функции, используемые в книге, можно уже заменить на более новые из OpenGL 4). Книжка [Wolf] — это собрание практических примеров реализации той или иной техники создания визуальных эффектов (освещение, текстурирование, тени, blending и пр.), написанное очень понятным языком.

Надо заметить, что практически во всех книгах авторы пользуются вспомогательными библиотеками для работы с оконным интерфейсом операционной системы — чаще всего это библиотеки FreeGLUT или GLFW. Для векторных вычислений используют библиотеку GLM. Я же принципиально не использовал никакие библиотеки (библиотека GLEW не в счет — она лишь загружает функции OpenGL), поскольку хотел освоить буквально все аспекты программирования графических приложений с нуля. Поэтому в дальнейших заметках речь пойдет, помимо прочего, и о создании окон при помощи WinAPI, и о векторных вычислениях.

Создание проекта

Я создал проект в MS Visual Studio 2015, назвал его RenderingEngine. Проект размещен в открытом репозитории на сайте BitBucket (о том как клонировать репозиторий и прочее см. заметку про Git). В этой и последующих заметках я буду приводить фрагменты исходного кода, которые существенны для обсуждения. Привести весь исходный код, прокомментировав каждую строчку невозможно, да и смысла нет. Если вам понадобятся подробности кода, которых нет в заметках, загляните в репозиторий.

Прежде всего я хотел «организовать рабочее пространство» — разложить всё по папочкам. Почитал некоторые рекомендации в интернете (StackOverflow — VC2010 C++ — organizing source files) и решил создать четыре папки:

Название: «Применение OpenGL»
Раздел: Остальные рефераты
Тип: реферат Добавлен 04:56:22 25 февраля 2012 Похожие работы
Просмотров: 59 Комментариев: 11 Оценило: 0 человек Средний балл: 0 Оценка: неизвестно Скачать

Позднее я добавил еще две папки:

include для хранения заголовочных файлов сторонних библиотек
lib для хранения двоичных файлов сторонних библиотек
src для хранения моего собственного кода (как заголовочных файлов, так и файлов исходного кода)
build для хранения двоичного файла моей программы

Чтобы изменить выходную папку, в которую помещается построенный исполняемый файл программы (по-умолчанию он помещается в папку $(SolutionDir)\$(Configuration)), я должен был поменять некоторые свойства проекта (через меню Project->Properties в Visual Studio):

Project -> Properties -> General -> Output Directory
$(SolutionDir)\build\$(Configuration)\

Project -> Properties -> Debugging -> Working Directory
$(OutDir)

$(SolutionDir), $(Configuration), $(OutDir) и прочее — это так называемые макросы для команд и свойств построения. При построении проекта они подменяются своими значениям. Например, $(Configuration) заменяется на Debug или Release.

Также надо было добавить папку include в пути поиска заголовочных файлов

Пространства имен

Поскольку дальше я буду приводить в тексте исходный код, забегу вперед и расскажу, по каким пространствам имен я этот код распределил. Ниже приведена таблица, где в левом столбце — название пространства имен, в правом — описание.

shaders для хранения шейдеров
textures для хранения текстур
opengl Несмотря на то, что API OpenGL основан на языке C, он, тем не менее — объектно-ориентированный в том же смысле, в каком объектно-ориентированным является и WinAPI. Сплошь и рядом функции OpenGL создают какие-то объекты и возвращают их так называемые дескрипторы (или хэндлы) — числовые идентификаторы, при помощи которых программа может на эти объекты ссылаться. В общем, API OpenGL легко и естественно можно перевести в объектно-ориентированный сиплюсплюсный вид, что я и сделал, создав такие классы как Shader, Buffer, FramebufferObject, Renderbuffer, ProgramPipeline и другие и поместив их в пространство имен opengl.
win В это пространство имен я поместил все классы и функции, которые используют WinAPI и поэтому являются специфичными для ОС Windows. Например, классы Window, HighResolutionTimer, winapi_error и другие.
engine В это пространство имен я помещал классы, которые воспринимаются мной как «высокий уровень абстракции». Например классы Camera, Mesh, Model3D, Skybox, ShadowMap и другие. Эти классы зависят от классов в пространстве имен opengl.
util В это пространство имен я помещал классы и функции, которые выполняли роль полезных в хозяйстве инструментов. Типичный пример — функции из пространства имен util::Requires, которые например проверяют корректность аргументов, переданных в функцию и другие условия. Также в пространство имен util попали функции file::ReadAllText, file::getFileExtension, класс Event и пр.
vmath Сюда попали классы Vector и Matrix и всевозможные функции для работы с матрицами и векторами. Эти классы зависят от платформы Intel, поскольку используют Streaming SIMD Extensions (SSE), впрочем использование SSE можно отключить при помощи условной компиляции.

Замечу, что среди приведенных пространств имен только пространство win содержит платформозависимый код. Во всяком случае, я надеюсь, что это так. Приведенные выше пространства имен отражают модульность программы. Эти пространства не зависят друг от друга (за исключением engine, которое зависит от всех остальных пространств имен) и поэтому их можно разрабатывать независимо, а это — большой плюс для проекта.

Загрузка функций OpenGL

Итак, я хочу использовать в своей программе некий API на языке C. Обычно это значит вот что. API представляет собой набор функций. Прототипы (сигнатуры) этих функций находятся в некоем заголовочном файле (или файлах), а сами функции находятся в двоичном файле библиотеки (в ОС Windows такие файлы имеют расширение .lib или .dll). Также существует документация, в которой написано, что каждая функция делает и как вообще эти функции следует использовать.

В OpenGL это не совсем так. В каждой ОС существует библиотека, которая содержит ограниченный набор OpenGL’евских функций. В Windows это библиотека opengl32.dll, которая содержит набор функций, соответствующих устаревшей сто лет назад версии OpenGL 1.1. С тех пор огромное количество новых функций было добавлено в API, к тому же многие функции теперь объявлены устаревшими (т. е. ими буквально нельзя пользоваться). Предполагается загружать новые функции динамически, т. е. получать адреса функций во время выполнения программы. Для этого в OpenGL всегда был предусмотрен так называемый OpenGL extension mechanism — механизм, позволяющий производителям видеокарт добавлять в API новые функции, а пользователям (программистам) — эти функции вызывать. Но если эти функции не находятся в opengl32.dll, то где они тогда лежат? Ответ: в библиотеке, которая входит в состав ПО, которое поставляется вместе с вашей видеокартой. Например для видеокарт NVIDIA файл OpenGL’евской библиотеки может называться как-то так: nvogl32.dll. Однако программист, разумеется, не должен компоновать свою программу с библиотекой производителя видеокарты (в противном случае его программа была бы плохо переносимой), вместо этого программа запрашивает указатели на функции API во время выполнения. Каким образом это делается, подробно описано в статье Load OpenGL Functions. Скажу только, что в Windows для получения указателя на какую либо функцию OpenGL используется функция под названием wglGetProcAddress (заголовочный файл wingdi.h, библиотека opengl32.dll; все функции, начинающиеся с wgl, относятся к ОС Windows). Функция wglGetProcAddress принимает в качестве параметра имя искомой функции. Что происходит у wglGetProcAddress «под капотом» для меня — тайна, покрытая мраком. Загружает ли она в память приложения библиотеку от производителя видеокарты и если да, то как она ее находит? Почему для ее вызова необходимо создать контекст OpenGL (об этом ниже)? Впрочем это не столь важно, так как на самом деле рекомендуется перепоручать загрузку всех функций OpenGL специальным библиотекам, которые называются OpenGL Loading Libraries. Одной из таких библиотек является OpenGL Extension Wrangler Library (GLEW), которую я и использовал в своем проекте.

Библиотека OpenGL Extension Wrangler Library (GLEW)

Библиотека GLEW предназначена для загрузки функций API OpenGL. Что это значит? Предположим, вам надо вызвать какую-нибудь OpenGL’евскую функцию, например glAttachShader. Чтобы вызвать функцию, вам нужен ее адрес. Адрес функции можно получить при помощи wglGetProcAddress. Поскольку вы собираетесь вызывать функцию glAttachShader много раз в разных местах программы, ее адрес надо сохранить в некой глобальной переменной. Эта переменная должна иметь тип указателя на функцию, сигнатура которой совпадает с сигнатурой функции glAttachShader, описанной в спецификации OpenGL. И таких функций как glAttachShader — сотни. Для каждой из них в библиотеке GLEW имеется глобальная переменная, которая хранит адрес функции. А значения этим переменным присваиваются функцией glewInit. Ниже показан фрагмент заголовочного файла glew.h, в котором объявлены прототип функции glAttachShader и указатель на нее. Я снабдил этот фрагмент своими комментариями для большей понимабельности.

// The most important function in GLEW library which fills a bunch of global function pointers
// with the adresses of OpenGL functions.
GLEWAPI GLenum GLEWAPIENTRY glewInit ( void ) ;

// No idea what this is used for :)
#ifndef GLEW_GET_FUN
#define GLEW_GET_FUN(x) x
#endif

// Pointer-to-function type definition.
typedef void ( GLAPIENTRY * PFNGLATTACHSHADERPROC ) ( GLuint program, GLuint shader ) ;

// The pointer to function glAttachShader.
GLEW_FUN_EXPORT PFNGLATTACHSHADERPROC __glewAttachShader ;

// Macrodefinition which translates all references to glAttachShader to __glewAttachShader.
#define glAttachShader GLEW_GET_FUN(__glewAttachShader)

Итак, чтобы воспользоваться библиотекой GLEW, надо

    Скачать библиотеку GLEW с оф. сайта. Из скачанного архива мне нужны только четыре файла: glew.h, wglew.h (этот заголовочный файл нужен, если программа предназначается для ОС Windows), glew32.lib и glew32.dll, которые я распихиваю в папки своего проекта. glew.h, wglew.h — в папку \include\GL , glew32.lib — в папку \lib\GLEW , glew32.dll — в папки \build\Debug и \build\Release . В своем проекте я использую динамическую компоновку с библиотекой glew32.dll, однако можно использовать и статическую — тогда файл glew32.dll не нужен, а вместо файла glew32.lib надо взять файл glew32s.lib. Включение заголовочных файлов GLEW и подключение библиотек glew32.lib и opengl32.lib я поместил в файл GLEWHeaders.h:

// GLEWHeaders.h
#pragma once

// Link to opengl32.lib
#pragma comment(lib, «opengl32.lib»)

// Link to glew32.lib
#pragma comment(lib, «glew32.lib»)

// You can also statically link GLEW library as follows:
// #pragma comment(lib, «glew32s.lib»)
// #define GLEW_STATIC

#include «GL/glew.h»
#include «GL/wglew.h»

С вызовом glewInit связаны некоторые трудности. Оказывается, для того, чтобы вызвать glewInit, надо сначала создать так называемый OpenGL rendering context (далее — rendering context или контекст рисования). Функция wglGetProcAddress тоже не будет работать без созданного rendering context’а. Что же это такое — контекст рисования? Попробую дать определение.

  • Rendering context представляет собой некий объект, обладающий состоянием. Это состояние влияет на результат (или управляет результатом) работы функций OpenGL. В частности, состояние, в котором находится rendering context, определяет изображение, которое формируется на экране компьютера. Вызовы функций OpenGL могут изменять состояние rendering context’а. Пользовательская программа может взаимодействовать с rendering context’ом только при помощи функций OpenGL.
  • Могу предположить, что rendering context хранится в памяти драйвера видеокарты.
  • Пользовательская программа может создавать (функции wglCreateContext и wglCreateContextAttribsARB) и уничтожать (функция wglDeleteContext) rendering context. При создании контекста программа указывает, каким требованиям он должен удовлетворять (например, поддерживать определенную версию OpenGL).
  • Rendering context как правило существует в приложении в единственном экземпляре. Однако можно создать в одной программе несколько rendering context’ов. Если в программе есть несколько rendering context’ов, то в конкретный момент времени только один из них может быть активным (или текущим). Активный rendering context — это тот, который в данный момент влияет на результат работы функций OpenGL (т. е. например на отрисовку изображения на экране) и на состояние которого влияют вызовы функций OpenGL. Программа может переключаться между несколькими rendering context’ами (делать текущим то один, то другой контекст — см. функцию wglMakeCurrent). Понятие «активный контекст» принадлежит потоку управления, т. е. каждый поток управления может иметь свой активный контекст OpenGL. Заметим, что нежелательно, чтобы два разных потока управления имели одинаковый активный контекст OpenGL.
  • В Windows rendering context связан с так называемым контекстом устройства (device context — даже не хочу разбираться, что это такое, можете почитать статью Device Contexts) или проще говоря — с окном. Без окна нельзя создать контекст рисования. Соответственно, в этом окне и будет отрисовываться изображение, формируемое программой путем вызова различных функции OpenGL. При этом во время выполнения программы можно связывать любой контекст рисования с любым контекстом устройства. Можно даже связать один контекст рисования с несколькими контекстами устройств (но не наоборот) — см. функцию wglMakeCurrent. Тогда вы получите одно и то же изображение, отрисованное в нескольких окнах.
  • Без контекста рисования нельзя ничего нарисовать на экране компьютера.

Почему надо непременно создать контекст рисования, чтобы вызвать glewInit? Возможно это связано с тем, что в Windows значения указателей на функции, которые возвращает wglGetProcAddress, зависят от текущего контекста рисования… О’кей, давайте создадим этот контекст, тем более, что для рисования все равно нужен контекст рисования. Но оказывается, что контекст контексту рознь. По-настоящему работающий контекст рисования (при помощи которого можно что-то нарисовать) можно создать только при помощи функций wglChoosePixelFormatARB и wglCreateContextAttribsARB. Но адресов этих функций у нас нет, а чтобы их получить, нам надо либо вызвать glewInit либо вызвать wglGetProcAddress, но ни то, ни другое нельзя сделать без контекста рисования. Замкнутый круг? К счастью существует старинная функция для создания контекста рисования wglCreateContext, которая экспортируется непосредственно библиотекой opengl32.dll и поэтому ее адрес у нас есть изначально. Хотя такой контекст рисования будет поддерживать только версию OpenGL 1.1, и рисовать что-либо с его помощью нецелесообразно, он позволит нам вызвать glewInit. Затем мы его удалим и создадим новый нормальный контекст. Всю работу по инициализации GLEW я поместил в функцию Initialize_GLEW_Library в файлах GlewInitializer.h и GlewInitializer.cpp (см. листинг ниже).
Итак, алгоритм инициализации библиотеки GLEW следующий:

  • Создать окно. Окно нужно, так как без него нельзя создать контекст рисования. Кстати, созданное окно мы не отображаем на экране.
  • Создать контекст рисования при помощи функции wglCreateContext. Сделать его активным при помощи функции wglMakeCurrent.
  • Вызвать функцию glewInit.
  • Уничтожить окно. Для этого надо послать ему сообщение WM_DESTROY при помощи функции DestroyWindow. Это сообщение надо еще и обработать в цикле обработки сообщений, именно поэтому, несмотря на то, что окно мы нигде не отображаем, цикл обработки сообщений запускать все же приходится.
  • Уничтожить созданный ранее контекст рисования (поскольку он больше не нужен) при помощи функции wglDeleteContext.

// GlewInitializer.h
#pragma once

namespace opengl
<
// Initializes GLEW library
void Initialize_GLEW_Library ( ) ;
>

#include
#include «GlewInitializer.h»
#include «winapi_error.h»
#include «GlException.h»

namespace opengl
<
LRESULT CALLBACK FalseWndProc (
HWND hWnd,
UINT message,
WPARAM wParam,
LPARAM lParam )
<
switch ( message )
<
case WM_DESTROY :
<
PostQuitMessage ( 0 ) ;
break ;
>
default :
<
return DefWindowProc ( hWnd, message, wParam, lParam ) ;
>
>

HGLRC CreateFalseRenderingContext ( HDC hDC )
<
PIXELFORMATDESCRIPTOR pfd ;

// Choose a stub pixel format in order to get access to wgl functions.
:: SetPixelFormat (
hDC, // Device context.
1 , // Index that identifies the pixel format to set. The various pixel formats supported by a device context are identified by one-based indexes.
& pfd ) ; // [out] Pointer to a PIXELFORMATDESCRIPTOR structure that contains the logical pixel format specification.

// Create a fiction OpenGL rendering context.
HGLRC hGLRC = wglCreateContext ( hDC ) ;

// Throw exception if failed to create OpenGL rendering context.
if ( hGLRC == NULL )
throw win :: make_winapi_error ( «opengl::CreateFalseRenderingContext() -> wglCreateContext()» ) ;

// Make just created OpenGL rendering context current.
if ( ! wglMakeCurrent ( hDC, hGLRC ) )
throw win :: make_winapi_error ( «opengl::CreateFalseRenderingContext() -> wglMakeCurrent()» ) ;

void Initialize_GLEW_Library ( )
<
static bool s_IsInitialized = false ;

if ( s_IsInitialized )
return ;

const char * wnd >= «FalseWindow» ;
HINSTANCE hInst = GetModuleHandle ( NULL ) ; // get application handle

// Create a struct describing window class.
WND >;
wcex. cbSize = sizeof ( WND >) ; // struct size
wcex. style = CS_HREDRAW | CS_VREDRAW | CS_OWNDC ; // window style
wcex. lpfnWndProc = FalseWndProc ; // pointer to window function WndProc
wcex. cbClsExtra = 0 ; // shared memory
wcex. cbWndExtra = 0 ; // number of additional bytes
wcex. hInstance = hInst ; // current application’s handle
wcex. hIcon = LoadIcon ( hInst, MAKEINTRESOURCE ( >) ) ; // icon handle
wcex. hCursor = LoadCursor ( NULL , >) ; // cursor handle
wcex. hbrBackground = ( HBRUSH ) ( COLOR_MENU + 1 ) ; // background brush’s handle
wcex. lpszMenuName = NULL ; // pointer to a string — menu name
wcex. lpszClassName = wnd >; // pointer to a string — window class name
wcex. hIconSm = LoadIcon ( hInst, MAKEINTRESOURCE ( >) ) ; // small icon’s handle

// Create a FICTION window based on the previously registered window class.
HWND hWnd = CreateWindow ( wnd >// window class name
wnd >// window title
WS_OVERLAPPEDWINDOW, // window type
CW_USEDEFAULT, CW_USEDEFAULT, // window’s start position (x, y)
100 , // window’s width in pixels
100 , // window’s height in pixels
NULL , // parent window
NULL , // menu handle
hInst, // application handle
NULL ) ; // pointer to an object passed to the window with CREATESTRUCT struct (field lpCreateParams), pointer to which is contained in lParam parameter of WM_CREATE message

// Throw exception if CreateWindow() failed.
if ( ! hWnd )
throw win :: make_winapi_error ( «opengl::Initialize_GLEW_Library() -> CreateWindow()» ) ;

// Get device context for the window.
HDC hDC = GetDC ( hWnd ) ;

// Create a fiction rendering context.
HGLRC tempOpenGLContext = CreateFalseRenderingContext ( hDC ) ;

// Initialize GLEW (is possible only if an OpenGL rendering context is created).
if ( glewInit ( ) ! = GLEW_OK )
throw GlException ( «opengl::Initialize_GLEW_Library() -> glewInit()» ) ;

wglMakeCurrent ( NULL , NULL ) ; // remove the temporary context from being active
wglDeleteContext ( tempOpenGLContext ) ; // delete the temporary OpenGL context
>
>

В приведенном коде используются классы win::winapi_error и opengl::GlException — это всего-навсего классы исключений для ошибок, связанных соответственно с WinAPI и OpenGL, приводить их код здесь не буду.

На этом все. В следующей заметке я расскажу о том, как создать окно (рисовать-то мы будем именно в окне).

Opengl лекция №1: введение события сообщения контекст

OpenGL является на данный момент одним из самых популярных программных интерфейсов (API) для разработки приложений в области двумерной и трехмерной графики. Стандарт OpenGL был разработан и утвержден в 1992 году ведущими фирмами в области разработки программного обеспечения, а его основой стала библиотека IRIS GL, разработанная Silicon Graphics.

На данный момент реализация OpenGL включает в себя несколько библиотек (описание базовых функций OpenGL, GLU,GLUT,GLAUX и другие), назначение которых будет описано ниже.


Характерными особенностями OpenGL, которые обеспечили распространение и развитие этого графического стандарта, являются:

Дополнения и изменения в стандарте реализуются таким образом, чтобы сохранить совместимость с разработанным ранее программным обеспечением.

Надежность и переносимость

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

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

Основные возможности OpenGL

  • Набор базовых примитивов: точки, линии, многоугольники и т.п.
  • Видовые и координатные преобразования
  • Удаление невидимых линий и поверхностей (z-буфер)
  • Использование сплайнов для построения линий и поверхностей
  • Наложение текстуры и применение освещения
  • Добавление специальных эффектов: тумана, изменение прозрачности,сопряжение цветов (blending), устранение ступенчатости (anti-aliasing).

Как уже было сказано, существует реализация OpenGL для разных платформ, для чего было удобно разделить базовые функции графической системы и функции для отображения графической информации и взаимодействия с пользователем. Были созданы библиотеки для отображения информации с помощью оконной подсистемы для операционных систем Windows и Unix (WGL и GLX соответственно), а также библиотеки GLAUX и GLUT, которые используются для создания так называемых консольных приложений.

Библиотека GLAUX уступает по популярности написанной несколько позже библиотеке GLUT, хотя они предоставляют примерно одинаковые возможности. В дальнейшем в данном пособии в качестве основной будет рассматриваться библиотека GLUT, предоставляющая широкий набор средств взаимодействия с пользователем.

В состав библиотеки GLU вошла реализация более сложных функций, таких как набор популярных геометрических примитивов (куб, шар, цилиндр, диск), функции построения сплайнов, реализация дополнительных операций над матрицами и т.п. Все они реализованы через базовые функции OpenGL.

Архитектура и особенности синтаксиса

С точки зрения архитектуры, графическая система OpenGL является конвейером, состоящим из нескольких этапов обработки данных:

  • Аппроксимация кривых и поверхностей
  • Обработка вершин и сборка примитивов
  • Растеризация и обработка фрагментов
  • Операции над пикселами
  • Подготовка текстуры
  • Передача данных в буфер кадра

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

Для обеспечения интуитивно понятных названий в OpenGL полное имя команды имеет вид:

type glCommand_name[1 2 3 4][b s i f d ub us ui][v](type1 arg1,…,typeN argN)

Таким образом, имя состоит из нескольких частей:

gl это имя библиотеки, в которой описана эта функция:
для базовых функций OpenGL, функций из библиотек GLU, GLUT, GLAUX это gl, glu, glut, aux соответственно.

Command_name имя команды
[1 2 3 4]число аргументов команды
[b s i f d ub us ui ]тип аргумента:

символ b означает тип GLbyte (аналог char в С\С++),
символ f тип GLfloat (аналог float),
символ i– тип GLint(аналог int) и так далее.

Полный список типов и их описание можно посмотреть в файле gl.h

[v] наличие этого символа показывает, что в качестве параметров функции используется указатель на массив значений

Символы в квадратных скобках в некоторых названиях не используются. Например, команда glVertex2i() описана как базовая в библиотеке OpenGL, и использует в качестве параметров два целых числа, а команда glColor3fv() использует в качестве параметра указатель на массив из трех вещественных чисел.

Структура консольного приложения

Будем рассматривать построение консольного приложения при помощи библиотеки GLUT или GL Utility Toolkit, получившей в последнее время широкое распространение. Эта библиотека обеспечивает единый интерфейс для работы с окнами вне зависимости от платформы, поэтому описываемая ниже структура приложения остается неизменной для операционных систем Windows, Linux и многих других.

Функции GLUT могут быть классифицированы на несколько групп по своему назначению:

  • Инициализация
  • Начало обработки событий
  • Управление окнами
  • Управление меню
  • Регистрация вызываемых (callback) функций
  • Управление индексированной палитрой цветов
  • Отображение шрифтов
  • Отображение дополнительных геометрических фигур(тор, конус и др.)

Инициализация проводится с помощью функции glutInit(int *argcp, char **argv)

Переменная argcp есть указатель на стандартную переменную argc описываемую в функции main(), а argv– указатель на параметры, передаваемые программе при запуске, который описывается там же. Эта функция проводит необходимые начальные действия для построения окна приложения, и только несколько функций GLUT могут быть вызваны до нее. К ним относятся:

glutInitWindowPosition (int x, int y)
glutInitWindowSize (int width, int height)
glutInitDisplayMode (unsigned int mode)

Первые две функции задают соответственно положение и размер окна, а последняя функция определяет различные режимы отображения информации, которые могут совместно задаваться с использованием операции побитового “или”(|):

GLUT_RGBA Режим RGBA. Используется по умолчанию, если не указаны явно режимы GLUT_RGBA или GLUT_INDEX.
GLUT_RGB То же, что и GLUT_RGBA.
GLUT_INDEX Режим индексированных цветов (использование палитры). Отменяет GLUT_RGBA.
GLUT_SINGLE Окно с одиночным буфером. Используется по умолчанию.
GLUT_DOUBLE Окно с двойным буфером. Отменяет GLUT_SINGLE.
GLUT_DEPTH Окно с буфером глубины.

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

Двойной буфер обычно используют для анимации, сначала рисуя что-нибудь в одном буфере, а затем меняя их местами, что позволяет избежать мерцания. Буфер глубины или z-буфер используется для удаления невидимых линий и поверхностей.

Функции библиотеки GLUT реализуют так называемый событийно-управляемый механизм. Это означает, что есть некоторый внутренний цикл, который запускается после соответствующей инициализации и обрабатывает, одно за другим, все события, объявленные во время инициализации. К событиям относятся: щелчок мыши, закрытие окна, изменение свойств окна, передвижение курсора, нажатие клавиши, и «пустое» (idle) событие, когда ничего не происходит. Для проведения периодической проверки совершения того или иного события надо зарегистрировать функцию, которая будет его обрабатывать. Для этого используются функции вида:

void glutDisplayFunc (void (*func) (void))

void glutReshapeFunc (void (*func) (int width, int height))

void glutMouseFunc (void (*func) (int button, int state, int x, int y))

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

То есть параметром для них является имя соответствующей функции заданного типа. С помощью glutDisplayFunc() задается функция рисования для окна приложения, которая вызывается при необходимости создания или восстановления изображения. Для явного указания, что окно надо обновить, иногда удобно использовать функцию void glutPostRedisplay(void)

Через glutReshapeFunc() устанавливается функция обработки изменения размеров окна пользователем, которой передаются новые размеры.

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

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

Структура приложения, использующего анимацию, будет следующей:

#include
void MyIdle(void) <
//—Код, который меняет переменные, определяющие следующий кадр—//
.
>;
void MyDisplay(void) <
//—Код OpenGL, который отображает кадр —//
.
//— После рисования переставляем буфера —//
glutSwapBuffers();
>;
void main(int argcp, char **argv) <
//— Инициализация GLUT —//
glutInit(&argcp, argv);
glutInitWindowSize(640, 480);
glutInitWindowPosition(0, 0);
//—Открытие окна—//
glutCreateWindow(«My OpenGL Application»);
//— Выбор режима:Двойной буфер и RGBA цвета —//
glutInitDisplayMode(GLUT_RGBA | GLUT_DOUBLE | GLUT_DEPTH);
//— Регистрация вызываемых функций —//
glutDisplayFunc(MyDisplay);
glutIdleFunc(MyIdle);
//— Запуск механизма обработки событий —//
glutMainLoop();
>;

Этот шаблон используется в тексте приложения, использующего OpenGL, который приводится в конце этого пособия.

В случае, если приложение должно строить статичное изображение, можно заменить GLUT_DOUBLE на GLUT_SINGLE, так как одного буфера в этом случае будет достаточно, и убрать вызов функции glutIdleFunc().

Небольшое введение в win32 OpenGL

Я думаю, вы, как и я, смотрели на эти OpenGL‘ные демки, как двигаются по экрану полигоны, меняются различные эффекты и так далее. Также, вполне вероятно, вы не очень сильны в математике и не хотите самостоятельно выводить все эти математические синусоидальные процедуры. OpenGL — это классная библиотека, которая позволит вам создать 3D-вселенную очень быстро, двигать ее и наложить серию спецэффектов, используя простую концепцию API.

В наши дни есть два основных вида программирования под OpenGL, в зависимости от операционной системы, которую вы используете. Обычный путь состоит в использовании glut-библиотеки, чтобы задавать анимацию, которая совместима с linux, win32, sgx-станциями и т.д. Другой путь — эт чистый win32. В последнем случае используются обратный вызов win32-клинта, чтобы переключиться на следующее изображение. Как бы то ни было, результат одинаков. Мы будем рассматривать программирование под win32. Поэтому для использования OpenGL вам сначала нужно инициализровать процедуру окна.

1 Процедура окна

Процедура окна обычно обрабатывает все системные события, которые направляются текущему (отображаемому или нет) окну. Этот принцип очень хорошо работает для GUI и уменьшает количество потребляемых машиной ресурсов.

1.1 Класс окна

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

Как только вы зарегистрировали класс, вам необходимо создать свою процедуру окна

1.2 Создание окна

Это просто: один вызов функции API

Это правильно, но если вы вызовите его таким образом, приложение может повиснуть, почему? Потому что мы еще не создали процедуру окна.

1.3 Процедура окна

Процедура окна получает 4 аргумента. Первый — это хэндл окна. Второй — это сообщение. Третий — это нижний параметр, а четвертый — это верхний параметр.

Так как Windows работает в C-подобной среде, аргументы лежат на стеке. Поэтому к ним легко получить доступ. Также вам потребуется позаботиться о некоторых регистра, не модифицировать ebp, например, или ваша программа будет закрыта.

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

1.4 Обработка события

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

Как только ваша процедура окна инициализирована, вам требуется синхронизировать ее с OpenGL.

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

2.1 При создании окна

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

Если результат равен 0, вызов API не удавлся, и пользователь не может отобразить разрешение. В противном случае вы можете напрямую установить пиксельный формат.

тогда это равно пиксельному формату. Получить текущий DC очень просто:

и eax равен текущему DC. Нам также требуется создать контекст OpenGL из этого DC.

Если eax == 0, то вызов не удался и лучшее, что мы можем сделать — это послать WM_CLOSE, чтобы благополучно закрыть приложение.

Далее мы синхронизируем оба контекста:

Сделано, OpenGL был проинициализирован, теперь вы можете наложить ряд эффектов, но прежде, чем сделать это, вы должны из разрешить. Делается это так:

Вы также можете попробовать другие значения: GL_DEPTH, G_LIGHTING.

2.2 Как только был изменен размер окна

Когда Windows закончила с инициализацией, она шлет сообщение WM_SIZE, которое озанчает, что настало время пофиксить все, связанное с графикой. Разрешение экрана посылается в lParam. Поэтому сначала устанавливаем порт просмотра.

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

После этого мы вызываем функцию glLoadIndentity:

Затем мы вызываем библиотеку GL-эффектов, чтобы добавить перспективу порту просмотра. Эта функция принимает только числа двойной точности. Компилер может инициализировать такое число так:

А вот макрос, облегчающий загонку таких чисел в стек:

Теперь передадим необходимые параметры порту просмотра.

Перспектива — это необходимый аспект нашего 3D-мира. Перспектива бывает разных видов, например как при обзоре через камеру наблюдения или обзор сцены в формате 16/9.

И, опционально, вы можете установить модель теней для сложной 3D-сцены, установите ее в GL_F:AT, это может ускорить рендеринг изображения.

Вот и все с этим.

3 Пришло время показать ваши умения демокодера

Windows говорит процедуре окна, что настало время обновить текущее изображение с помощью сообщения WM_PAINT.

Первое, что нужно сделать — это обновить текущее изображение с помощью сообщения WM_PAINT.

Теперь мы можем установить порт просмотра в виде камеры. У этой камеры есть 9 операндов.

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

А теперь надо отрисовать окружение. Можно сделать много вещей: установить свет, тени, прозрачность, искажения и так далее.

Давайте сделаем такую простую вещь:

glColor3d и glVertex3i существуют во многих форматах. Последняя буква задает тип переменной, который вы хотите использовать. glVertex3d означается, что вы используете аргумент двойной точности.

Когда окружение построено, вы можете закончить это так:

И буфер окажется на экране.

4 Небольшое приложение

Часто люди удивляются, почему их продукция отображается на экране очень странно. Причина проста: OpenGL не интересуется другими элементами, расположенными на экране, поэтому н может перерисовать их. Вы можете установить тест глубины во время создания окна:

Некоторые люди хотят полноэкранное разрешение, это можно сделать в два шага, меняя разрешение дисплея:

Илон Маск рекомендует:  Php сервис определения минимально необходимой версии php для скриптов
Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL