OpenGL уроци

1. Въведение, поставяне на цели.

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

И така, ще започнем, като дефинираме какво се има предвид под растерен модел.

3. Създаване на основната обвивка на програмата.

Следните елементи ще бъдат разположени на прозореца (описание според номерата на фигура 2):

  1. Програмно меню. В момента ще създадем 3 раздела на менюто: Файл, Чертеж и Слоеве (техните подменюта могат да се видят на Фигура 3). Фигура 3. Примери за създадени менюта.
  2. Лента с инструменти. Ширина на панела44пиксела. Създайте3-xзаготовки за бутони за бъдещето. В следващите части на главата ще запълним този панел с бутони за настройка на режимите за рисуване на четки, рисуване на геометрични обекти и т.н.
  3. ЕлементътSimpleOpenGLControl. Позиционирайте го, както е показано, след това задайте свойствотоnameнаAnT. Това е мястото, където ще отиде основниятrender.
  4. Ето елементаCheckedListBox. В следващата част на главата ще внедрим система от слоеве, които ще участват активно във визуализацията. Този елемент ще покаже списък с нашите слоеве. Шелът ще получи и функции за редактиране на параметрите на слоя.
  5. Това е мястото, където ще бъде лентата с инструменти. Засега ще поставим бутони2върху него. В бъдеще бутоните на този панел ще отговарят за операциите върху слоевете.
Ако имате някакви въпроси в процеса на създаване на контроли или настройка на инициализацията на елементаSimpleOpneGLControl, напомняме ви, чеГлава 2.2 описва процеса на създаване на менюта и ленти с инструменти, аГлава 4.4 описва процеса на настройка на елементи за OpenGL изобразяване и тяхното първоначално изобразяванеинициализация.

С елементите на място, нека се докоснем до кода на обвивката за момент и след това да преминем към създаването на допълнителни класове. Не забравяйте да включите необходимите пространства от имена:

Конструкторът на формуляр изглежда така:

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

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

Тази функция ще съдържа код за инициализация наOpenGL. Кодът му е показан по-долу:

Както можете да видите от кода, ние създаваме2Dортографска проекция и за разлика от кода, който използвахме в предишните глави, тук задаваме проекцията така, че размерът на видимата област в проекцията да бъде равен на размерите на елементаAnT. С други думи,Xкоординатата наAnTелемента ще бъде равна наXкоординатата на видимата част вOpenGLкоординатната система. Но координатната осYе насочена в обратната посока, така че координататаYв координатната системаOpenGLще бъде равна наAnT.HeightY’(Y’е координататаYна елементаAnT).

Кодът за функцията за обработка на събития на таймера и функцията за изобразяване ще изглежда така:

Когато създадохме програмата, която изобразява функционалната графика, вече присвоихме обектаAnTза обработка на събитието за движение на мишката. Тук трябва да се предприемат подобни стъпки.

Функционалният код на манипулатора ще изглежда така:

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

Нека поговорим малко за това, което се случва тук. С инициализацията наOpenGLвъв функциятаForm1_Load, създаването на броячи и присвояването на манипулатори на събития, всичко е ясно (в противен случай не сте следвали предишните глави).

Функцията за обработка на движението на мишката проверява дали левият бутон на мишката е натиснат. Ако да, тогава ще бъде извикан методът за рисуване с четка (Drawing), който допълнително ще имплементираме в класаanEngine. Стойностите на координатите на елементаAnT-XиAnT.Height-Yсе предават на този метод като параметри (тъй като оситеYса насочени в противоположни посоки, вече обсъдихме това в настройките за инициализация наOpenGL).

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

Сега нека да преминем към създаването на "ядрото" или "двигателя" на нашата програма.

4. Създаване на допълнителни класове и техните базови методи.

Добавете нов клас към програмата. Вече направихме това вглава 1.3. Именувайте класаanEngine. За него ще бъде създаден клас мъниче и отделен файл с изходен код, нареченanEngine.cs. Всъщност тук ще поставим още дведопълнителни класове и дори да започнете с тях, за да избегнете объркване. Генерираният код на клас ще изглежда така:

Нека започнем да го актуализираме: първо ще добавим всички пространства от имена, които са във файлаForm1.cs, така че в бъдеще да не се сблъскваме с факта, че което и да е пространство от имена не е включено.

Плъгируеми пространства от имена:

Сега нека преминем към добавяне на още два класа към изходния код:anLayerиanBrush.

Нека започнем сanBrush. Поставете кода за този клас преди кода за заглушката на класаanEngine. Въпреки че няма да бъде много разнообразен: цялата му функционалност ще бъде намалена до съхраняване на маската на четката. Освен това на този етап (създаване на заготовка на всички класове и настройка на тяхната производителност) ще въведем стандартна четка под формата на кръст в конструктора.

anBrushкод на класа:

Както можете да видите, кодът за този клас досега е много прост. Но имаме нужда от него, за да заложим незабавно алгоритъма за рисуване с четка в класаanLayer.

Четката, която задаваме по подразбиране с кода:

По-лесно е да си представите и разберете значението на този код, като разгледате чертежа на получения масив от числа (фиг. 4).

opengl
Фигура 4. Принципът, по който се задава маската на четката. Сега разгледайте класаanLayer. Неговата задача ще бъде да съхранява данните за запълнените пиксели на един слой от изображението. В допълнение към съхраняването на графични данни, слоят съдържа и алгоритъм за рисуване, базиран на избраната четка: когато потребителят се опита да нарисува нещо върху прозореца, като задържи левия бутон на мишката и премести курсора върху елементаAnT, обвивката получава събитие на мишката, което се обработва (координататаYсе коригира) и се предава на екземпляра на класаanEngineс помощта на функциятаDrawing. Вече прегледахме този код.

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

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

Кодът за този клас в момента ще изглежда така:

След това изваждаме от координатата, където трябва да се осъществи рисуването, половината от ширината на четката за остаXи половината от височината за остаY. В този случай можем да получим отрицателни стойности, ако например четката е широка10пиксела, следователно трябва да преместим5пиксела наляво, за да стартираме алгоритъма за рисуване с четка. Но какво ще стане, ако координататаXна точката, в която трябва да се извърши рисуването, е например2? Тогава ще получим отрицателен индекс на елемента от масива, от който трябва да започне рисуването и ще получим грешка.

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

Сега остава да анализираме кода на класаanEngine. В него ще декларираме предварително някои методи, които все още няма да се използват, но определено ще ни бъдат полезни.

anEngineкод на класа:

Както можете да видите, някои функции са декларирани, но не носят никакво функционално натоварване.

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

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

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

Бележки

Ако използвате Visual Studio 2010, тогава трябва да добавите реда:

за да можете да използвате ArrayList във вашето приложение.