Opengl и delphi на практике


Opengl и delphi на практике

На кого рассчитана статья

Я рассчитываю на то, что вы знакомы с азами создания приложений в С++Builder или Delphi и совсем не знаете OpenGL.

Введение

OpenGL (Open Graphics Library) — популярная библиотека для работы с 3D графикой. Стандарт OpenGL появился в 1992 году благодаря компании Silicon Graphics и сейчас переживает годы своего самого бурного развития.

Чуть-чуть побольше узнать об OpenGL и о том, как с ним работать в VC, можно, почитав wat’а:

Я хочу показать, как работать с этой библиотекой в таких популярных и, на мой взгляд, очень удобных средах разработки, как Delphi и С++Builder.

Эта — первая — статья посвящена в основном инициализации OpenGL.

Инициализация

Первым делом нужно подключить заголовочные файлы:

С++

Delphi

Если вы используете Delphi, то всё необходимое для работы с OpenGL находится в модуле OpenGL.dcu. А если вы используете С++Builder, то подключать придётся несколько файлов:

delphi opengl программирование

  • gl.h и glu.h содержат прототипы основных функций OpenGL определённых в opengl32.dll и glu32.dll.
  • glaux.h содержит вспомогательные (auxiliary) функции (glaux.dll). В этой статье я не буду использовать glaux.h, т.к. его функции не доступны в Delphi, да и не люблю я эту библиотеку. Кроме того основные задачи glaux (как, в прочем, и аналогичной, но более качественной, библиотеки GLUT) — это создание окон, таймеров, обработка клавиатуры и мыши, всё это есть в ИСР (Интегральная Среда Разработки) Delphi или С++Builder.

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

С++

Delphi

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

cColorBits — глубина цвета

cDepthBits — размер буфера глубины (Z-Buffer)

cStencilBits — размер буфера трафарета (мы его пока не используем)

iPixelType — формат указания цвета. Может принимать значения PFD_TYPE_RGBA (цвет указывается четырьмя параметрами RGBA — красный, зленный, синий и альфа) и PFD_TYPE_COLORINDEX (цвет указывается индексом в палитре). Как вы видите, я использую RGBA, и вам придётся поступить также, т.к. если вы захотите использовать COLORINDEX, то вам придётся изменить мою функцию: добавить пару флагов и дать начальные значения ещё нескольким переменным.

Более подробную информацию смотрите в справочнике или в MSDN.

Функция ChoosePixelFormat подбирает формат пикселей, максимально удовлетворяющий нашим требованиям, и возвращает его дескриптор, а SetPixelFormat устанавливает его в контексте устройства (dc).

После того как в контексте устройства установлен формат пикселей, нужно создать контекст воспроизведения (Rendering Context) для этого в OpenGL определены следующие функции:

С++

Delphi

Наверное, объяснять их значение не стоит

Теперь перейдём к форме. В объявлении класса формы в области private добавьте следующее:

С++

Delphi

ghRC — указатель на контекст воспроизведения (Rendering Context)

ghDC — дескриптор устройства (для нас — просто указатель на окно)

Процедура Draw будет отвечать за рисование.

Далее заполняем FormCreate:

С++

Delphi

Вы видите, что тут вызывается FromResize, который мы ещё не описали. Надо это исправить. Поместите туда следующий код:delphi opengl программирование

С++, Delphi

Теперь, наверное, надо кое-что объяснить.

glClearColor устанавливает цвет (в нашем случае чёрный), которым будет заполняться экран при очищении. У этой процедуры — 4 параметра, что соответствует RGBA. Вместо нее можно написать glClearIndex(0.0). Эта процедура устанавливает индекс цвета в палитре.

glViewport устанавливает область вывода — область, в которую OpenGL будет выводить изображение. В нашем случае — вся форма.

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

glLoadIdentity заменяет текущую матрицу видового преобразования на единичную (матрицу идентичности), т.е. просто сбрасывает ваши изменения.

glOrtho устанавливает режим ортогонального (прямоугольного) проецирования. Это значит, что изображение будет рисоваться как в изометрии. 6 параметров типа GLdouble (или просто double): left, right, bottom, top, near, far определяют координаты соответственно левой, правой, нижней, верхней, ближней и дальней плоскостей отсечения, т.е. всё, что окажется за этими пределами, рисоваться не будет. На самом деле эта процедура просто устанавливает масштабы координатных осей. Для того чтобы установить перспективное проецирование, используются процедуры glFrustum и gluPerspective, но о них — потом.

gluLookAt устанавливает параметры камеры: первая тройка — её координаты, вторая — вектор направления, третья — направление оси Y.

В OpenGL всё включается и выключается (разрешается и запрещается) процедурами glEnable и glDisable. Таким образом, мы разрешили тест глубины (GL_DEPTH_TEST), чтобы изображение было объёмным, разрешили давать нашим объектам какой-то цвет (GL_COLOR_MATERIAL), разрешили освещение (GL_LIGHTING) и включили (GL_LIGHT0).

glLightfv устанавливает свойства : позицию и направление света.

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

С++

Delphi

А теперь, давайте уже что-нибудь нарисуем!

Пример

Давайте нарисуем сферу, а потом заставим её крутиться. Итак, всё, что нам понадобится — это форма и таймер.

Установите интервал таймера на 10 миллисекунд (нам этого будет вполне достаточно). Теперь скопируйте все представленные выше фрагменты кода в соответствующие места. В процедуре Timer1Timer напишите одну сточку: Draw(); (в Delphi без скобок).

Теперь нам осталось только что-нибудь нарисовать, т.е. отредактировать функцию Draw.

С++

Delphi


Всё, можно нажимать F9.

А теперь кое-что поясню (в процедуре Draw не встретилось ни одной знакомой строчки).

glClear сбрасывает значения всего перечисленного в качестве параметров (в нашем случае очищает буфер цвета и буфер глубины). Этой процедуре передавать много всяких буферов для очистки, но лично я использую только 3: GL_DEPTH_BUFFER_BIT, GL_COLOR_BUFFER_BIT, иногда GL_STENCIL_BUFFER_BIT (буфер трафарета).

glColor устанавливает цвет фигуры. Существует следующий синтаксис как для glColor, так и для других функций OpenGL:

Поясняю, каждая функция OpenGL начинается с префикса . Далее следует название функции. После названия — количество параметров (если функция определена для разного кол-ва параметров). И, наконец, переменными какого типа являются параметры:

  • b — GLbyte байт
  • s — GLshort короткое целое
  • i — GLint целое
  • f — GLfloat дробное
  • d — GLdouble дробное с двойной точностью
  • ub — GLubyte беззнаковый байт
  • us — GLushort беззнаковое короткое целое
  • ui — GLuint беззнаковое целое
  • v — вектор — массив из n элементов указанного типа

Итак, glColor3f означает, что цвет задаётся тремя компонентами типа GLfloat.

Для рисования сферы мы используем механизм из glu32.dll. Создаём объект типа GLUquadricObj и инициализируем его функцией gluNewQuadric. Далее устанавливаем стиль фигуры функцией gluQuadricDrawStyle (quadObj, GLU_FILL). Стиль может быть GLU_FILL, GLU_LINE или GLU_POINT. Что каждый из них значит, проверьте сами.

gluSphere — делает из quadObj сферу. Три последних параметра — это радиус и количество разбиений поперёк и вдоль оси Z соответственно. Я взял маленькое число разбиений, чтобы было видно, что сфера крутится.

И не забудем освободить память, занимаемую под quadObj — gluDeleteQuadric(quadObj).

glRotatef — заставляет нашу сферу крутиться. О том, как это делается — в следующей статье.

И, наконец, SwapBuffers (ghDC) выводит всё на экран.

программирование delphi opengl

Подключение библиотеки OpenGL в Delphi

procedure MesDblClick (var MyMessage TWMMouse); message wm LButtonDblClk;

Замечание
Как правило, перехватчики сообщений для повышения надежности работы приложения описываются в блоке protected

Имя процедуры я задал таким, чтобы не появлялось предупреждение компилятора о том, что я перекрываю соответствующее событие формы. Служебное слово message указывает на то, что процедура будет перехватывать сообщение, мнемонику которого указывают за этим словом. Тип аргумента процедуры-перехватчика индивидуален для каждого сообщения. Имя аргумента произвольно, но, конечно, нельзя брать в качестве имени служебное СЛОВО message. Пожалуй, самым сложным в процессе описания перехвата сообщений является определение типа аргумента процедуры, здесь оперативная помощь оказывается малополезной. В четвертой и пятой версиях Delphi инспектор кода облегчает задачу, но незначительно. Чтобы решить эту задачу для сообщения wm_LButtonDblclk, я просмотрел все вхождения фразы «LButtonDblClk» в файле messages. pas и обнаружил строку, подсказавшую решение:

В этом же файле я нашел описание структуры Twmouse, чем и воспользовался при кодировании процедуры MesDblclick для получения координат курсора. Обратите внимание, что здесь не пришлось самостоятельно разбивать, по словам значение параметра, как в предыдущем проекте. Итак, в рассматриваемом примере перехватывается сообщение «двойной щелчок левой кнопки мыши». Событие Dblclick формы наступает точно в такой же ситуации. Выясним, какая из двух процедур, перехватчик сообщения или обработчик события, имеет преимущество или же они равноправны. Создайте обработчик события OnDblclick формы — вывод любого тестового сообщения. Запустите проект, дважды щелкните на форме. Процедура-перехватчик среагирует первой и единственной, до обработчика события очередь не дойдет.

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

protected
procedure WMPaint(var Msg: TWMPaint); message WM_PAINT; .

procedure TForml. WMPaint(var Msg: TWMPaint);

var ps: TPaintStruct;

begin BeginPaint(Handle, ps);

Rectangle (Canvas. Handle, 10, 10, 100, 100);

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

Работа с таймером

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

const
AppName = ‘WinPaint’;

Идентифицировать таймер необходимо потому, что у приложения их может быть несколько. Для включения таймера (то, что в привычном антураже соответствует Timerl. Enabled: = True) вызывается функция API SetTimer, где задается требуемый интервал таймера:

SetTimer (Window, id_Timer, 200, nil); // установка таймера

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

wm_Timer: InvalidateRect (Window, nil, False);

Как видим, работать с таймером, используя только функции API, совсем не сложно. Компонент Delphi TTimer основывается на функциях и сообщениях, которые мы только что рассмотрели.

Работа с мышью и клавиатурой

При нажатой левой кнопки мыши за указателем остается след. Оконная функция дополнилась обработчиками сообщений wm_LButtonDown, wm_LButtonUp И wm_MouseMove. Для определения координат курсора пользуемся тем, что поле iParam подобных сообщений содержит эти самые координаты.

wm_Create: Down: = False;

begin If Down then begin xpos: = LoWord ( LParam);

ypos: = HiWord ( LParam); InvalidateRect(Window, nil, False); end; end;

wm_Paint: begin If Down then begin dc: = BeginPaint (Window, MyPaint);

Ellipse (dc, xPos, yPos, xPos + 2, yPos + 2);

EndPaint (Window, MyPaint); ReleaseDC (Window, dc);

end;
Последнее, что мы рассмотрим в данном разделе и что обязательно потребуется в дальнейшем — это обработка клавиатуры. Оконная функция дополнилась обработчиком соответствующего сообщения

wm_Char: // анализ нажатой клавиши

case wParam of $58, $78: If HiWord

MessageBox(Window, ‘X’, ‘Нажата клавиша’, MB_OK) else

MessageBox(Window, ‘X вместе с Shift’, ‘Нажата клавиша’, MB_OK);

При нажатии клавиши ‘X’ выводится сообщение, в котором указано, нажата ли одновременно клавиша . Я использовал шестнадцатеричное представление кода клавиши, но, конечно, можно использовать и десятичное. Надеюсь, здесь не требуются особые пояснения, и мы сможем использовать этот код в качестве шаблона в будущих проектах.

Файлы DLL (Dynamic Link Library, библиотека динамической компоновки) являются основой программной архитектуры Windows и отличаются от исполняемых файлов фактически только заголовком

Для загрузки операционной системы необходимо запустить файл win com, имеющий размер всего 25 Кбайт. Как легко догадаться, в файл такого размера невозможно поместить код, реализующий всю ту гигантскую работу, которая производится по ходу выполнения любого приложения. Этот файл является загрузчиком ядра операционной системы, физически размещенным в нескольких DLL-файлах. Помимо кода, DLL-файлы могут хранить данные и ресурсы. Например, при изменении значка (ярлыка) пользователю предлагается на выбор набор значков из файла SHELL32. DLL. Библиотека OpenGL физически также размещена в виде двух DLL-файлов: opengl23. dll и glu32. dll. Первый из этих файлов и есть собственно библиотека OpenGL. Назначение его — осуществление взаимодействия с акселератором или программная эмуляция ускорителя за счет центрального процессора. Поддержка ЗD-акселерации осуществляется с помощью полного (устанавливаемого) клиентского драйвера (Installable Client Driver, ICD) и мини-драйвера (Mini-Client Driver, MCD). Библиотека OpenGL реализована по клиент-серверной схеме, т.e. ее одновременно может использовать несколько приложений при единственной копии сервера в памяти или вообще при удаленном расположении сервера (сервер в принципе может располагаться и не на компьютере клиента). Чаще всего DLL представляет собой набор функций и процедур. Как говорится в справке Delphi по DLL, «динамические библиотеки являются идеалом для многоязыковых проектов». Это действительно так: при использовании OpenGL совершенно безразлично, в какой среде созданы сама библиотека и вызывающие ее модули.

Раздел 2 “Подключение OpenGL”

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

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

hrc: HGLRC; // ссылка на контекст воспроизведения

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

SetDCPixelFormat(Canvas. Handle); //задаем формат пиксела

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

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

procedure SetDCPixelFormat (hdc: HDC);


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

nPixelFormat: = ChoosePixelFormat (hdc, @pfd);

SetPixelFormat (hdc, nPixelFormat, @pfd);

По поводу формата пиксела мы подробнее поговорим в следующем разделе. Во второй строке обработчика OnCreate задается величина типа HGLRC, т.e. создается контекст воспроизведения. Аргументом функции wglCreateContext является ссылка на контекст устройства, на который будет осуществляться вывод. Сейчас устройством вывода служит окно формы. Для получения этого контекста OpenGL необходима величина типа HDC. Здесь, как и во многих других примерах, мы используем факт, что canvas. Handle и есть ссылка на контекст устройства, связанная с окном формы. Поскольку это первая функция, имеющая непосредственно отношение к OpenGL, то я немного отвлекусь на некоторые общие пояснения. Только начиная с пятой версии Delphi поставляется с системой помощи, удовлетворительно настроенной с точки зрения получения справок по командам OpenGL и функциям API, в предыдущих версиях ее вроде, как и нет. Однако на самом деле такая помощь доступна и в ранних версиях, и самый простой способ ее получения — контекстная подсказка. По командам OpenGL справки мы будем получать точно так же, как и по функциям API, т. e если вам необходима более подробная информация, например, о функции wglcreateContext, то установите курсор на строку с этой функцией и нажмите клавишу . Функция wgicreateContext физически размещается в файле opengl32 dll, а прототип ее находится в файле windows. pas. В этот файл также помещены прототипы всех функций и процедур, имеющих отношение к реализации OpenGL под Windows, a прототипы собственно команд OpenGL расположены в файле opengl pas функции и процедуры, имеющие отношение только к Windows-версии OpenGL, обычно имеют приставку wgl, как, например, wglcreateContext, но могут и не иметь такой приставки, как, например, SwapBuffers. Собственно команды OpenGL имеют приставки gl или glu в зависимости от размещения в библиотеках opengl32. dll и glu32. dll, соответственно. Итак, контекст воспроизведения создан, и теперь можно осуществлять вывод командами OpenGL. Обработка события onpaint выглядит следующим образом:

wglMakeCurrent (Canvas. Handle, hrc); // установить контекст

glClearColor (0. 5, 0. 5, 0. 75, 1. 0); // цвет фона

glClear (GL_COLOR_BUFFER_BIT); // очистка буфера цвета

wglMakeCurrent (0, 0); // освободить контекст

Илон Маск рекомендует:  Движки сайтов. CMS

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

Замечание
Согласно справке, для освобождения контекста воспроизведения оба параметра должны быть установлены в NULL, но хотя компилятор и пропустит такие значения, во время выполнения получим ошибку «Invalid variant type conversion», так что будем всегда для освобождения контекста задавать эти значения нулевыми. Обработка события onDestroy формы состоит из одной строки:

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

Замечание
Очень важно запомнить, что процедуры и функции, имена которых начинаются на gl или glu, т.e. команды OpenGL, имеют какой-либо результат только при установленном контексте воспроизведения

Вернемся к команде glclearcolor, определяющей цвет фона. У нее четыре аргумента, вещественные числа, первые три из которых задают долю красного, зеленого и синего в результирующем цвете. Четвертый аргумент я задал его значение равным единице. Можете варьировать это значение произвольно, в данном примере это никак не скажется, так что пока можете просто не обращать внимания на этот аргумент. Согласно справке, все четыре аргумента функции glclearColor имеют тип GLclampf, соответствующий вещественным числам в пределах от нуля до единицы. О типах OpenGL подробнее если вы используете Delphi версии три или четыре, вы, возможно, столкнетесь с одной небольшой проблемой. Если запускать проекты, использующие OpenGL, под управлением среды Delphi, программа может случайным образом аварийно завершаться. Оборот «случайным образом» здесь я употребил постольку, поскольку один и тот же проект может привести к аварийному завершению, а может и работать вполне успешно. Я сталкивался с этой проблемой на компьютерах с различной конфигурацией и с различными версиями операционной системы, и, по-видимому, она связана с некорректным взаимодействием среды Delphi c драйверами. Если подобная проблема возникла и у вас, я рекомендую просто не запускать под управлением среды проекты, использующие OpenGL, a запускать собственно откомпилированные модули. В пятой версии Delphi такая ситуация не возникала, так что, по-видимому, этот недостаток разработчиками выявлен и устранен.

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

FPS в Delphi с использованием OpenGL (dglOpenGL)

Читал в гайде по OpenGL, что есть возможность регулировки FPS, но как, не объяснялось

Можно ли узнать как это вообще делается, потому что мой проект (пытаюсь создать 2д игру), выдаёт всего 17-18 fps

07.06.2020, 20:14

Криво работает алгоритм столкновений | Delphi 7 + dglOpenGL
Вообщем, я пишу игру бродилку типа Wolf3d/Doom. Юзаю Opengl 4.3(dglOpengl) и Delphi 7, так сказать.

OpenGL и FPS
Пустое окно, в котором нарисованы две линии, выдает 420 FPS, это нормально или должно быть больше?

Как подсчитать FPS (OpenGL/C++) ?
Здравствуйте, само задание звучит так: В программе нужно генерировать случайный набор точек с тремя.

dglOpenGL выгодно или нет?
Здравствуйте товарищи программеры :) В общем такая вот тема, сегодня где в интернете напоролся.

Ошибки в DLL при использовании dglopengl.pas
Есть функция которую реализую в DLL. В ней вызываются ф-ции из DglOpenGL.pas (Например.

08.06.2020, 00:13 2 08.06.2020, 22:45 3 09.06.2020, 01:27
Меню пользователя @ northener
09.06.2020, 08:45 5 09.06.2020, 08:59 [ТС] 6 09.06.2020, 12:26 7 09.06.2020, 13:20 8

Главное, что не мозг

09.06.2020, 14:00 [ТС] 9 09.06.2020, 14:27 10 09.06.2020, 14:44 [ТС] 11

я тоже, пока не прошел через агрессию, непонимание, а потом перепрочёл раз в 6 то сообщение, и понял его смысл.

Добавлено через 8 минут

09.06.2020, 16:30 12

Решение

В КАЖДОМ кадре вы последовательно загружаете текстуры с диска в одну и ту-же переменную, вот вам и тормоза.
Перед загрузкой новой текстуры в ту-же переменную память не освобождается, вот вам и утечка памяти.
Кто вам мешает создать несколько текстур и загрузить их при старте формы, а при рисовании просто менять их?

Ну и на правах рекламы.
В Delphi есть встроенный модуль opengl. Для начала разобрались бы с ним, а уже потом за сторонние принимайтесь. Потому как сейчас, чтоб помочь вам приходится, скопировав код и попытавшись запустить, к удивлению обнаруживать что такого модуля как dglopengl и нет вовсе, равно как и textures.

Добавлено через 4 минуты
Кстати да, строка

09.06.2020, 17:41 [ТС] 13

В источниках, которые я читал, утверждалось что opengl, встроенный в delphi, устарел и не имеет всего функционала более новых версий, и рекомендовали взять библиотеку gdlopengl, а textures, всё в том же источнике, используется для загрузки и обработки текстур

Спасибо, попробую добавить переменных, либо их обнуление, после чего отпишусь.

Так же, я хотел, после нахождения причины ошибки, привести код в более должное состояние, потому что там ужасный бардак

да, есть, но когда я создал эту тему, файл был несколько другого вида, то есть такого, который сейчас есть код тут, еле нашел скриншоты со старыми элементами кода, и «на коленке», тут в блокноте внёс изменения, потому что сейчас доступа к rad нет

Добавлено через 12 минут

Спасибо тебе, о великий!
Как только я сделал загрузку всех 4 текстур через FormCreate, а в самих кадрах просто их активацию glActiveTexture, окно программы сразу стало ходить адекватно и программа перестала вылетать!
Большое спасибо!

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

Статья: OpenGL и Delphi на практике

Любая теория хороша, если она может быть реализована на Delphi :-). Поэтому предлагаю не откладывая в долгий ящик написать первую программу на OpenGL — а потом, окрылившись успехом, вернуться к теории и как следует проштудировать все книги и сайты по сабжу, чтобы уж стать настоящими монстрами трехмерного моделирования.

Для начала придется проделать подготовительную работу:

настроить формат пикселей с учетом отображаемой информации;

создать контекст OpenGL и подготовить сам движок OpenGL к работе.

Формат пикселей удобно вынести в отдельную процедуру, которую мы оформим следующим образом:

procedure SetDCPixelFormat (dc: HDC);

var pfd: TPixelFormatDescriptor;

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

nSize:= sizeof (pfd);


dwFlags:= PFD_DRAW_TO_WINDOW or

SetPixelFormat (DC, nPixelFormat,@pfd);

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

Далее посредством вызова ChoosePixelFormat система выбирает подходящий формат пикселя — и мы присваиваем его (через SetPixelFormat) нашему окну.

Теперь нужно инициализировать контекст самого OpenGL посредством функций, содержащихся в модуле Windows, и произвести дополнительную настройку движка:

procedure TForm1.FormCreate (Sender: TObject);

прикладная математика

Основы программирования OpenGL в Borland Delphi: Введение

OpenGL (Open Graphics Library) – популярная библиотека для работы с 3D графикой. Стандарт OpenGL появился в 1992 году благодаря компании Silicon Graphics и сейчас переживает годы своего самого бурного развития.

Чуть-чуть побольше узнать об OpenGL и о том, как с ним работать в VC, можно, почитав wat’а: http://www.gamedev.ru/coding/11203.shtmlЯ хочу показать, как работать с этой библиотекой в таких популярных и, на мой взгляд, очень удобных средах разработки, как Delphi и С++Builder.

Эта — первая — статья посвящена в основном инициализации OpenGL.

Первым делом нужно подключить заголовочные файлы:

Если вы используете Delphi, то всё необходимое для работы с OpenGL находится в модуле OpenGL.dcu. А если вы используете С++Builder, то подключать придётся несколько файлов:

· gl.h и glu.h содержат прототипы основных функций OpenGL определённых в opengl32.dll и glu32.dll.

· glaux.h содержит вспомогательные (auxiliary) функции (glaux.dll). В этой статье я не буду использовать glaux.h, т.к. его функции не доступны в Delphi, да и не люблю я эту библиотеку. Кроме того основные задачи glaux (как, в прочем, и аналогичной, но более качественной, библиотеки GLUT) – это создание окон, таймеров, обработка клавиатуры и мыши, всё это есть в ИСР (Интегральная Среда Разработки) Delphi или С++Builder.

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

ppfd.dwFlags := PFD_DRAW_TO_WINDOW xor

pixelformat := ChoosePixelFormat(dc, ppfd);

if pixelformat=0 then

MessageBox(0, ‘ChoosePixelFormat failed’, ‘Error’, MB_OK);

if SetPixelFormat(dc, pixelformat, ppfd)=false then

MessageBox(0, ‘SetPixelFormat failed’, ‘Error’, MB_OK);

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

cColorBits – глубина цвета

cDepthBits – размер буфера глубины (Z-Buffer)

cStencilBits – размер буфера трафарета (мы его пока не используем)

iPixelType – формат указания цвета. Может принимать значения PFD_TYPE_RGBA (цвет указывается четырьмя параметрами RGBA — красный, зленный, синий и альфа) и PFD_TYPE_COLORINDEX (цвет указывается индексом в палитре). Как вы видите, я использую RGBA, и вам придётся поступить также, т.к. если вы захотите использовать COLORINDEX, то вам придётся изменить мою функцию: добавить пару флагов и дать начальные значения ещё нескольким переменным.

Более подробную информацию смотрите в справочнике или в MSDN.

Функция ChoosePixelFormat подбирает формат пикселей, максимально удовлетворяющий нашим требованиям, и возвращает его дескриптор, а SetPixelFormat устанавливает его в контексте устройства (dc).

После того как в контексте устройства установлен формат пикселей, нужно создать контекст воспроизведения (Rendering Context) для этого в OpenGL определены следующие функции:

function wglCreateContext(dc: HDC): HGLRC;

function wglMakeCurrent(dc: HDC; glrc: HGLRC):Boolean;

Наверное, объяснять их значение не стоит

Теперь перейдём к форме. В объявлении класса формы в области private добавьте следующее:

ghRC – указатель на контекст воспроизведения (Rendering Context)

ghDC – дескриптор устройства (для нас – просто указатель на окно)

Процедура Draw будет отвечать за рисование.

Далее заполняем FormCreate:

procedure TForm1.FormCreate(Sender: TObject);

if bSetupPixelFormat(ghDC)=false then

glClearColor(0.0, 0.0, 0.0, 0.0);

Вы видите, что тут вызывается FromResize, который мы ещё не описали. Надо это исправить. Поместите туда следующий код:

glViewport( 0, 0, Width, Height );

glOrtho(-5,5, -5,5, 2,12);

gluLookAt(0,0,5, 0,0,0, 0,1,0);

Теперь, наверное, надо кое-что объяснить.

glClearColor устанавливает цвет (в нашем случае чёрный), которым будет заполняться экран при очищении. У этой процедуры – 4 параметра, что соответствует RGBA. Вместо нее можно написать glClearIndex(0.0). Эта процедура устанавливает индекс цвета в палитре.

glViewport устанавливает область вывода – область, в которую OpenGL будет выводить изображение. В нашем случае – вся форма.

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

glLoadIdentity заменяет текущую матрицу видового преобразования на единичную (матрицу идентичности), т.е. просто сбрасывает ваши изменения.

glOrtho устанавливает режим ортогонального (прямоугольного) проецирования. Это значит, что изображение будет рисоваться как в изометрии. 6 параметров типа GLdouble (или просто double): left, right, bottom, top, near, far определяют координаты соответственно левой, правой, нижней, верхней, ближней и дальней плоскостей отсечения, т.е. всё, что окажется за этими пределами, рисоваться не будет. На самом деле эта процедура просто устанавливает масштабы координатных осей. Для того чтобы установить перспективное проецирование, используются процедуры glFrustum и gluPerspective, но о них – потом.

gluLookAt устанавливает параметры камеры: первая тройка – её координаты, вторая – вектор направления, третья – направление оси Y.

В OpenGL всё включается и выключается (разрешается и запрещается) процедурами glEnable и glDisable. Таким образом, мы разрешили тест глубины (GL_DEPTH_TEST), чтобы изображение было объёмным, разрешили давать нашим объектам какой-то цвет (GL_COLOR_MATERIAL), разрешили освещение (GL_LIGHTING) и включили «лампочку №0» (GL_LIGHT0).

glLightfv устанавливает свойства «лампочек»: позицию и направление света.

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

procedure TForm1.FormClose(Sender: TObject; var Action:

А теперь, давайте уже что-нибудь нарисуем!

Давайте нарисуем сферу, а потом заставим её крутиться. Итак, всё, что нам понадобится – это форма и таймер.

Установите интервал таймера на 10 миллисекунд (нам этого будет вполне достаточно). Теперь скопируйте все представленные выше фрагменты кода в соответствующие места. В процедуре Timer1Timer напишите одну сточку: Draw(); (в Delphi без скобок).


Теперь нам осталось только что-нибудь нарисовать, т.е. отредактировать функцию Draw.

glClear(GL_DEPTH_BUFFER_BIT xor GL_COLOR_BUFFER_BIT);

Всё, можно нажимать F9.

теперь кое-что поясню (в процедуре Draw не встретилось ни одной знакомой строчки).

glClear сбрасывает значения всего перечисленного в качестве параметров (в нашем случае очищает буфер цвета и буфер глубины). Этой процедуре передавать много всяких буферов для очистки, но лично я использую только 3: GL_DEPTH_BUFFER_BIT, GL_COLOR_BUFFER_BIT, иногда GL_STENCIL_BUFFER_BIT (буфер трафарета).

glColor устанавливает цвет фигуры. Существует следующий синтаксис как для glColor, так и для других функций OpenGL:

Поясняю, каждая функция OpenGL начинается с префикса «gl». Далее следует название функции. После названия – количество параметров (если функция определена для разного кол-ва параметров). И, наконец, переменными какого типа являются параметры:

· s – GLshort короткое целое

· f – GLfloat дробное

· d – GLdouble дробное с двойной точностью

· ub – GLubyte беззнаковый байт

· us – GLushort беззнаковое короткое целое

· ui – GLuint беззнаковое целое

· v – вектор – массив из n элементов указанного типа

Итак, glColor3f означает, что цвет задаётся тремя компонентами типа GLfloat.

Для рисования сферы мы используем механизм из glu32.dll. Создаём объект типа GLUquadricObj и инициализируем его функцией gluNewQuadric. Далее устанавливаем стиль фигуры функцией gluQuadricDrawStyle (quadObj, GLU_FILL). Стиль может быть GLU_FILL, GLU_LINE или GLU_POINT. Что каждый из них значит, проверьте сами.

gluSphere – делает из quadObj сферу. Три последних параметра – это радиус и количество разбиений поперёк и вдоль оси Z соответственно. Я взял маленькое число разбиений, чтобы было видно, что сфера крутится.

И не забудем освободить память, занимаемую под quadObj — gluDeleteQuadric(quadObj).

glRotatef – заставляет нашу сферу крутиться. О том, как это делается — в следующей статье.

И, наконец, SwapBuffers (ghDC) выводит всё на экран.

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 — ссылка на окно, уникальный идентификатор всех зарегистрированных в системе окон.

Илон Маск рекомендует:  Программная выписка счета в системе webmoney

Графическая система 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».

OpenGL и Delphi на практике

Издательский Дом «КОМИЗДАТ»

Любая теория хороша, если она может быть реализована на Delphi :-). Поэтому предлагаю не откладывая в долгий ящик написать первую программу на OpenGL — а потом, окрылившись успехом, вернуться к теории и как следует проштудировать все книги и сайты по сабжу, чтобы уж стать настоящими монстрами трехмерного моделирования.

Для начала придется проделать подготовительную работу:

настроить формат пикселей с учетом отображаемой информации;

создать контекст OpenGL и подготовить сам движок OpenGL к работе.

Формат пикселей удобно вынести в отдельную процедуру, которую мы оформим следующим образом:

procedure SetDCPixelFormat (dc: HDC);

var pfd: TPixelFormatDescriptor;

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

nSize:= sizeof (pfd);

dwFlags:= PFD_DRAW_TO_WINDOW or

SetPixelFormat (DC, nPixelFormat,@pfd);

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

Далее посредством вызова ChoosePixelFormat система выбирает подходящий формат пикселя — и мы присваиваем его (через SetPixelFormat) нашему окну.

Теперь нужно инициализировать контекст самого OpenGL посредством функций, содержащихся в модуле Windows, и произвести дополнительную настройку движка:

procedure TForm1.FormCreate (Sender: TObject);

wglMakeCurrent (DC, RC);

Как видим, сначала мы задали для нашей графики необходимый формат пикселей. Теперь при помощи функции wglCreateContext создаем OpenGL-контекст, а впоследствии делаем его текущим контекстом. Далее, используя уже универсальные функции**, произведем настройку «мира», который будем создавать. Для этого через glClearColor очистим контекст и заполним ее 60-процентным черным цветом. Далее выберем матрицу проекций, которая определяет, как будут проецироваться трехмерные объекты на плоскость экрана (в оконные координаты) и через glLoadIdentity установим единичную матрицу и зададим границы плана в «мировых координатах» при помощи вызова glFrustum. После чего загрузим модельно видовую матрицу и произведем ее смещение (glTranslatef).

Что будем рисовать

Конечно, можно было нарисовать простую пирамиду или же куб. Но мы сделаем большее — нарисуем «признание в любви»** (рис. 1). Специально для этого методом «научного перебора» была разработана модель, описывающая соответствующую кривую:

Остается только перевести ее с языка математики на нормальный человеческий.

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

Далее в FormPaint используем подготовленные заранее методы DrawFace и DrawElement (см. листинг ниже) для отрисовки упомянутого объекта. А для придания ему еще большей «жары» используем возможности OpenGL по освещению сцены.

С точки зрения сложности освоения OpenGL сопоставим с другими подобными библиотеками. Так что с одной стороны нет разницы, в чем разбираться и что изучать. Но с точки зрения разумного подхода любой проект трехмерной графики должен как минимум поддерживать OpenGL в качестве одной из опций. Ведь серьезные вещи считаются и визуализируются, как правило, под Unix/IRIX/Linux/FreeBSD, и в то же время было бы неправильно игнорировать пользователей Windows. Так что OpenGL как раз и является тем универсальным языком и общим знаменателем, позволяющим вашим приложениям свободно мигрировать с одной платформы на другую.

Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,

Dialogs,OpenGL, StdCtrls, ExtCtrls;

procedure FormCreate(Sender: TObject);

procedure FormDestroy(Sender: TObject);

procedure Timer1Timer(Sender: TObject);

procedure FormPaint(Sender: TObject);

procedure FormResize(Sender: TObject);

const mat1_dif:Array[0..2] of Single = (0.8,0.8,0.0);

const mat1_amb:Array[0..2] of Single = (0.2,0.2,0.2);

const mat1_spec:Array[0..2] of Single = (0.6,0.6,0.6);

OpenGl в Delphi

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

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

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

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

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

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

Илон Маск рекомендует:  Добавляем компонент в стандартный message dialog

Как операционная система различает окна для осуществления диалога с ними? Все окна при своем создании регистрируются в операционной системе и получают уникальный идентификатор, называемый «ссылка на окно». Тип этой величины в 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».

Opengl и delphi на практике

wglMakeCurrent (DC, RC);

Как видим, сначала мы задали для нашей графики необходимый формат пикселей. Теперь при помощи функции wglCreateContext создаем OpenGL-контекст, а впоследствии делаем его текущим контекстом. Далее, используя уже универсальные функции**, произведем настройку «мира», который будем создавать. Для этого через glClearColor очистим контекст и заполним ее 60-процентным черным цветом. Далее выберем матрицу проекций, которая определяет, как будут проецироваться трехмерные объекты на плоскость экрана (в оконные координаты) и через glLoadIdentity установим единичную матрицу и зададим границы плана в «мировых координатах» при помощи вызова glFrustum. После чего загрузим модельно видовую матрицу и произведем ее смещение (glTranslatef).

Что будем рисовать

Конечно, можно было нарисовать простую пирамиду или же куб. Но мы сделаем большее — нарисуем «признание в любви»** (рис. 1). Специально для этого методом «научного перебора» была разработана модель, описывающая соответствующую кривую:

Остается только перевести ее с языка математики на нормальный человеческий.

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

Далее в FormPaint используем подготовленные заранее методы DrawFace и DrawElement (см. листинг ниже) для отрисовки упомянутого объекта. А для придания ему еще большей «жары» используем возможности OpenGL по освещению сцены.

С точки зрения сложности освоения OpenGL сопоставим с другими подобными библиотеками. Так что с одной стороны нет разницы, в чем разбираться и что изучать. Но с точки зрения разумного подхода любой проект трехмерной графики должен как минимум поддерживать OpenGL в качестве одной из опций. Ведь серьезные вещи считаются и визуализируются, как правило, под Unix/IRIX/Linux/FreeBSD, и в то же время было бы неправильно игнорировать пользователей Windows. Так что OpenGL как раз и является тем универсальным языком и общим знаменателем, позволяющим вашим приложениям свободно мигрировать с одной платформы на другую.

Opengl и delphi на практике

wglMakeCurrent (DC, RC);

Как видим, сначала мы задали для нашей графики необходимый формат пикселей. Теперь при помощи функции wglCreateContext создаем OpenGL-контекст, а впоследствии делаем его текущим контекстом. Далее, используя уже универсальные функции**, произведем настройку «мира», который будем создавать. Для этого через glClearColor очистим контекст и заполним ее 60-процентным черным цветом. Далее выберем матрицу проекций, которая определяет, как будут проецироваться трехмерные объекты на плоскость экрана (в оконные координаты) и через glLoadIdentity установим единичную матрицу и зададим границы плана в «мировых координатах» при помощи вызова glFrustum. После чего загрузим модельно видовую матрицу и произведем ее смещение (glTranslatef).

Что будем рисовать

Конечно, можно было нарисовать простую пирамиду или же куб. Но мы сделаем большее — нарисуем «признание в любви»** (рис. 1). Специально для этого методом «научного перебора» была разработана модель, описывающая соответствующую кривую:

Остается только перевести ее с языка математики на нормальный человеческий.

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

Далее в FormPaint используем подготовленные заранее методы DrawFace и DrawElement (см. листинг ниже) для отрисовки упомянутого объекта. А для придания ему еще большей «жары» используем возможности OpenGL по освещению сцены.

С точки зрения сложности освоения OpenGL сопоставим с другими подобными библиотеками. Так что с одной стороны нет разницы, в чем разбираться и что изучать. Но с точки зрения разумного подхода любой проект трехмерной графики должен как минимум поддерживать OpenGL в качестве одной из опций. Ведь серьезные вещи считаются и визуализируются, как правило, под Unix/IRIX/Linux/FreeBSD, и в то же время было бы неправильно игнорировать пользователей Windows. Так что OpenGL как раз и является тем универсальным языком и общим знаменателем, позволяющим вашим приложениям свободно мигрировать с одной платформы на другую.

Понравилась статья? Поделиться с друзьями:
Кодинг, CSS и SQL