OpenGL и Delphi. Введение

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

Чтобы начать программировать с OpenGL в Delphi, необходимо иметь заголовочный файл OpenGL и собственно Delphi.

В стандартной поставке Delphi уже есть заголовочные файлы OpenGL, но мы сейчас поговорим о хорошем заголовочном файле, чем все остальные. Так как, стандартные постанавливаемые с Delphi заголовки, являются ущербными, не придерживается стандарта OpenGL, а так же полностью устаревшими. Для эффективного использования OpenGL в Delphi необходимо загрузить заголовочный файл DGLOpenGL.pas с этого сайта или сайта разработчиков (команды DGL) www.delphigl.com .

Прежде чем приступить к работе необходимо загруженный файл dglOpenGL.pas разместить в нужном месте, лучше всего его разместить в папке “\lib” в каталоге Delphi.
Теперь запустите Delphi, перед вами должен открыться пустой проект.
Примечание: если у вас установлена операционная система Windows 7. Вам так же необходимо произвести некоторые действия, чтобы успешно все компилировалось. Для начала вам необходимо выделить папку “\lib” кликнуть на ней правой кнопкой мыши. В контекстном меню выбрать «свойства» снять галочку «только для чтения». Потом выбрать закладку «безопасность» нажать кнопку «изменить». Появится окно «разрешения для группы «lib»». В этом окне выбрать пользователи и разрешить полный доступ и нажать кнопку применить (смотрите рисунок ниже).

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

Теперь объявить два свойства формы в разделе public (которые будут общедоступны):

dc: HDC – контекст устройств
hrc: HGLRC – контекст рендеринга, он необходим для OpenGL, чтобы рисовать на форме Инициализацию необходимо производить непосредственно при создании формы. Таким образом, следующий код должен быть в событии OnCreate формы:

Строчка Application.OnIdle:= IdleHandler; объясняется ниже.
После этой совершенно простой инициализации (можно, конечно, делать все вручную, с помощью стандартных функций Delphi,и определять формат пикселя, но это делается дольше, а с InitOpenGL все это делается проще и быстрей), вы имеете сразу в распоряжении все особенности OpenGL, которые определенны в начальных значениях. Но часто, определенные параметры по умолчанию в OpenGL не будут использоваться или их
нужно изменить. Поэтому напишем еще маленькую дополнительную процедуру: SetupGL.

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

Вы можете устанавливать цвет фона по своему усмотрению с glClearColor. В OpenGL цвета всегда определены как смесь красного, зеленого и синего.

Процедура SetupGL должна вызывать в конце FormCreate:

[…]
ActivateRenderingContext(dc,hrc); //активируем контекст рендеринга
end; Так как OpenGL выводится на форму, то при изменении её размеров, изменяется и выход изображения OpenGL, и не которые части могут скрыться, чтобы этого не происходило, необходимо среагировать на событие OnResize и она будет следующей.

Строка 2: Это булева переменная используется как манекен.
Строка 4: Задает область вывода указанием координат левого нижнего и правого верхнего углов (в пикселях, в оконных координатах).
Строка 5: Задаем, что используется проекционная матрица
Строка 6 и 9: Устанавливаем текущую матрицу равной единичной.
Строка 7: Здесь устанавливается, как зритель должен видеть сцену.
Строка 8: Задаем, что используется модельная матрица
Строка 11: То, что делает IdleHandler, описывается позже.
Кроме того необходимо определить две константы NearClipping и FarClipping в части Const. Они указывают расстояние до уровней отсечения (ограничения сцен) и играют определенную роль в тесте глубины.

Так как мы создавали контексты программно, то что бы после закрытия программы они в памяти не оставляли мусор необходимо освободить память. Лучше всего это сделать в событии OnDestroy.

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

Строка 3: Этот вызов заботиться о том, чтобы буфер цвета и буфер глубины удалялись. Если это не сделать, вы уведите все что угодно, но только не, то, что вы хотели передать. Цвет буфера не удаляется, а просто перезаписывается glClearColor, который вы определили в создании формы
Строка 7: здесь снова ставиться перспектива. Этот же вызов и при FormResize, они должны быть идентичны по параметрам. Иначе после Resize вывод будет другим. Если перспектива не изменяется между рендерингом, то эта команда также может пропускаться.
Строка 12: Этот вызов перемещает «камеру» (собственно в данном случае это ничего не дает), потому что мы хотим видеть, что мы изображаем. Все что находится слишком близко, отрезается плоскостью отсечения.
Строка 14: glBegin/glEnd инкапсулирует команды. Ими строятся красочный четырехугольник. Думаю для первого раза должно хватить. Очень важно в этом контексте следующее: OpenGl не важно, где команда вызывается. Все будет оцениваться и может оказаться в буфере кадра. Таким образом, вы можете написать подпрограмму, в которой содержатся команды OpenGL.
Строка 21: SwapBuffers гарантирует, что содержимое буфера кадра будет появляться на экране. Без этой команды, вы ничего не увидите OpenGL.

Так…Отлично. Теперь у вас есть функция рисования, но так или иначе, вы должны вызвать её. Но вызвать просто при активации или при нажатии какой-нибудь кнопки это очень просто. Результат нужно изменять так, например, как в играх, чтоб функция вызывалась снова и снова. Есть два варианта, оба имеют свои преимущества и недостатки. Смотрите таблицу

Аргумент

Таймер

Максимальная частота кадров достижима

Число кадров регулируемо

Условно (затруднительно)

Доступна плавная анимация (для шутеров)

Условно (плохо)

Доступно для меню

Доступно для простой анимации (стратегий)

Не разрежает аккумулятор ротативных компьютеров

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

Второй вариант с событием OnIdle, это событие типа TIdleEvent возникает при простое приложения. Чтобы смочь реагировать на «событие Idle», вы должны соединить теперь лишь эту функцию с событием. Это необходимо сделать, в то время когда пишете событие формы OnCreate в последней строчке:

[…]
SetupGL; //установка режимов OpenGL
Application.OnIdle:= IdleHandler;
end; Как вы поймете сами, этот метод загружает процессор на 100% (это плохо для батареи ноутбука), вы можете обходить это, маленьким трюком:
вставлять перед “Done:= false” строку Sleep(1) или Sleep(5). Вследствие этого опускается нагрузка процессора до 80%. Таким образом, код процедуры IdleHandler будет выглядить так:

Вот и все собственно, вы теперь можете откомпилировать и увидеть то, что у вас получилось.

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

Введение
По мере знакомства с использованием OpenGL в Delphi у меня появился ряд проектов, иллюстрирующих различные аспекты этого вопроса. Проекты начинаются с самых минимальных программ, в которых просто окрашивается окно или выводится прямоугольник с использованием функций OpenGL и заканчиваются масштабными проектами из тысяч строк кода. Эти работы позволяют проследить мой путь и могут служить подмогой для тех, кто только начинает разбираться в этих вопросах.
Когда я начинал изучение этого, у меня не было ни одного примера использования OpenGL в Delphi, только ворох программ на C и C++, поэтому пришлось начинать с того, чтобы перекладывать эти программы на Delphi. Затем появились и полностью собственные проекты. Моя основная работа связана с преподаванием в вузе, после того, как я включил в учебные курсы изучение основ OpenGL, студенты с моей помощью смогли создать ряд интересных проектов.
Я решил опубликовать некоторые из проектов моей коллекции, озаглавил набор "ЖиЛистая Delphi" и предложил сайту "Королевство Delphi". На сайте мне предложили дополнить эти проекты серией статей по вопросам использования OpenGL в Delphi. Данная статья является первой статьей этого цикла.
Статьи я предполагаю писать на уровне, доступном для самой широкой аудитории - от новичков в программировании для Windows до умудренных профессионалов. Я постараюсь придерживаться краткости в своих рассуждениях, освещая только суть рассматриваемых вопросов. Многие вопросы, освещаемые здесь, ясно проиллюстрированы в проектах "ЖиЛистой Delphi".
OpenGL - стандартный для большинства платформ и операционных систем набор низкоуровневых функций двумерной и трехмерной графики, библиотека, широко используемая в промышленных CAD-системах и играх.
Поставляется в составе операционной системы Windows, начиная с версии OSR2 в виде двух DLL-файлов - opengl32.dll и glu32.dll. Первая из этих библиотек и есть собственно набор функций OpenGL, вторая содержит дополнительный набор функций, упрощающих кодирование, но построенных и выполняемых с подключением opengl32.dll и являющаяся надстройкой.
То, что эти библиотеки поставляются в составе операционной системы, значительно упрощает распространение разработанных приложений. То, что OpenGL распространяется в виде динамических библиотек, упрощает доступ к его функциям.
При выборе базы для построения приложений графики несомненными достоинствами OpenGL являются его простота и стандартность - код в случае необходимости можно легко перенести на другую платформу или под другую операционную систему.
Для более подробной информации о OpenGL Вы можете обратиться на сайт http://www.opengl.org
Вместе с Delphi, начиная с третьей версии, поставляется файл помощи по OpenGL фирмы MicroSoft и заголовочный файл opengl.pas, позволяющий использовать эту графическую библиотеку в приложениях, написанных на Delphi.
Есть также альтернативные версии заголовочных файлов независимых разработчиков и компоненты, использующие OpenGL, упрощающие доступ к его функциям и использующие ООП подход. Некоторые из этих файлов и компонентов могут использовать версию OpenGL для Windows фирмы SGI, имеющую собственное расширение функций и имеющую более высокие скоростные показатели. Одна из самых полных систем, реализующая набор функций всех версий OpenGL - это библиотека разработчика MGL фирмы SciTechSoft.
Мы в своих примерах будем опираться только на стандартные файлы и компоненты Delphi, так что Вам не придется искать и приобретать дополнительные файлы и компоненты. Умея самостоятельно использовать OpenGL, Вы легко сможете использовать готовые компоненты, скрывающие черновую работу подключения и использования функций OpenGL. Тем более, что многие из этих компонентов отличаются неустойчивой работой.
Итак, Delphi в стандартной поставке позволяет использовать OpenGL в разрабатываемых приложениях, но как это сделать, плохо понятно из файла помощи, а готовыми примерами использования OpenGL Delphi не сопровождается (по крайней мере, на сегодня). Поэтому начинающим часто тяжело самостоятельно разобраться, как же работать с OpenGL в Delphi. Рассмотрению вопросов использования OpenGL вообще и использованию в Delphi и будет посвящен данный курс статей.
К сожалению, эта тема осложнена тем обстоятельством, что для построения самой минимальной программы OpenGL требуется выполнить ряд обязательных действий, код реализации которых может напугать начинающих своей обширностью.
Для понимания смысла этих действий желательно понимать смысл основных понятий операционной системы Windows - ссылка, контекст, сообщение, в проектах Delphi не всегда активно используемых программистами. Желательно иметь хотя бы минимальные знания о роли динамических библиотек в этой операционной системе. Хотя, конечно, можно успешно использовать OpenGL и без глубоких знаний в этой области, используя готовые шаблоны приложений и сосредоточившись собственно на функциях OpenGL.
Важно также отметить то, что чаще всего приложения, активно использующие графику, нуждаются от Delphi только в создании окна приложения, таймере и обработчике манипуляций с клавиатурой и мышью. Для таких приложений чаще всего и не требуется богатство библиотеки VCL. и крайне важны скорость работы и "профессиональная" миниатюрность откомпилированного модуля. Поскольку мы вынуждены с самого начала рассматривать и разбирать темы уровнем ниже RAD-технологий, то нам становится по силам и написание программ без визуальных средств вообще, программ, использующих только функции Windows API, стремительно компилируемых и занимающих после компиляции миниатюрные размеры (порядка двух десятков килобайт).
Итак, наш разговор приходится начинать с вопросов, напрямую вроде бы не связанных с OpenGL.
Постараемся ограничить рассмотрение этих тем самым минимальным объемом, поскольку Вы легко можете найти другие, более богатые источники по этим вопросам.
Событие. Сообщение. Контекст.
Начнем наш разговор с понятий "событие" и "сообщение".
Очень часто это синонимы одного и того же термина операционной системы, общающейся с приложениями посредством посылки сообщений. Код, написанный в проекте Delphi как обработчик события OnCreate, выполняется при получении приложением сообщения WM_CREATE, сообщению WM_PAINT соответствует событие OnPaint, и т.д..Такие события использует мнемонику, сходную с мнемоникой сообщений.
Как операционная система различает окна для осуществления диалога с ними? Все окна при своем создании регистрируются в операционной системе и получают уникальный идентификатор, называемый "ссылка на окно". Тип этой величины в Delphi - HWND (WiNDow Handle, ссылка на окно).
Ссылка на окно может использоваться не только операционной системой, но и приложениями для идентификации окна, с которым необходимо производить манипуляции.
Попробуем проиллюстрировать смысл ссылки на окно на несложном примере.
Откомпилируйте минимальное приложение Delphi и начните новый проект. Форму назовите Form2, разместите на форме кнопку, обработчик события OnClick кнопки приведите к следующему виду:

begin

if H <> 0 then

ShowMessage ("Есть Form1!" )

else

ShowMessage ("Нет Form1!" )

end ;


Теперь при щелчке на кнопке выдается сообщение, есть ли запущенное приложение, класс окна которого зарегистрирован в операционной системе как "TForm1", в заголовке которого записано "Form1". То есть если одновременно запустить обе наши программы, при нажатии на кнопку выдается одно сообщение, если окно с заголовком "Form1" закрыть, при щелчке на кнопку выдается другое сообщение.
Здесь мы используем функцию API FindWindow, возвращающую величину типа HWND - ссылку на найденное окно либо ноль, если такое окно не найдено.
Итак, ссылка на окно однозначно определяет окно. Свойство Handle формы и есть эта ссылка, значение которой форма получает при выполнении функции API CreateWindow - создании окна. Имея ссылку на окно, операционная система общается с окном путем посылки сообщений-сигналов о том, что произошло какое-либо событие, имеющее отношение именно к этому окну. Если окно имеет намерение отреагировать на это событие, операционная система имеет это в виду и вместе с окном осуществляет эту реакцию. Окно может и не имея фокус получать сообщения и реагировать на них.
Проиллюстрируем это на примере.
Обработчик события OnMouseMove формы приведите к виду:

procedure TForm2.FormMouseMove (Sender: TObject ;

Shift: TShiftState; X, Y: Integer ) ;

begin

Caption:= "x=" + IntToStr (X) + ", y=" + IntToStr (Y) ;

end ;

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

procedure TForm2.Button1Click (Sender: TObject ) ;

begin

H:= FindWindow ("TForm1" , "Form1" ) ;

if H <> 0 then

SendMessage(H, WM_CLOSE, 0 , 0 ) ;

end ;

Если имеется окно класса "TForm1" с заголовком "Form1", наше приложение посылает ему сообщение WM_CLOSE - пытается закрыть окно.
Точно также, если необходимо нарисовать что-либо на поверхности чужого окна, необходимо получить ссылку на это окно.
Для начала попробуем рисовать на поверхности родного окна.
Разместите еще одну кнопку, обработку щелчка которой приведите к виду:

begin

Dc:= GetDC (Handle) ;

ReleaseDC (Handle, dc) ;

end ;

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

procedure TForm2.Button2Click (Sender: TObject ) ;

begin

Window:= FindWindow ("TForm1" , "Form1" ) ;

if Window <> 0 then

begin

Dc:= GetDC (Window) ;

Rectangle (dc, 10 , 10 , 110 , 110 ) ;

ReleaseDC (Handle, dc) ;

end ;

Теперь во время работы приложения, если в системе зарегистрировано окно класса "TForm1" с заголовком "Form1", вывод будет осуществляться на него. Запустите параллельно откомпилированные модули минимального и только что созданного приложений. При щелчке на кнопке прямоугольник рисуется на поверхности чужого окна.
Замечу, что если закрыть Project1.exe и загрузить в Delphi соответствующий ему проект, при щелчке на кнопке прямоугольник будет рисоваться на поверхности окна формы, что будет выглядеть необычно.
Функции Windows для воспроизведения нуждаются в специальной величине типа HDC (Handle Device Context, ссылка на контекст воспроизведения), для задания значения которой необходимо иметь величину типа HWND - ссылка на окно, уникальный идентификатор всех зарегистрированных в системе окон. В зависимости от версии Delphi ссылки имеют тип либо Integer, либо LongWord.
Графическая система OpenGL, как и любое другое приложение Windows, также нуждается в ссылке на окно, на котором будет осуществляться воспроизведение - специальной ссылке на контекст воспроизведения - величина типа HGLRC (Handle openGL Rendering Context, ссылка на контекст воспроизведения OpenGL). Для получения этого контекста OpenGL нуждается в величине типа HDC (контекст воспроизведения) окна, на который будет осуществляться вывод.
Поэтому наши примеры имеют следующие строки в разделе private описания формы:


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

DC:= GetDC(Handle) ;

SetDCPixelFormat;

Hrc:= wglCreateContext(DC) ;

WglMakeCurrent(DC, hrc) ;

То есть мы получаем контекст воспроизведения Windows, задаем желаемый формат пикселей, создаем контекст воспроизведения OpenGL и делаем его текущим, чтобы вызываемые функции OpenGL могли работать с этим окном.
По поводу формата пикселей мы поговорим подробнее чуть позже, а сейчас я хотел бы обратить внимание на два момента.
Во-первых, величину типа HDC мы получаем при создании окна, в обработчике события OnCreate, или, другими словами, в обработчике сообщения WM_CREATE. Это является обычным и традиционном для Windows-программ.
Некоторые программисты сделали мне замечание, что получение контекста воспроизведения при создании окна является несколько некорректным для Windows 9X и более правильным было бы получение контекста в обработчике событий OnShow или OnPaint. Возможно, это так и есть, и в некоторых ситуациях может сказаться на корректности работы приложения. Вы должны учитывать это при написании ответственных приложений.
Во-вторых, контекст воспроизведения Windows и контекст воспроизведения OpenGL обычно освобождаются приложением. То есть, команды вывода OpenGL обычно обрамляются следующими строками:

dc:= BeginPaint(Window, ps) ;
wglMakeCurrent(DC, hrc) ;
wglMakeCurrent(0 , 0 ) ;
EndPaint (Window,ps) ;
ReleaseDC (Window, dc) ;

Повторяю, это обычные последовательности действий для Windows-программ, контекст воспроизведения должен быть доступен системе и другим приложениям. Я же во многих примерах пренебрегаю этим правилом для сокращения кода. Вы можете убедиться, что программы работают в общем случае корректно, хотя мы отдаем себе отчет, что в некоторых ситуациях такой подход может привести к "глюковатости" работы приложения. Это также надо учесть при написании ответственных приложений.
В наших примерах контекст воспроизведения OpenGL мы занимаем сразу же при его получении, в обработчике события OnCreate, а освобождаем в конце работы приложения, в обработчике события OnDestroy.
Еще одно замечание - команды и функции OpenGL имеют префикс gl для размещенных в библиотеке opengl32.dll и glu для размещенных в библиотеке glu32.dll. Прототипы этих функций находятся в модуле opengl.pas. Функции OpenGL, имеющие отношение только к реализации OpenGL под Windows, имеют префикс wgl, как, например, wglCreateContext, а некоторые вообще не имеют префикса, например, SwapBuffers. Их прототипы описаны в модуле windows.pas.
Если понятия "сообщение" и "контекст" Вами поняты, сейчас Вы можете разобрать проекты WinMin.dpr и Paint.dpr в каталоге Beginer/0. В списке uses данных проектов перечислены всего два модуля - Windows и Messages (SysUtils в проекте Paint не используется). Это означает, что данные проекты не используют библиотеку VCL Delphi. После компиляции этих проектов Вы получите 16-ти килобайтные приложения. Приложения эти иллюстративные, умеют делать немногое, но для нас важен код проектов, возвращающий во времена старого доброго Borland Pascal-я, громоздкий, плохочитаемый, но эффективный для наших задач. Эти проекты помогают понять новичкам, какую каторожную работу выполняет за нас Delphi, и как в действительности работают Windows-приложения. Проекты я постарался хорошо откомментировать, чтобы Вам было легче разобраться.
Если Вы разберетесь, как рисовать функциями GDI на поверхности своего окна, Вы яснее сможете понять, как машина OpenGL рисует на поверхности чужого окна.

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

Хочу разъяснить ситуацию со всевозможными версиями библиотек и заголовочных файлов OpenGL.

Начиная с Delphi 3 в каталоге Source можно найти файл OpenGL.pas - это и есть оригинальная версия заголовков библиотеки OpenGL. Она получена трансляцией файлов Gl.h и Glu.h, соответственно с языка C.

Существует еще одно практически стандартное расширение - Glut. В оригинале Glut.h и ее трансляция на Delphi Glut.pas. Это расширение создано для более быстрого построения различных объектов OpenGL и набор полезных процедур.

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

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

Итак начнем. Приведу иерархию объектов, использованных в данной библиотеке:

Введение

Пример 1

В этом примере рассмотрены следующие вопросы:

Создание сцены TSceneGL

Scene:TSceneGL; ... Scene:=TSceneGL.create; // Создаем новую сцену....

Создание объекта TEntity - куб, задание его с помощью TFace, установка цвета и добавление на сцену.

Cube:Tentity; Face:TFace; .... Cube:=TEntity.create; // Создаем пустой объект TEntity Cube.SetColor(90,200,150); // Инициируем цвета R,G,B Face:=cube.addFace; // Создаем 1-й face в исходном кубе всего 6 граней (в кубе) Face.AddVertex(1.0, 1.0, 1.0,0.0, 0.0, 1.0); // добавляем 1-й vertex Face.AddVertex(-1.0, 1.0, 1.0,0.0, 0.0, 1.0); // добавляем 2-й vertex Face.AddVertex(-1.0, -1.0, 1.0,0.0, 0.0, 1.0);// добавляем 3-й vertex Face.AddVertex(1.0, -1.0, 1.0,0.0, 0.0, 1.0);// добавляем 4-й vertex .... Scene.Entities.add(cube);// добавим куб на сцену

Задание источника света Tlight и добавление на сцену.

Light:=Tlight.create(1); // создадим источник света и добавим его на сцену Scene.lights.add(light);

Инициализация сцены

// передадим Handle Panel1 нашей сцене, на ней будет происходить рендеринг Scene.InitRC(panel1.handle); Scene.UpdateArea(panel1.width,panel1.height);

Простейший поворот объекта

Procedure TForm1.Timer1Timer(Sender: TObject); begin // изменяем rx, ry,rz .... Tentity(Scene.Entities.Items).Rotate(rx,ry,rz); // повернем куб Scene.Redraw; // ... и обновим сцену end;

Пример 2

Отличия от предыдущего примера: Оъекта TEntity - дельфин задается не через TFace, а грузится из DXF файла - что очень упрощает жизнь.

Dolphin:=TEntity.create; Dolphin.SetColor(100,100,160); Dolphin.LoadDXF("..Dolphin.dxf",true); with dolphin do begin move(0,0,-15); // переместим немного назад rotate(-30,-30,-30); // и повернем end; Scene.entities.add(dolphin); // добавим дельфина на сцену...

Пример 3

Изменения: У объекта Dolphin ищутся нормали к TFace, при этом получается эффект объемного пространства:

Dolphin.CalcNormals; // высчитываем нормали векторов объекта Dolphin.Center; ....

Пример 4

Procedure TForm1.FormCreate(Sender: TObject); .... Mouse:=T3DMouse.create(dolphin); // Создаем T3DMouse для нашего объекта Mouse.scale(1,1,0.1,1,1,1); // задаем скорость перемещения и поворота....

Перемещение объекта можно осуществить:

Procedure TForm1.Panel1MouseMove(Sender: TObject; Shift: TShiftState; X, Y: Integer); begin Mouse.move(x,y,shift); // обновляем объект - дельфин в новых координатах Scene.Redraw; // перерисуем сцену end;

Можно установить блокировку на перемещение:

Mouse.Block(4,TspeedButton(sender).down); // блокировка поворота rx ...

Пример 5

Добавим управление отображением:

Case dolphin.wireframe of 0:begin speedbutton5.caption:="Линии"; dolphin.wireframe:=1; end; 1:begin speedbutton5.caption:="Заливка"; dolphin.wireframe:=2; end; 2:begin speedbutton5.caption:="Точки"; dolphin.wireframe:=0; end; end; Scene.Redraw; // обновим сцену...

Пример 6

Соберем все в один пример, добавим возможность печати и покажем FPS при повороте.

Ну вот, самая основа должна стать Вам понятна. Если у Вас не запускаются примеры, проверьте наличие библиотек Opengl32.dll и Glu32.dll. Исходники примеров находятся в каталогах Demo1-XXX.

Текстуры

Эта глава посвящена текстурированию объектов в OpenGL. В каждом примере я даю пояснения только к новому. По этому лучше посмотреть сначала пример 1 потом 2, а не начинать с примера 5. В этой главе описаны примеры использующие фильтрации текстур, и при использовании GL_linear нужен 3D ускоритель, иначе будет работать ОЧЕНЬ медленно.

Пример 1

Закрашиваем полигоны

Point:TVertex; .... begin // Получаем выбранный полигон при нажатии на панель Panel1 mouse.FindVertex(x,panel1.height-y,Scene,point); for i:=1 to numFound do // Для всех полигонов найденных по Mouse.FindVertex begin poly:=VertexHits[i] shr 16; vert:=VertexHits[i] mod 65536; If (poly "

Пример 2

Формат DXF является очень неудобным для записи т.к. имеет большие размеры. Для этого автор создает другой формат записи. Добавлена функция чтения-записи.

Пример 3

Наложение текстур на объект

Thing.CreateTexture; // добавим текстуры на объект thing.texture.LoadTexture("texture.bmp"); thing.texture.automatic:=False; ... // а далее идет рутинный процесс задания вершин для текстур у каждой поверхности объекта with thing do begin vertex:=Tvertex(Tface(thing.faces.items).vertices.items);// x,y,z =0,0,0 vertex.tx:=1; vertex.tz:=0; ... end;

Да в качестве эксперимента вы можете убрать все начиная с with thing do ..... end (задания точек для текстур) и переписать thing.texture.automatic:=true.

Использование типов фильтрации текстур

Thing.Texture.MagFilter:=GL_linear;

Thing.Texture.MagFilter:=GL_nearest;

Пример 4

Установка прозрачности у текстур (альфа каналов)

Thing.texture.EnvironmentMode:=GL_decal; // Установка transparent texturing thing.texture.envblendcolor:=scrollbar1.position; // Значение 0 - 255

При запуске программы сначала создается сцена с освещением, но объект создается только после установки, по выполнению процедуры FormCreate_partII.


Пример 5

Программная текстура:) Процесс прост - при нажатии на кнопки с цифрами мы создаем TBitmap и через его Canvas пишем эти цифры. Далее сохраняем этот ТBitmap в файл "texture.bmp". Удаляем с экрана компа старую текстуру и загружаем новую из ранее созданного "texture.bmp".


Исходные тексты всех примеров находятся в каталогах Demo2-XXX

Освещение

Пример 1

В этом примеры показано как задать параметры источника. Ранее источник света задавался по умолчанию. Создадим источник типа прожектор

(CLspot). Light:=Tlight.create(1); Light.LightType:=CLspot; Light.CutOffAngle:=5; Light.SpotExponent:=100;

Параметр CutOffAngle - угол расхождения луча.

SpotExponent - параметр затухания источника.

Light.SetOrientation(0,-1,0); Light.Source.SetPosition(0,5,-20);

Грубо говоря это коэфициент показывающий как далеко пройдет свет. Если SpotExponent=0, то пучек света уходит в бесконечность. Идем далее:

Задаем ориентацию прожектора и его положение в системе координат.
При изменении позиций полос прокрутки просто меняем компоненты цвета в источнике. Например для Ambient:

SBlightRed.position:=round(Light.Fambient * 100); SBlightGreen.position:=round(Light.Fambient * 100); SBlightBlue.position:=round(Light.Fambient * 100);

Пример 2

Наиболее полно контролируем параметры освещения и даже перемещем источник в пространстве.


Все, исходные тексты всех примеров этой главы вы найдете в каталогах Demo3-XXX

Туман

Пример 1

Установить поддержку тумана в сцене очень просто:

Scene:TSceneGL; ... scene.fogEnabled:=True;

Все остальное в примере это установка параметров тумана:

Тип тумана может быть:

FogType:=Gl_Linear; fogType:=GL_exp; fogType:=GL_exp2;

Минимальная и максимальная дистанция:

  • fogMaxDist
  • fogMinDist

Интенсивность:

  • fogDensity

Цвет для каждой компоненты RGB:

  • scene.fogcolor; R
  • scene.fogcolor; G
  • scene.fogcolor; B

После каждого изменения параметров не забывайте вызывать Redraw. Для нормального эффекта тумана очень важно подобрать правильный цвет фона и самого тумана. Они должны примерно совпадать /см картинку /.


Примеры находятся в каталоге Demo4-XXX.