OpenGL - Лекция №1

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

Когато започнах да уча това, нямах нито един пример за използване на OpenGL в Delphi, а само куп C и C++ програми, така че трябваше да започна с транспониране на тези програми в Delphi. Тогава имаше напълно собствени проекти. Основната ми работа е свързана с преподаване в университета, след като включих изучаването на основите на OpenGL в курсовете за обучение, студентите с моя помощ успяха да създадат редица интересни проекти.

OpenGL е набор от ниско ниво на 2D и 3D графични функции, стандартни за повечето платформи и операционни системи, библиотека, широко използвана в индустриални CAD системи и игри.

Доставя се като част от операционната система Windows, като се започне от версията OSR2, под формата на два DLL файла - opengl32.dll и glu32.dll. Първата от тези библиотеки е действителният набор от функции на OpenGL, втората съдържа допълнителен набор от функции, които опростяват кодирането, но са изградени и работят с връзката opengl32.dll и са добавка.

Фактът, че тези библиотеки се доставят като част от операционната система, значително опростява разпространението на разработените приложения. Фактът, че OpenGL се разпространява като динамични библиотеки, улеснява достъпа до неговите функции.

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

За повече информация относно OpenGL, моля посетете www.opengl.org.

Заедно с Delphi, започвайки от третата версия, се доставя помощният файл за OpenGL от MicroSoft и заглавният файл opengl.pas, което позволява използването на тази графична библиотека в приложения, написани на Delphi.

Има и алтернативни версии на заглавни файлове и компоненти на трети страни, които използват OpenGL, опростяват достъпа до неговите функции и използват OOP подход. Някои от тези файлове и компоненти може да използват версията на SGI на OpenGL за Windows, която има собствено функционално разширение и има по-бърза производителност. Една от най-пълните системи, която реализира набор от функции за всички версии на OpenGL, е библиотеката за разработчици MGL от SciTechSoft.

В нашите примери ще разчитаме само на стандартни Delphi файлове и компоненти, така че не е нужно да търсите и купувате допълнителни файлове и компоненти. Знаейки как сами да използвате OpenGL, можете лесно да използвате готови компоненти, които скриват грубата работа по свързване и използване на OpenGL функции. Освен това много от тези компоненти се характеризират с нестабилна работа.

И така, Delphi в стандартната дистрибуция ви позволява да използвате OpenGL в разработени приложения, но как да направите това е слабо разбрано от помощния файл и Delphi не е придружен от готови примери за използване на OpenGL (поне за днес). Поради това за начинаещите често е трудно да разберат сами как да работят с OpenGL в Delphi. Разглеждане на въпросиизползване на OpenGL като цяло и използването му в Delphi и този курс от статии ще бъде посветен на това.

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

Също така е важно да се отбележи, че повечето приложения с интензивна графика се нуждаят от Delphi само за създаване на прозорец на приложение, таймер и манипулатор на клавиатура и мишка. За такива приложения най-често не се изисква богатството на VCL библиотеката. а скоростта на работа и "професионалната" миниатюризация на компилирания модул са изключително важни. Тъй като от самото начало сме принудени да разглеждаме и анализираме теми на ниво под RAD технологиите, можем също да пишем програми без визуални инструменти изобщо, програми, които използват само Windows API функции, които се компилират бързо и заемат миниатюрни размери след компилация (около две десетки килобайта).

Така че трябва да започнем нашия разговор с въпроси, които не изглеждат пряко свързани с OpenGL.

Ще се опитаме да ограничим разглеждането на тези теми до минималния обем, тъй като лесно можете да намерите други, по-богати източници по тези въпроси.

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

Нека започнем нашия разговор с понятията "събитие" и "съобщение".

Много често те са синоними на един и същ термин за операционна система, която комуникира с приложения чрез изпращане на съобщения. Кодът, написан в проекта Delphi като манипулатор на събития OnCreate, се изпълнява, когато приложението получи съобщение WM_CREATE, събитие OnPaint съответства на съобщение WM_PAINT и т. н. Тези събития използват мнемоника, подобна на мнемоника на съобщение.

Връзка към прозорецаможе да се използва не само от операционната система, но и от приложения за идентифициране на прозореца, който трябва да бъде манипулиран.

Нека се опитаме да илюстрираме значението на връзката към прозореца на прост пример.

Компилирайте минимално Delphi приложение и започнете нов проект. Назовете формуляра Form2, поставете бутон върху формуляра, пренесете манипулатора на събитие OnClick на бутона към следния формуляр:

Вече при натискане на бутона се извежда съобщение дали има работещо приложение, чийто клас прозорец е регистриран в операционната система като 'TForm1', в заглавието на което пише 'Form1'. Тоест, ако стартираме и двете си програми едновременно, при натискане на бутона се показва едно съобщение, ако прозорецът със заглавие 'Form1' е затворен, при натискане на бутона се показва друго съобщение.

Тук използваме функцията FindWindow API, която връща стойност от типа HWND – връзка към намерения прозорец или нула, ако не е намерен такъв прозорец.

Нека илюстрираме това с пример.

Променете манипулатора на събитие OnMouseMove на формуляра на:

Координатите на показалеца на мишката се показват в заглавката на формуляра.

Стартирайте два екземпляра на програмата и обърнете внимание, че прозорецът, който няма фокус („неактивен“), също реагира на преместване на показалеца по повърхността му.

Имайки връзка към прозореца, приложението може да извършва всякакви действия с него, като изпраща съобщения до него.

Нека променим кода на манипулатора за кликване върху бутон:

Ако има прозорец от клас 'TForm1' със заглавие 'Form1', нашето приложение му изпраща WM_CLOSE съобщение - то се опитва да затвори прозореца.

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

Първо, нека се опитаме да нарисуваме върху повърхността на основния прозорец.

Публикувайте още единбутон, чиято обработка на щракване трябва да изглежда така:

Стартирайте приложението. Когато щракнете върху добавения бутон, върху повърхността на прозореца се изчертава квадрат. За рисуване използваме функции от ниско ниво на Windows.

Нека се опитаме да нарисуваме върху повърхността на прозореца на някой друг, за който ще променим кода, който току-що написахме:

Сега, докато приложението работи, ако в системата е регистриран прозорец от класа „TForm1“ със заглавие „Form1“, изходът ще се извърши върху него. Стартирайте паралелно компилираните модули на минималното приложение и новосъздаденото приложение. При натискане на бутона върху повърхността на чуждия прозорец се изчертава правоъгълник.

Отбелязвам, че ако затворите Project1.exe и заредите съответния проект в Delphi, когато щракнете върху бутона, върху повърхността на прозореца на формуляра ще се изчертае правоъгълник, който ще изглежда необичайно.

Следователно нашите примери имат следните редове в частния раздел на описанието на формуляра:

И манипулаторът на събитие OnCreate на формуляра започва със следните редове:

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

Ще говорим повече за пикселния формат малко по-късно, но засега бих искал да обърна внимание на две точки.

Първо, получаваме стойността на типа HDC, когато прозорецът е създаден, в манипулатора на събитие OnCreate или, с други думи, в манипулатора на съобщения WM_CREATE. Това е обичайно и традиционно за Windows програми.

Някои програмисти ми посочиха, че получаването на контекста за възпроизвеждане при създаване на прозорец е донякъде неправилно в Windows 9X и би било по-правилно да получите контекста в манипулатора на събития OnShow или OnPaint.Може би това е така и в някои ситуации може да повлияе на правилната работа на приложението. Трябва да вземете предвид това, когато пишете отговорни приложения.

Второ, Windows Replay Context и OpenGL Replay Context обикновено се освобождават от приложението. Това означава, че изходните команди на OpenGL обикновено са рамкирани със следните редове:

Отново, това са нормални програми за Windows, контекстът на възпроизвеждане трябва да е достъпен за системата и другите приложения. В много примери пренебрегвам това правило, за да съкратя кода. Можете да се уверите, че програмите работят като цяло правилно, въпреки че сме наясно, че в някои ситуации този подход може да доведе до "бъгови" приложения. Това също трябва да се вземе предвид при писане на критични приложения.

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

Още една забележка – командите и функциите на OpenGL имат префикс gl за тези, които се намират в библиотеката opengl32.dll, и glu за тези, които се намират в библиотеката glu32.dll. Прототипите за тези функции са в модула opengl.pas. Функциите на OpenGL, които са подходящи само за реализацията на OpenGL в Windows, имат префикс wgl, като wglCreateContext, а някои изобщо не са с префикс, като например SwapBuffers. Техните прототипи са описани в модула windows.pas.

Ако разбирате как да рисувате с GDI функции върху повърхността на вашия прозорец, ще можете да разберете по-ясно как OpenGL машината рисува върху повърхността на нечий друг прозорец.