Автор: Пользователь скрыл имя, 02 Ноября 2012 в 14:22, курсовая работа
Эта работа посвящена компьютерной графике, а именно тому, как использовать OpenGL в Delphi. OpenGL - это стандартная библиотека для всех 32-разрядных операционных систем, в том числе и для операционной системы Windows. OpenGL - не отдельная программа, а часть операционной системы
ВВЕДЕНИЕ__________________________________________________2стр.
1. Раздел 1 “Windows-приложения“_____________________5стр.
1.1. Событие, сообщение, ссылка ___________________________5стр.
1.2. Перехват сообщений_________________________________10стр.
2. Раздел 2 “Подключение OpenGL”_____________________17стр.
2.1. Минимальная программа OpenGL_____________________17стр.
2.2. Вывод на компоненты Delphi средствами OpenGL _______27стр.
Вывод_____________________________________________________41стр.
Список литературы ____________________________________42стр.
Прежде чем получить контекст воспроизведения,
сервер OpenGL должен получить детальные
характеристики используемого оборудования.
Эти характеристики хранятся в специальной
структуре, тип которой - TPlxelFormatDescriptor (описание
формата пикселя). Формат пикселя определяет
конфигурацию буфера цвета и вспомогательных
буферов. Наберите в тексте модуля фразу
"PixelFormatDescriptor", нажмите клавишу <Fl>,
и вы получите подробную информацию об
этом типе. Обращаю внимание: мы видим
раздел справки Microsoft, рассчитанной на
программистов, использующих С или C++,
поэтому описание содержит термины и стилистику
именно этих языков. По традиции Delphi имена
типов начинаются с префикса Т, но нам
не удастся найти помощь по термину TPixelFormatDescnptor.
К сожалению, это не единственное неудобство,
которое придется испытать. Например,
если мы заглянем в файл windows. pas и найдем
описание записи TPixeдFormatDescriptor, то обнаружим,
что в файле помощи не указаны некоторые
константы, имеющие отношение к этому
типу, а именно: PFD_SWAP_LAYER_BUFFERS,
PFD_GENERIC_ACCELERATED и PFD_DEPTH_DONTCARE.
А константа, названная PFD_DOUBLE__BUFFER_DONTCARE,
по-видимому, соответствует константе,
описанной в модуле windows. pas как PFD_DOUBLEBUFFER
DONTCARE.
Итак, смысл
структуры pixelFormatDescriptor - детальное описание
графической системы, на которой происходит
работа. Вас может озадачить дотошность
этого описания, но, уверяю, особое внимание
из всего этого описания требуют совсем
немногие вещи. В проекте я привел описание
всех полей структуры TPixelFormatDescriptcr на русском
языке (в момент их первоначального заполнения).
Делается это в процедуре setDCPixelFormat, вызываемой
между получением ссылки на контекст устройства
и созданием ссылки на контекст воспроизведения
OpenGL. Посмотрим подробнее, что там делается.
Полям структуры присваиваются желаемые
значения, затем вызовом функции choosePixelFormat
осуществляется запрос системе, поддерживается
ли на данном рабочем месте выбранный
формат пикселя, и, наконец, вызовом функции
SetPixelFormat устанавливается формат пикселя
в контексте устройства.
Функция
choosePixeiFormat возвращает индекс формата пикселя,
который нам нужен в качестве аргумента
функции SetPixeiFormat. Заполнив поля структуры
TPixelFormatDescriptor, мы определяемся со своими
пожеланиями к графической системе, на
которой будет происходить работа приложения,
OpenGL подбирает наиболее подходящий к нашим
пожеланиям формат и устанавливает уже
его в качестве формата пиксела для последующей
работы. Наши пожелания корректируются
сервером OpenGL применительно к реальным
характеристикам системы. To, что OpenGL не
позволит нам установить нереальный для
конкретного рабочего места формат пикселя,
значительно облегчает нашу задачу. Предполагая,
что разработанное приложение будет работать
на машинах разного класса, можно запросить
"всего побольше", а уж OpenGL разберется
в каждом конкретном случае, каковы параметры
и возможности оборудования, на котором
в данный момент выполняется приложение.
На этом можно было бы и закончить разговор
о формате пиксела, если бы мы могли полностью
довериться выбору OpenGL. Обратим внимание
на поле структуры "битовые флаги",
dwFlags. To, как мы зададим значение флагов,
может существенно сказаться на работе
нашего приложения, и наобум задавать
эти значения не стоит. Тем более что некоторые
флаги совместно "не уживаются", а
некоторые присутствуют только в паре
с определенными флагами. В рассматриваемом
примере я присвоил флагам значение PFD_DRAW_TO
WINDOW or PFD_SUPPORT_OPENGL, сообщив тем самым системе,
что собираюсь осуществлять вывод в окно
и что моя система в принципе поддерживает
OpenGL (рис. 1. 1).
Рис. 1. 1. Поумолчанию режим двойной буферизации не установлен
Я ограничился всего двумя константами из обширного списка, приведенного в модуле 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, так что установить такое значение для величины типа Byte не удастся.
OpenGL позволяет узнать, какой же формат пикселя он собирается использовать. Для этого необходимо использовать функцию DescribePixelFormat, заполняющую величину типа TPixelFormatDescriptor установленным форматом пикселя. Построим несложное приложение на основе использования этой функции, которое позволит детальнее разобраться с форматом пикселя и подобрать формат для конкретного рабочего места. В примере с битовым флагам задаем все возможные значения одновременно, числовым полям задаем заведомо нереальное значение 64, и смотрим на выбор формата пикселя, сделанный OpenGL. Результат, который вы получите для выбранного OpenGL формата пикселя, я предсказать не могу: он индивидуален для каждой конкретной конфигурации компьютера и текущих настроек. Скорее всего, окажется, что режим двойной буферизации не будет установлен. (Напоминаю: многие флаги устанавливаются только в определенных комбинациях с другими.) Наше приложение позволяет менять параметры формата пикселя и устанавливать его заново, а чтобы видеть воспроизведение, небольшая площадка на экране при каждом тестировании окрашивается случайным цветом, используя функции OpenGL. Поэкспериментируйте с этим приложением, например, определите комбинацию флагов для установления режима двойной буферизации. Посмотрите значение числовых полей формата при различной палитре экрана: 16 бит, 24 бита, 32 бита, если у вас есть такой режим, но не в палитре с 256 цветами. О выводе OpenGL при палитре экрана в 256 цветов у нас будет отдельный разговор. Это приложение, в частности, дает ответ на вопрос, как определить, оснащен ли компьютер графическим акселератором. Сделать это можно после вызова функции DescribePixelFormat следующим образом:
var
i, j: Integer;
i: = pfd. dwFlags and PFD_GENERIC_ACCELERATED; ]: = pfd.
dwFlags and PFD_GENERIC__FORMAT;
If (i = 0) and (з = 0)
then // полноценньм ICD-драйвер с функциями ускорения
else If (i = 1) and (j = 1)
then // MCD-драйвер, аппаратно реализуется
// только часть функций ускорения else // режим программной эмуляции, всю работу выполняет центральный // процессор
В следующей главе мы узнаем еще один способ определения наличия акселератора. С помощью рассмотренного проекта вы найдете ответ на вопрос, на который я вам ответить не смогу, а именно - как заполнить структуру TPixelFormatDescriptor для вашего компьютера.
Решение проблем
Если значение соответствующей переменной равно нулю, вывод OpenGL оказывается невозможным:
If hrc=0 then ShowMessage('OTcyTCTByeT контекст воспроизведения OpenGL');
Обратите также внимание на следующую важную вещь. В самой первой программе, использующей OpenGL, как и в подавляющем большинстве последующих примеров, процедура установки формата пикселя записана мною в самом коротком варианте:
FillChar {pfd, SizeOf (pfd), 0);
nPixelFormat: =ChoosePixelFormat (hdc, @pfd);
SetPixelFormat (hdc, nPixelFormat, @pfd);
To есть ни одно из полей pfd я не задаю явно, отдавая все на откуп OpenGL. B некоторых же случаях я офаничиваюсь только заданием необходимых значений для полей битовых флагов. Я не встречал ситуаций, когда бы такой подход не срабатывал, не считая случаев с использованием других, нежели фирмы Microsoft, версий OpenGL, но поручиться за то, что он будет работать для всех графических карт, не могу. Возможно, проблемы возникнут также из-за некорректной работы Драйверов (стандартная отговорка, не правда ли?). Если примеры с прилагаемой дискеты у вас не работают, выдавая просто черный экран, начните поиск причины с определения значения hrc сразу же после создания ссылки на контекст воспроизведения. Если это значение Равно нулю, в процедуре установки формата пикселя задайте всем полям значения согласно полученным с помощью приложения проекта TestPFD.Скорее всего, вам рано или поздно потребуется разрешать подобные проблемы, связанные с неверным форматом пикселя или подобными системными ошибками. Сделать это в проектах, где не используется библиотека классов Delphi, оказывается сложным для новичков.
Полный список системных ошибок, связанных с использованием OpenGL, можно посмотреть в файле windows. pas, в разделе "OpenGL Error Code". Учтите, что в этом примере выводится информация о последней системной ошибке, а она могла произойти задолго до работы приложения, так что следует использовать такую диагностику ошибок только при отладке приложений. Первый аргумент функции API FormatMessage позволяет определять дополнительные детали вывода сообщения.
2.2. Вывод на компоненты Delphi средствами OpenGL
Теоретически с помощью функций OpenGL можно осуществлять вывод не только на поверхность формы, но и на поверхность любого компонента, если у него имеется свойство Canvas. Handle, для чего при получении ссылки на контекст воспроизведения необходимо указывать ссылку на контекст устройства, ассоциированную с нужным компонентом, например, image1. Canvas. Handie. Однако чаще всего это приводит к неустойчивой работе, вывод то есть, то нет, хотя контекст воспроизведения присутствует и не теряется. OpenGL прекрасно уживается с визуальными компонентами, как видно из примера TestPFD, так что чаще всего нет необходимости осуществлять вывод на поле не формы, а компонента Delphi. Если для ваших задач необходимо ограничить размер области вывода, то для этого есть стандартные методы, которые мы обсудим во второй главе. Например, дан проект, в котором вывод осуществляется на поверхность панели - компонента, вообще не имеющего свойства canvas. Для этого мы пользуемся тем, что панель имеет отдельной окно:
dc: = GetDC (Panell. Handle);
SetDCPixelFormat(dc);
hrc: = wglCreateContext(dc);
Аналогичным образом можно организовать вывод на поверхность любого компонента, имеющего свойство Handle (т.e. имеющего самостоятельное окно), например, на поверхность обычной кнопки. Обязательно попробуйте сделать это для вывода на компонент класса TImage можете записать dc: = Imagel. Canvas. Handle; и удалить строки BeginPaint и EndPaint, поскольку класс TImage не имеет свойства Handie, т. e. не создает отдельного окна. Однако вывод на компоненты, подобные компонентам класса Timage, т.e. не имеющие свойства Handie, отличается полной неустойчивостью, так что я не гарантирую вам надежного положительного результата. Почему это происходит, выясним в следующем разделе.
Стили окна и вывод OpenGL
В следующем применре я немного модифицировал пример минимальной программы OpenGL таким образом, что получилось простое MDI-приложение, в котором каждое дочернее окно окрашивается случайным образом с использованием команд OpenGL (рис. 1. 2).
Рис. 1. 2. Приложение для вывода командами OpenGL может иметь сколько угодно окон
Следующий проект отличается от предыдущего только тем, что получается SDI-приложение (рис. 1. 3).
Рис. 1. 3. Для вывода командами OpenGL можно использовать любой интерфейс приложений
Из этих примеров видно, что приложение может иметь сколько угодно контекстов воспроизведения. Однако следует иметь в виду, что каждое окно может иметь только один контекст воспроизведения, чем, в частности, и объясняется неустойчивый вывод на компоненты, не имеющих собственного окна. В справке по функции SetPixelFormat говорится о том, что вывод средствами OpenGL не может быть осуществлен на окно совершенно произвольного стиля, класс окна должен включать стили "ws_CLiPCHiLDREN and ws_CLiPSiBLiNGs". Это соответствует обычным окнам Delphi - и родительским, и дочерним. Также говорится и о том, что атрибуты класса окна не могут включать стиль "cs_PARENTDc", что является полным ответом на вопрос о неустойчивости вывода средствами OpenGL на поверхность компонентов ТИпа класса TImage. Я не встречал ситуации, когда бы подобные проекты с выводом на окно без рамки или MDI-приложения с выводом средствами OpenGL на дочерние окна работали некорректно или неустойчиво, независимо от текущих установок или используемого акселератора, если свойство windowstate имеет значение wsNormal. Более вероятно возникновение проблем в случае полноэкранных приложений.
Полноэкранные приложения
Такие приложения достойны отдельного разговора в силу их особой значимости. Занять всю область экрана вам может потребоваться для того, чтобы повысить эффектность и зрелищность вашего проекта, особенно если на экране присутствует много объектов. Прежде всего, необходимо сказать о том, что некоторые графические акселераторы поддерживают ускорение только в полноэкранном режиме и при определенных установках экрана, например, только при разрешении экрана 640x480 и цветовой палитре 16 бит на пиксель. К сожалению, мне придется сообщить о наличии здесь некоторых проблем, впрочем, мы их успешно разрешим. Вернитесь к последнему примеру проекту, где вывод средствами OpenGL осуществляется на окно без рамки и области заголовка. Поменяйте свойство windowstate формы на wsMaximized, чтобы окно после запуска раскрывалось на весь экран. Запустите проект или откомпилированный модуль. Что у вас получится, я предсказать не могу, он зависит от конкретной конфигурации машины. Если на вашем компьютере все происходит в ожидаемом режиме, т.e. весь экран занят окном голубоватого цвета, вы избавлены от множества проблем, если только не собираетесь распространять свои приложения. Дело в том, что я тестировал подобные проекты на компьютерах самой различной конфигурации и обнаружил, что чаще всего результат получается обескураживающий и неприятный: окно раскрывается некорректно, не полностью занимая экран. Причем неприятности появляются именно в связи c использованием OpenGL, простые проекты вполне справляются с задачей раскрывания на весь экран. Возможно, корень проблемы в несогласованности работы драйверов (как всегда!) или ошибках операционной системы. Однако решение проблемы оказывается совсем простым.
Рассмотрим пример. Это тот же пример, что и предыдущий, только немного измененный. Свойство windowstate окна формы Установлено в wsNormai, а обработчик события onCreate дополнился строкой:
windowState: = wsMaximized;
Теперь все работает превосходно, окно действительно раскрывается на весь экран свойство FormStyle окна можно задать как fsstayOnTop, и тогда приложение будет вести себя так, как многие профессиональные игры, не позволяя переключиться на другие приложения. Другое решение проблемы очень похоже на предыдущее. Рассмотрим пример - модифицированный пример вывода на поверхность панели. Панель занимает всю клиентскую область окна (свойство Align имеет значение alclient), а окно формы максимизировано и не имеет рамки. Надеюсь, у вас теперь не будет неприятностей при попытке захватить всю область экрана, хотя нелегко объяснить, почему обычный метод не работает, а такой метод, по сути, ничем от него не отличающийся, работает. Итак, полный экран отвоевывать мы научились. Теперь необходимо научиться менять программно, т.e. без перезагрузки, разрешение экрана: приложению может потребоваться другое, нежели установленное пользователем разрешение, к которому он привык в своей повседневной работе. Проект FullScr является упрощенным вариантом такой программы. После запуска приложения пользователю из списка предлагается выбрать желаемое разрешение экрана, которое устанавливается на время работы основного модуля - минимальной программы OpenGL. После окончания работы модуля возвращается первоначальное разрешение экрана. Пример построен на использовании функции API ChangeDisplaySettings, первый аргумент которой - структура, описывающая требуемый режим. Второй аргумент - битовая комбинация констант, одна из которых задает тестирование режима без его установки. Массив LowResModes заполняем перечислением всех возможных режимов, тестируем последовательно каждый из них и отмечаем те, тестирование для которых оказалось успешным. После пользовательского выбора действительно устанавливаем выбранный режим, а по завершению работы приложения возвращаем запомненный первоначальный режим. Протестировав программу в различных режимах, вы можете выяснить, что не во всех из них возможно использование OpenGL, в некоторых режимах контекст воспроизведения не может быть получен. В целом такой способ создания полноэкранного приложения я нахожу вполне удовлетворительным. При тестировании на компьютере без акселератора приложение в проблемных режимах выдавало сообщение о невозможности получения контекста, а при подключении акселераторов сообщение не появлялось, но в некоторых режимах окно и не окрашивалось. Акселераторы первых моделей могут искаженно передавать картинку в некоторых режимах. Приведу еще один пример на полноэкранный режим работы (проект из подкаталога Ex31). Здесь для переключения в различные режимы используется DirectDraw, все необходимые модули для его использования находятся также в этом подкаталоге. Параметры режима - ширина, высота и разрядность пикселя - задаются в виде трех чисел в командной строке. С помощью этого приложения вы можете выяснить, что на самом деле не все режимы доступны для использования в принципе, чем и объясняется то, что в предыдущем примере не во всех режимах можно было получить контекст воспроизведения. В проблематичных режимах на экране хорошо заметно искажение изображения. Я думаю, что описанный способ создания полноэкранного приложения вполне можно считать универсальным и достаточно надежным. Есть еще один, очень простой, способ создания полноэкранного приложения: рисование прямо на поверхности рабочего стола. Во второй главе я приведу соответствующий пример, хотя вы уже сейчас знаете, что для этого необходимо сделать. Но этот способ может не работать с вашей картой.