Процесори :: Дънни платки :: Видео :: Мултимедия :: Носители :: Периферия
Комуникации :: Софтуер :: Технологии :: Links & Downloads :: Форум
 
 
Въведение в OpenGL
       

Съдържание на материала:

  1. Въведение
  2. GLUT: OpenGL Utility Toolkit
  3. OpenGL - Технологии за напреднали

OpenGL (Open Graphic Library) е една от основните графични библиотеки, използване за създаване на графични приложения или игри. Тя се явява и основния (ако не и единствения) конкурент на DirectX. Предимствата на OpenGL пред DirectX са:

- OpenGL (както показва и името й) е open-source библиотека. На първо място това означава че е тя безплатна
- OpenGL е платформо-независима библиотека - можете да я използвате еднакво добре и под Windows, и под Linux. DirectX е приложима единствено под Windows
- DirectX е лошо-организирана, лошо описана библиотека. Добри ръководства за DirectX се намират изключително трудно. OpenGL - напротив. Тя е чудесно организирана, добре написана и документирана с множество достъпни open-source разработки, библиотеки, изградени на базата на OpenGL за улесняване на някои задачи и др.

Разбира се, OpenGL си има и свойте недостатъци:

- DirectX е значително по-функционална библиотека - OpenGL предоставя функции единствено за определяне на прости фигури, такива като линии и полигони. Съществуват множество допълнителни библиотеки, като GLU (OpenGL Utiliti Library), които улесняват често изпълними задачи, като рендериране на кубове, сфери и конуси. Но все пак функционалността на DirectX е на съвсем друго ниво. С появата на OpenGL 2 вероятно разликата във функционалностите ще намелее значително, но това ще може да се каже единствено, когато излезе финалната версия на OpenGL 2
- OpenGL не предоставя никакви функции за инициализация на прозорец за рендериране или обработка на вход от потребителя. Този проблем лесно се решава чрез използването на допълнителни библиотеки. За всяка операционна система съществува библиотека, придаваща на OpenGL тази функционалност. За Windows това е WGL, за X-Window - GLX. Съществува платформо-независима библиотека, наречена GLUT (OpenGL Utility Toolkit) която предоставя всички необходими функции за инициализация на прозорец и обработка на вход от потребителя.

Под Windows често се използва glaux вместо GLUT. Това изглежда е по-разпространената библиотека за работа под Windos - glaux се разпространява заедно с някои от компилаторите за C и C++. Много от функциите на GLUT и glaux за идентични. В това ръководство ще използвам GLUT за инициализация на прозорец и обработка на вход от потребителя. С glaux обикновено нещата се осъществяват по същия начин, както и с GLUT, с тази разлика, че функциите започват с aux, а не с glut, а също и константите започват с AUX_ а не с GLUT_. Ако все пак някоя функция на GLUT не работи с glaux, проверете документацията на glaux или се снабдете с GLUT - Версията за Windows можете да намерите на адрес http://www.opengl.org/developers/documentation/glut/glut37.zip

- OpenGL базирани библиотеки

Както вече споменахме, съществуват множество библиотеки, изградени на базата на OpenGL и увеличаващи функционалността му. Тук ще разгледаме две от тях. OpenGL Utility Library (GLU), е част от всяка стандартна реализация на OpenGL и съдържа функции, които улесняват изрисуването на комплексни фигури, а също така и такива, улесняващи работата с матрици (за повече информация относно матриците, четете по-нататък). Нейните функции започват с glu. GLUT е платформено-независима библиотека, осигуряваща взаимодействието между OpenGL и операционната система. Тя също предоставя функции за изрисуване на комплексни фигури. Функциите й започват с glut. Функциите на OpenGL започват с gl, завършват с число и буква (или само буква). Буквата указва типа на приеманите аргумент: f - float, d - double, i - integer. Числото, ако присъства, указва броя на аргументите (тъй като много от функциите могат да бъдат извиквани с различен брой аргументи, то различните варианти на една и съща функция ползват число, обозначаващо броя на приеманите аргументи, за да се разграничат от други такива функции и да не се получи грешка от грешен брой аргументи)

Ако присъства буквата v накрая, това означава че функцията приема арумент указател към масив, който съдържа съответния брой стойности и е от съответния тип. Ако в скоби {} са посочени няколко букви или няколко числа, това означава че функцията има няколко разновидности: може да се напише със всяко едно от числата или буквите в скобите. Ако има накрая [v] това означава че е възможно използването на версия на функцията, използваща масив като аргумент

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

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

забележка: всички примери в текущия материал са дадени на C++

Инициализацията на библиотеката става чрез функцията

glutInit(int *argc, char **argv)

Следващите функции се отнасят до инициализация на процореца:

= glutInitDisplayMode определя режима на рендериране - RGBA или Color-Index режим, единичен или двоен буфер, z-буфер. Color-Index режим е режима при 16 или 256 цвята. Характерно за него е наличието на "цветова палитра" - т.е. на всеки един от индексите между 0 и 15 или 0 и 255 съответства точно определен цвят. Възможностите на OpenGL в този режим са силно ограничени, освен това са възможни някои странни ефекти, предизвикани от факта, че на съседни позиции в палитрата има коренно-различни цветове. На функцията се подават няколко GLUT константи, обедидени със символа |. Възможните константи са:

- GLUT_SINGLE или GLUT_DOUBLE за единичен или двоен буфер. Препоръчва се използването на двоен буфер, освен ако не рендерирате нещо наистина просто или ако не рендерирате единично (статично) изображение. Разликате в двата режима е, че при наличието на двоен буфер само единия е видим. Изрисуването се осъществява в невидимия буфер след което двата буфера се разменят
- GLUT_DEPTH определя наличието на z-буфер. Той не е необходим ако няма да рендерирате триизмерна графика
- GLUT_RGBA определя използването на RGBA режим. Препоръчвам винаги да ползвате този режим

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

glutInitDisplayMode(GLUT_DOUBLE | GLUT_DEPTH | GLUT_RGBA);

= glutInitWindowPosition приема два аргумента и определя позицията на прозореца на екрана. Първия аргумент определя x-координатата на горния ляв ъгъл на прозореца, втория - неговата y-координата. Координатите се отчитат спрямо горния ляв ъгъл на екрана

glutInitWindowSize отново приема два аргумента - ширина и височина на прозореца

= glutCreateWindow(char *string) създава прозореца (трябва да се има предвид, че той не се изрисува преди да бъде извикана функцията glutMainLoop())

GLUT предоставя няколко функции, които приемат като аргумент име на функция. Тези функции създават обработката на събитията, като определят функция, която да се извиква при съответното събитие. Най-важната от тях е glutDisplayFunc. Тя определя коя функция да се извиква, когато трябва да се изрисува нещо на екрана. Функцията трябва да бъде от вида void functionname()

= glutReshapeFunc определя функция, която се извиква, когато прозорецът промени размера си. Функцията трябва да бъде от вида void functionname(int h, int w), като h и w определят съответно височината и ширината на прозореца

= glutKeyboardFunc определя функция, която се извиква, когато потребителя натисне клавиш от клавиатурата. Функцията трява да има вида void functionname(unsignet char key, int x, int y)

= glutMouseFunc определя функцията, която да се извика ако бутона на мишката бъде натиснат или отпуснат. Функцията трябва да има вида void functionname(int button, int state, int x, int y)

= glutMotionFunc определя функция, която се извиква, която да се извиква, когато мишката се премества докато нейния бутон все още е натиснат. Функцията трябва да има вида void functionname(int x, int y)

= glutIdleFunc определя функцията, която да се извиква ако не се получава нито едно събитие. Функцията трябва да има вида void functionname()

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

-Прости фигури

OpenGL предоставя функции за рендериране единствено на прости фигури - точки, линии и полигони. Останалите функции на OpenGL са за определяне на осветление, текстури, криви, както и за вида на изобразяваните линии и полигони. В OpenGL дефинирането на една проста фигура се осъщесвява чрез определяне на нейните върхове. За всеки връх може да се определи За всеки един връх може да се определи множество различна информация - неговия цвят, полупрозрачност, нормала. Всички върхове се определят между командите glBegin() и glEnd() указващи съответно начало и край на изрисуване. Всеки връх се изрисува с текущия цвят за рисуване и текущата нормала. Нормалата е векторът, перпендикулярен на равнината - OpenGL го използва за определяне на осветлението. т.е. нормалата определя как е наклонена една повърхност. Определянето на цвят за изрисуване се определя чрез следните функции:

За RGBA режим това е:

glColor{34}f[v](R, G, B [A])

Тази функция може да приеме три или четири аргумента, които са или числа с плаваща запетая (float) или указател към масив, съдържащ съответно 3 или 4 стойности с плаваща запетая. Стойностите за R, G и B (а също и за A ако присъства трябва да са в интервала 0.0 (0%) и 1.0 (100% наситеност). А идва от Alpha и се използва за определяне на прозрачността в този връх.

Определянето на цвят в Color-Index режим става чрез функцията

glIndex(index)

Като единствена стойност се подава индекса на желания цвят. Отново казвам, в този режим възможностите са твърде ограничени. Функцията glNormal3f[v](x, y, z) определя вектора на нормалата. x,y,z са координатите на края на вектора - неговото начало съвпада с върха
Една точка се определя от нейните координати - x,y,z и w. Четвъртата координата има една специална функция - първите три се делят на нея. т.е. координатите на една точка реално са x/w, y/w, z/w. По подразбиране z е 0, а w - 1. Определянето на координатите на връх става чрез командата:

glVertex{234}f[v](x,y,z,w)

Така се определят точките. Как се интерпретират определя параметърът, подаден към glBegin(). Възможностите са:

= GL_POINTS - точките остават такива, без да се свързват
= GL_LINES - точките се свързват две по две в линии. Ако са нечетно количество, последната се игнорира
= GL_LINE_STRIP - точките се свързват във верига от линии, като първата се свързва с втората, втората - със третата, третата - с четвъртата и т.н.
= GL_LINE_LOOP - същото като предишното, с тази разлика че последната точка се свързва с първата
= GL_TRIANGLES - точките се свързват три по три в триъгълници. Ако броят им не е кратен на 3, последнита една или две се игнорират
= GL_TRIANGLE_STRIP - точките се свързват в серия от триъгълници, като се свързват точки 1, 2 и 3, след това 3,2,4, след това 3,4,5 и т.н.
= GL_TRIANGLE_FAN - същото като предишното, с изключение че се свързват 1,2,3, след това - 1,3,4, след това 1,4,5 и т.н.
= GL_QUADS - точките се свързват в отделни четириъгълници,. Ако броят им не е кратен на четири, последните една, две или три се игнорират
= GL_QUAD_STRIP - точките се свързват в серия от четириъгълници, свързани един с друг.
= GL_POLYGON точките се свързват в полигон, като той трябва да е изпъкнал и не бива една линия от полигона да пресича друга линия от същия полигон - в противен случай резултата е непредсказуем.

- Изрисуване

Преди да започнете да рисувате, вероятно ще искате да изчистите буфера от предишното изображение. Tова се осъществява чрез функцията glClear() която приема като аргументи буферите, които трябва да се почистят. Отделние буфери се отделят със символа |; параметъра за цветовия буфер е GL_COLOR_BUFFER_BIT, а за z-буфера - GL_DEPTH_BUFFER_BIT. Чрез функцията glClearColor() можете да определите цвят, с кйто да се запълват всички пиксели на буфера при неговото изчистване. Това ще е и цвета на простаството, незаето от други изображения. По подразбиране е черно.

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

OpenGL предлага усъвършенстван контрол над вида на полигоните. Преди всичко можете да определите дебелина и вид на линията - не е задължително да ползвате непрекъсната линия. По-интересна е възможността за смесване на цветовете. По подразбиране полигона (както и отделната линия) е едноцветен, независимо че може различни върхове да има различн цвят. Можете обаче да направите така, че цвета да прелива между отделните върхове. Ако създадете например една линия, точката определяща единия край на която е черна, а другата бяла, и определите преливане, то вие ще поличите линия в градация на сивото, крайните точки на които са черно и бяло. Определянето на преливане става чрез извикване на функцията glShadeModel() с параметър GL_SMOOTH. Ако я извикате с GL_FLAT ще получите ефекта по подразбиране - едноцветни полигони.

По подразбиране всички точки са с размер 1 пиксел, всички линии са широки 1 пиксел. Можете да промените тези параметри, както и да създадете прекъсната, пунктирна линия или полигон, запълнен с някакви шрихи.

= glPointSize(size) определя размера на точката в пиксели. Обърнете внимание че size 2 определя точка с размер 4 пиксела - квадрат със страна 2 пиксела.

= glLineWidth(width) определя ширината на линията в пиксели.

= glLineStipple(factor, pattern) - определя прекъсната линия. pattern определя вида на "накъсването" - това са 16 бита (обикновено дадени в шенсайсетичен вид), като единица означава има точка, нула - няма точка. По този начин можете да определите линия от точки, тирета или комбинация от точки и тирета. factor определя колко пъти да се повтори всеки един от битовете преди да се премине към следващия. Така можете да определите 16 пиксела линия с 4 пиксела прекъсване, като определите за factor 4 и за pattern 0111101111011110. За да използвате функцията, трябва да подадете GL_LINE_STRIPLE на glEnable().

= glPolygonMode(face, mode) определя начина, по който се изрисуват полигоните. За face можете да определите GL_FRONT (предната част), GL_BACK (задната част) или GL_FRONT_AND_BACK (и двете части). За mode можете да определите GL_POINT (изобразява само върховете), GL_LINE (изобразява само линиите) или GL_FILL (изобразява запълнен полигон).

Подобно на линиите, и в полигоните можете да определяте начин на запълване. Това става чрез определяне на масив от 32x32 бита, като единицата се интерпретира като оцветяване на пиксела, нулата - пиксела остава непроменен. С тази картина 32x32 пиксела се запълва целия полигон. За да използвате тази функция, първо трябва да я разрешите като подадете GL_POLYGON_STRIPPLE на glEnable. След това трябва да създадете масив и да го подадете като аргумент на функцията glPolygonStripple()

Тъй като правоъгълника е често срещан, OpenGL предоставя специална функция:

glRectf(x1, x2, y1, y2) изрисува правоъгълник, с координати на горния ляв ъгъл (x1,y1) и долен десен ъгъл с координати (x2,y2)

-Осветление

Едно от основните неща, които прави триизмерните изображения да изглеждат триизмерни, е осветлението. OpenGL приема дефиниция на осветлението и дефиниция на материала, от който са изградени обектите и поема всички изчисления по създаването на реалистични ефекти. т.е основнта работа на OpenGL е да изчисли цвета на всеки един пиксел на базата на някакви параметри. OpenGL позволява да се определят до 8 различни източника на светлина. За да използвате осветление обаче, първо трябва да извикате функцията glEnable() с аргумент GL_LIGHTING. Отново с glEnable() трябва да се включи проверката на разстоянията (като се подаде GL_DEPTH_TEST). Тази проверка трябва да се включва винаги, когато се създава 3D пространство, за да бъдат изобразявани коректно обектите един спрямо друг.

Определянето на осветлението има две основни части - определяне на източници на светлина и определяне на материал, от който е изграден всеки обект. Определянето на източник на светлина става чрез дефиниране на позицията му, цвят и начин на излъчване; определянето на материала става чрез дефиниране на това, какво количество от постъпилата червена, зелена или синя (R, G или B компонента в един RGB цвят) светлина се отразява и как се отразява. Ще разгледаме последователно двата етапа:

В OpenGL съществуват три основни типа светлина:

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

Diffuse е светлина, която идва от определена посока и се отразява във всички посоки. Отражението на Diffuse и Ambient определя цвета на обекта.

Specular е светлина, идваща от определена посока и отразяваща се в определена посока - светлинния блясък на една сфера е пример за такава светлина.

Освен това за материалите съществува още един тип светлина - Emisive Това е светлина, която се излъчва от тях. Трябва да се отбележи, че това променя само вида на обекта, но не предизвиква излъчване на светлина в околната среда. Подобен ефект може да се комбинира с поставянето на светлинен източник на същото място за да се направи източник на светлина, например лампа.

Създаването на светлинен източник става чрез функцията:

glLightf{v}(name, pname, param)

където name е един от осемте светлинни източници, съответно GL_LIGHT0, GL_LIGHT1, GL_LIGHT2, GL_LIGHT3, GL_LIGHT4, GL_LIGHT5, GL_LIGHT6 или GL_LIGHT7. Източника се включва чрез glEnable(name) и се изключва чрез glDisable(name) pname е параметърът, който се определя за дадена светлина (могат да се направят няколко обръщения към glLightfv() за определяне на няколко различни параметъра, като се задава един и същи светлинен източник в name), а param е съответната стойност. С невекторния вид на функцията могат да се определят само тези параметри, които имат само една стойност. В таблицата по-долу са дадени параметрите, приеманите от тях стойности и тяхното значение pname param дефиниция :
name param какво прави

= GL_AMBIENT R,G,B,A Определя интензцивността на Ambient излъчването на източника със съответната RGBA стойност
= GL_DIFFUSE R,G,B,A Определя интензивността на Diffuse излъчването на източника със съответната RGBA стойност
= CL_SPECURAL R,G,B,A Определя интензивността на Specular излъчването на източника със съответната RGBA стойност
= GL_POSITION x,y,z,w Определя координатите на източника на светлина. Ако w е нула, източника е на безкрайно разстояние и координатите определят посоката, от която идва светлината
= GL_SPOT_DIRECTION x,y,z Посока на разпространение на светлината
= GL_SPOT_EXPONENT число Фокус на светлината (колкото е по-голям, толкова е по-ярка в центъра и по-слаба по краищата на конуса)
= GL_SPOT_CUTOFF ъгъл Определя ъгъла между оста X на координатната система и края на (0-90) конуса, в който се разпространява светлината. Ако е 180, светлината не се разпростанява в определена посока и няма конус. При тази стойност светлината се разпространява във всички посоки и предишните два параметъра не са в сила

-Определяне на материала

Определелянето на материал става с функцията:

glMaterialfv(face, pname, params)

Където face е една от константите GL_BACK, GL_FONT или GL_FRONT_AND_BACK, които определят съответно дефиниране на материала на предната страна, на задната страна или и на двете едновременно. Рname е параметърът, който определяме (отново както при осветлението можем да определим множество параметри чрез поредни извиквания на функцията), а params е масив, съдържащ съответните стойности. Параметрите, с които можете да извикате функцията, и съответните стойности са, както следва:

Параметър Стойност Дефиниция

= GL_AMBIENT RGBA определя количеството отразена светлина тип AMBIENT
= GL_DIFFUSE RGBA определя количеството отразена светлина тип DIFFUSE
= GL_SPECULAR RGBA определя количеството отразена светлина тип SPECULAR
= GL_EMISSION RGBA определя излъчвана от обекта светлина
= GL_SHININESS RGBA определя цвета на най-яркото отражение (светлинно петно)
= GL_AMBIENT_AND_DIFFUSE RGBA определя едновременно еднакъв цвят за AMBIENT и
= DIFFUSE - обикновено двете стойности са равни, освен ако не искате да постигнете някакъв много специален ефект

-Преобразования на матрицата: ротация, транслация и мащабиране; проекции

Може да звучи сложно, но не е. За тези които знаят какво е матрица ще спомена само, че в OpenGL се използват матрици 4x4. За тези които не знаят - да не се притесняват. Не е необходимо да учат висша математика, за да ползват OpenGL, трябва само да разберат няколко основни концепции. Аз също не бих могъл да работя с матрици извън употребата им в OpenGL. Така че няколко основни неща за матриците:

- В OpenGL съществуват три матрици и три различни типа трансформации: MODELVIEW, PROJECTION и TEXTURE. Последната се отнася за текстурите, тук ще разгледаме първите две матрици
- Когато направите някаква трансформация на матрица това няма да се отрази на изрисуването на екрана. Вместо това засегнати ще са бъдещите изрисувания
- В най-общ аспект преобразованията на матрицата променят координатната система
- Можете да модифицирате само една от матриците в един и същи момент. За да промените няколко матрици, трябва да използвате съответната функция за промяна на текущата матрица
- Всяка матрица има така наречената първична матрица, която е началната матрица. За да направите преобразования относно нея, а не относно последната матрица, трябва да извикате функцията glLoadIdentity()

Последното сигурно звучи доста сложно. За какво става въпрос. Ако определите ротация от 30 градуса, а след това от 60 - резултата ще е матрица, завъртяна на 90 градуса. Ако вместо това искате да я завъртите на 60 градуса, след като е била завъртяна на 30, трябва да извикате функцията glLoadIdentity(). Тази функция отменя всички текущи промени над матрицата.

- Матриците често се запазват стек. Ако не знаете как се работи със стекове, имайте предвид следното: Възможни са две действия: поставяне в стека и изваждане. Когато поставяте нещо, вие го поставяте най-отгоре в стека. Можете да извадите от стека само най-горната матрица. Стекът е изключително полезен за запазване на някакви трансформации преди да се извика glLoadIdentity() и да се направят нови промени за да може след това да се възстанови предишната матрица. Поставянето в стека става чрез функцията glPushMatrix(), а изваждането - чрез glPopMatrix()

Нека сега разгледаме трансформациите поотделно:

Определянето на матрица за промяна се осъществява чрез функцията glMatrixMode() кояро приема като аргумент една от константите GL_MODELVIEW (за MODELVIEW матрица) GL_PROJECTION (за PROJECTION матрица) и GL_TEXTURE (за TEXTURE матрица). Чрез MODELVIEW матрица се определят позицията на камерата, ротация, транслация и мащабиране. Чрез PROJECTION матрица може да се определи ъгълът на виждане. Освен това може да се определи пространството за изрисуване, но тази функция не се нуждае от матрица.

-Определяне на прозорец за изрисуване

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

glViewPort(x, y, width, height)

x и y определят координатите на долния ляв ъгъл на прозореца, а width и height съответно неговата ширина и височина

= MODELVIEW матрица

Тази матрица ви позволява да завъртите (ротация), преместите (транслация) или промените размеите (мащабиране) на обекта, а също да определите позиция и посока на камерата.

glTranslatef(x, y, z) премества сцената със съответните стойности по x, y и z координатите

glRotatef(angle, x, y, z) завърта сцената на определен ъгъл. Сцената се завърта по x координата на ъгъл angle*x, по y - на angle*y и по z координатата - на angle*z. Т.е. ако искате да завъртите сцената на 30 градуса по y координатата, трябва да използвате функция glRotatef(30, 0.0, 1.0, 0.0);

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

За да определите позиция и посока на камерата, най-лесно е да използвате функцията

gluLookAt(posx, posy, posz, tarx, tary, tarz, upx, upy, upz)

Обърнете внимание, че тази функция е част от GL Utility library. Тя приема девет аргумента - три групи по три аргумента, определящи съответно x, y и z координатите на позицията на камерата, на точката, към която е насочена, и на върха на вектора (основата му съвпада с позицията на камерата), определящ кое е "горе". Т.е. можете да завъртите камерата "с главата надолу" като определите за upy стойност, по-ниска от posy.

= PROJECTION матрица

Тази матрица определя единствено видимото пространство. Функцията, която осъществява това е glOrtho(). Приема шест аргумента: разстояние в ляво, в дясно, надолу, нагоре, както и "далече" и "близо". Така се образува един куб, в който се намира видимото пространство. Всичко извън този куб.

 

GLUT: OpenGL Utility Toolkit

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

Текущия материал представя най-полезните от функциите на GLUT. Макар да е разгледана голяма част от библиотеката, материала не претендира за изчерпателност. За пълно описание на функциите на библиотеката, проверете GLUT 3 Specification, достъпна на http://www.opengl.org

1. Инициализация на прозорец

Едно от нещата, които не могат да се направят с OpenGL, е инициализацията на прозорец за рендериране. Такъв прозорец обаче е необходим за да бъде изведено каквото и да е на екрана. OpenGL не предоставя функции за инициализация н прозорец или обработка на вход от потребителя за да бъде платформо-независима. За всяка операционна система съществува съответната библиотека, разширяваща функционалността на OpenGL, предоставяйки необходимите функции за инициализация на прозорец. Същото може и да се извърши и чрез ивикване на функциите на операционната система, но целта е да се спести това на потребителя, като по този начин се позволи OpenGL да се използва и от програмисти, които не познават добре системните функции на съответната операционна система. И все пак използването на различни библиотеки прави кода непреносим - или поне не и без редактирането му, така че да ползва функциите на друга библиотека. За да бъде направен кода преносим е създадена платформо-независима библиотека, скриваща извикването на различните системни функции и предоставяща универсални функции за инициализация и манипулация на прозорец, както и за обработване на събития, постъпващи от клавиатурата и мишката. Тази библиотека се нарича GLUT (OpenGL Utility Toolkit). Библиотеката предоставя следните функции за инициализация на прозорец:

- void glutInit(int *argcp, char **argv) - инициализира самата библиотека, като обработва всички command-line параметри (първия аргумент съдържа техния брой, втория самите аргументи)

- void glutInitWindowPosition(int x, int y) определя позицията на прозореца на екрана. Ако аргументити и са -1,-1 то позицията на прозореца се определя от операционната система

- void glutInitWindowSize(int width, int height) определя ширината (width) и височината (height) на прозореца. Аргументите трябва да са числа, по-големи от 0

- void glutInitDisplayMode(unsigned int mode) - определя режима, в който работи прозореца (за целта се използват поредица константи, дефинирани от самата библиотека, разделени с логическото "или" - |

- GLUT_RGBA указва използването на RGBA режим (който е по подразбиране), докато GLUT_INDEX създава прозорец в Color-Index режим (256 цвята)

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

- GLUT_DEPTH определяне използването на z-буфер от прозореца

- GLUT_STENCIL определя използването на Stencil буфер от прозореца

- GLUT_ACCUM определя използването на Accimulation буфер от прозореца

- glutCreateWindow(char *name) - създава прозорец. Единствения му аргумент определя името на прозореца. Макар и функцията да създава прозорец, той няма да бъде показан преди извикването на glutMainLoop(), разгледана малко по-нататък.
- void glutDestroyWindow(int win) унищожава прозорец, определен от идентификатора win.

- int glutGetWindow(void) връща числовия идентификатор на текущия прозорец

- void glutPostRedisplay(void) - указва на операционната система, че текущия прозорец трябва да бъде изрисуван наново, което води до извикване на функцията glutDisplayFunc(). Използвайте glutPostRedisplay() винаги, когато променяте съдържанието на прозореца извън неговата функция за рендериране (определена от glutDisplayFuns() - това ще бъде разгледано малко по-нататък)

- void glutSwapBuffers(void) е функция, използвана при работа с двоен буфер. Тъй като двойния буфер (или double buffering) се осъществява чрез изрисуването на невидим екран и разменянето на видимия и невидимия екран (т.е. невидимия става видим за да се покаже изображението), е необходим начин за размяна на местата на двата екрана. Именно това прави тази функции. Използвайте я винаги, когато сте в режим на двоен буфер и сте приключили с изрисуването.

- void glutPositionWindow(int x, int y) променя позицията на прозореца, поставяйки го на позиция, определена от подадените аргументи

- void glutReshapeWindow(int width, int height) променя размерите на прозореца. Първия аргумент определя неговата ширина (width), а втория - неговата височина (height)

- void glutFullScreen(void) е изключително полезна функция, тъй като тя изисква от операционната система да покаже съдържанието на текущия прозорец във fullscreen режим

- void glutSetWindowTitle(char *name) определя заглавие на прозореца, което да бъде изписано в неговата заглавна лента (titlebar). Заглавието се определя от подадения аргумент

- void glutSerCursor(int cursor) променя курсора на мишката. Този курсор се ползва само докато курсора е в рамките на прозореца. Използват се поредица дефинирани от библиотеката константи, някои от по-интересните от които са описани по-нататък. За пълен списък на възможните константи, проверете GLUT Specification.

- GLUT_CURSOR_RIGHT_ARROW - стрелка надясно
- GLUT_CURSOR_LEFT_ARROW - стрелка нялво
- GLUT_CURSOR_INFO - ръка с изпънат показалец и свити останалите пръсти
- GLUT_CURSOR_CROSSHAIR - ръка (използвна обикновено като курсор над хиперлинкове)
- GLUT_CURSOR_HELP - стрелка с въпросителна
- GLUT_CURSOR_WAIT - пясъчен часовник
- GLUT_CURSOR_TEXT - курсора, използван често в текстовите редактори за обознаначаване на полето, в което може да се въвежда текст
- GLUT_CURSOR_NONE - невидим курсор
- GLUT_CURSOR_DESTROY - череп с кръстосани кости

Точните изображения, ползвани като курсор, се определят от операционната система

2. Създаване на меню

GLUT предоставя функции, позволяващи създаването на различни контекстни менюта за програмата.
int glutCreateMenu(void *(func)(int value)) създава ново меню, като връща неговия идентификационен номер и определя функция, която да се извиква, когато бъде избран елемент от менюто. Функцията трябва да има вида void func(int value) като подадения й аргумент е идентификационния номер на избрания елемент от менюто.
void glutAddMenuEntry(char *name, int value) добавя елемент към менюто. Първия аргумент определя текста, който да се показва; втория определя идентификационен номер на елемента, който да се предаде към обработващата функция, ако елемента бъде избран.

- void glutSetMenu(int menu) определя текущото контекстно меню. Единствения параметър е идентификационния номер на менюто

- int glutGetMenu(void) връща идентификационния номер на текущото контекстно меню

- void glutDestroyMenu(int menu) унищожава менюто, чийто идентификационнен номер е подаден кто параметър

- void glutAddSubMenu(char *name, int menu) добавя разширяващо се меню към текущото. name определя текста, който да бъде изписан; ако курсора се задържи над елемента ще се покаже подменю, определено от идентификатора menu.

Трябва първо да създадете контекстно меню за да можете после да го добавите към друго като подменю.
void glutRemoveMenuItem(int entry) премахва елемент от менюто с пореден номер entry (първия елемент има индекс 1). Броя на елементите в менюто можете да получите чрез функцията glutGet(GLUT_MENU_NUM_ITEMS)
void glutAttachMenu(int button) асоциира текущото контекстно меню с бутон на мишката, определен от аргумента button. Стойността на аргумента може да е GLUT_LEFT_BUTTON, GLUT_MIDDLE_BUTTON или GLUT_RIGHT_BUTTON.

- void glutDeachMenu(int button) премахва асоциацията на подадения като аргумент бутон със съответстващото му меню.

3. Обработка на събития

GLUT предоставя редица функции за обработка на събития. Общото за тези функции е, че те приемат ккато аргумент функция, която бива извикана ако дадено събития възникне. Тези функции обаче нямат да имат ефект преди извикаването на glutMainLoop(), която стартира безкраен цикъл, използван за обработката на събития.

- void glutDisplayFunc(void (*func)(void)) определя функция, която да се извиква, когато трябва да се изрисува нещо в прозореца (в това число при първоначално изрисуване на прозореца)

- void glutReshapeFunc(void (*func)(int width, int height)) определя фунция, която се извиква ако размерите на прозореца бъдт променени. На фунцкията се подават два параметъра: новата ширина (width) и новата височина (height) на прозореца.

- void glutKeyboardFunc(void (*func)(unsigned char key, int x, int y)) определя функция, която да се извика ако бъде натиснат клавиш от клавиатурата. На функцията се подават три аргумента - ASCII кода на натиснатия клавиш (key) и координатите на курсора на мишката (x и y)

- void glutMouseFunc (void (*func)(int button, int state, int x, int y)) определя функция, която да се извиква при натискане или отпускане на бутон на мишката. Първия от подадените елементи е натиснатия (или отпуснатия) бутон и може да е една от константите GLUT_LEFT_BUTTON (ляв бутон), GLUT_MIDDLE_BUTTON (среден бутон) или GLUT_RIGHT_BUTTON (десен бутон). Аргумента state определя дали бутона на мишката е натиснат (GLUT_DOWN) или отпуснат (GLUT_UP). Параметрите x и y определят координатите на курсора на мишката. Ако със съответния бутон е асоциирано контектно меню, за него няма да бъде извикана обработващата функция.

- void glutMotionFunc(void (*func)(int x, int y)) определя функция, която да се извиква, ако мишката се премести докато някой от бутоните й е натиснат. Подадените й параметри съдържат x и y координатите на курсора на мишката

- void glutPassiveMotionFunc(void (*func)(int x, int y)) определя функция, която се извиква, ако мишката се премести, когато няма натиснат бутон. Подадените й параметри съдържат координатите на курсора на мишката.

- void glutEntryFunc(void (*func)(int state)) определя функция, която да се извиква ако курсора напусне прозореца или се върне в него. Подадения й параметър указва кое от двете действия е възникнало и е една от двете константи GLUT_LEFT (курсора е напуснела прозореца) или GLUT_ENTERED (курсора се е върнал в прозореца)
void glutIdleFunc(void (*func)(void)) определя функция, която се извиква, ако никоя от по-рано описаните фунцкии не бъде извикана, т.е. ако няма възникнали събития. Това е изключително полезно при създаване на анимация.

- void glutTimerFunc(unsigned int msecs, void (*func)(int value), int passvalue) определя функция, която да се извиква на определен интервал от време. msecs определя интервала в милисекунди. Функцията трябва да приема един аргумент от тип int; passvalue се подава като аргумент на функцията.

И така, GLUT. Тя е безплатна, платформонезависима библиотека с отворен код. Позволява използването на голямо количество възможности. В този кратък материал съм се постарал да ви обясня и представя основните й функции, ако съм ви заинтригувал, можете да се прехвърлите на http://www.opengl.org и да видите спецификациите на GLUT.

 

OpenGL - Технологии за напреднали

1.Полупрозрачност
2.Мъгла
3.Display List
4.Изображения
5.Текстури

1. Полупрозрачност

Цветовете в OpenGL (когато не си използва Color-Index режим) имат четири компонента, отговарящи на четирите елемента на RGBA модела. Именно четвъртата стойност се използва за създаване на полупрозрачност, известна още като Alpha blending. Това е технология при която цветовете на предния и задния обект се сливат, за да се получи един среден цвят, създаващ ефект на прозрачност на прендния обект. Тази прозрачност може да варира в различни степени, като позволява създаването на ефект от ползването на цветни стъкла. За да има ефект четвъртата стойност на цвета обаче, трябва първо да разрешите използването на alpha blending, което по подразбиране е изключено (тъй като допълнително натоварва процесора). Това става чрез подаване на GL_BLEND нафункцията glEnable(). Слеващото нещо, което трябва да направите, е определяне на алгоритъм за смесване на цветовете. Това става чрез извикване на функцията glBlendFunc(), приемаща два аргумента, първия от които определя как се изменя цвета на предния обект, а втория - на задния. Възможните стойности на първия аргумент са:

- GL_ZERO - цвета на обекта въобще не се взима предвид
- GL_ONE - използва се цвета на обекта
- GL_DST_COLOR - цвета на ибекта се умножава по този на задния обект
- GL_ONE_MINUS_DST_COLOR - цвета на обекта се умножава с (1,1,1,1 минус цвета на задния обект)
- GL_SRC_ALPHA - цвета на обекта се умножава с неговата четвърта (Alpha) стойност (т.е. първите три стойности се умножават по четвъртата)
- GL_ONE_MINUS_SRC_ALPHA - цвета на обекта се умножава с 1 минус цвета на четвуртата му стойност
- GL_DST_ALPHA - цвета на обекта се умножава с Alpha компонентата на цвета на задния обект
- GL_ONE_MINUS_DST_ALPHA - цвета на обекта се умножава с 1 минус четвъртата стойност на цвета на задния обект.

За втори параметър се използват същите константи, с тази разлика че вместо GL_DST_COLOR се използва GL_SRC_COLOR (и съответно цвета се умножава по този на предния обект)
Звучи сложно, нали? Това определя начина на смесване на цветовете, а наличието на толкова различни възможности позволява създаването на различни ефекти. Но на практика много малко от комбинациите се използват.

Стандартния вид на функция за използване на прозрачност е

glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA)

като тя се извиква само за предния обект (за задния GL_BLENDING трябва да е забранен)
Обърнете внимание, че всеки един от компоннетите на RGBA цвета приема стойност от 0 до 1. Така извиканата функция ще умножи първите три компонента с четвъртия и след това ще добави цвета на задния обект умножен по 1-четвъртия компонент на обекта. Така резултата ще е изображение, наполовина съставено от предното, наполовина от задното. Ако не разбирате тази математика - не е и необходимо. Така извиканата функция ще създаде необходимия ефект; трябва само да знаете че колкото по-ниска е стойността на четвъртия компонент на цвета, толкова по-прозрачен е обекта.

Важно е да се отбележи, че реда на изрисуване на обектите има значение. Винаги трябва да изрисувате задния обект преди предния, за да получите необходимия обект.

2.Мъгла

OpenGL предоставя една функция, чрез която можете да създадете ефейт на мъгла или просто да направите по-далечните обекти по-бледи, за да получите реалистични ефекти. Функцията е glFog(), и има три разновидности. Затова първо трябва да определите типа "мъгла", който ще използвате, а след това и нейния цвят. За целта трябва да извикате glFogi() с два параметъра (glFog() винаги се извиква с два параметъра), първия от които GL_FOG_MODE, указващ че следва определянето на режим на работа на функцията, а втория - една от три възможни стойности. Това всъщност са три различни алгоритъма, по които OpenGL ще изчислява замъгляването на обектите на базата на тяхното разстояние от камерата, които се използват за постигане на различни типове ефекти.

Ако искате просто по-далечните обекти да са по-тъмни, а по-близките - по-ярки, то необходимия режим е GL_LINEAR. GL_EXP се използва за мъгла и облаци, докато GL_EXP2 е подходящ за много силни замъглявания, като например дим или замъгляване около слънцето. След като е избран тип на замъгляването, трябва да се определи неговия цвят. За GL_LINEAR това обикновено е цвета на фона, но не е задължително.

Тъй като glFog() приема само два аргумента, ще трябва да й подадете като аргумент масив от четири стойности, отговарящи на RGBA цвета. Първия аргумент на glFogfv() трябва да е GL_FOG_COLOR, указващ определяне цвят на мъглата.

Когато използвате GL_FOG_EXT или GL_FOG_EXT2 можете да определите и плътност на мъглата. Това се осъществява чрез извикване на функцията glFоgf() с първи параметър GL_FOG_DENSITY и втори - желаната плътност.Можете също така да определите начална и крайна дълбочина, за която да се изчислява замъгляване. Често пространството непосредствено преди камерата се оставя без мъгла за постигане на по-добри резултати. Това се осъществява чрез подаване на GL_FOG_START или GL_FOG_END и съответна стойност на Z-координатата, която да се смята за начало или край на изчислението на мъглата, на функцията glFogf(). Но за да използвате функцията glFog() (или по точно за да има ефект нейното използване, първо трябва да разрешите използването на мъгла, подавайки GL_FOG като параметър на функцията glEnable().

3. Display list

OpenGLпредоставя технология, наречена Display List. Чрез нея можете да съхраните поредица от команди и да ги изпълните отново по-късно. Това, което прави Display List толкова важен е, че не самите команди се съхраняват, а резултата от тях. Което означава, че вашата програма трябва само веднъж да направи изчисленията и след това може да ползва готовите стойности, вместо да пре-изчислява всеки път. Това ускорява значително процеса, но си има своята цена. След като веднъж е бил създаден Display List, той не може да бъде променян. Разбира се, можете да създадете толкова списъка, колкото си поискате.

Самия Display List се идентифицира по номер. Всеки списък трябва да има свой собствен уникален номер. Списъкът се създава чрез командите glNewList() и glEndList() - между тях може да се съдържа произволен код и всичко преди glEndList() става част от списъка. glEndList() не приема аргументи; glNewList() обаче приема два. Първия е номера на списъка, който трябва да е уникален. Втория е една от константите GL_COMPILE (която указва на OpenGL само да създаде списъка) или GL_COMPILE_AND_EXECUTE (която указва на OpenGL да компилира и след това да изпълни командите от списъка). Можете да получите свободен идентификатор за списък, използвайки функцията glGenLists(), която приема един аргумент - брой идентификатори, които да резервира (т.е. да отбележи като използвани и следователно да не ги връща при следващи извиквания на функцията). glGenLists връща номера на първия свободен списък. т.е. ако извикате glGenLists(5) и тя върне 35, можете да използвате за създаване на списъци идентификаторите 35, 36, 37, 38 и 39. След като веднъж сте създали списък, можете да го извикате чрез командата glCallList(), която приема като единствен аргумент номер на списъка, който искате да изпълните. Можете също така да извикате последователно множество списъци с една единствена команда - glCallLists(). Тя приема три аргумента - брой на елементите в масива, тип на данните в масива и масив от идентификатори на списъци. Можете също така да изтриете списък чрез командата glDeleteLists(), която приема два аргумента - номер на първия списък за изтриване и количество списъци, които да бъдат изтрити. Така можете да изтриете няколко поредни списъка. Изтриването на списък, който повече няма да се използва е важно, защото освобождава паметта, използвана от този списък.

4. Използване на изображения

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

Ще вземем за начало най-простия пример - едно черно-бяло изображение. То има ширина WIDTH и височина HEIGHT - съответно WIDTHxHEIGHT пиксела. Можете да създадете масив с WIDTHxHEIGHT елемента, в който всеки елемент е 1 или 0. 1 съответно означава има цвят - 0 няма цвят. Точно това всъщност прави командата glBitmap() - приема масив от единици и нули и навсякъде, където се среща 1, оцветява в пиксела в текущия цвят за запълване. Тази технология се използва не толкова за създаване на двуцветни изображения, колкото за създаване на собствени шрифтове. Всичко което трябва да направите е да създадете по един масив - карта на пикселите - за всяка една буква и да накарате програмата да ги изведе в определен ред. Така екрана ще се оцвети само там, където има единица в масива и ще се изведе исканото съобщение :)

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

glRasterPos2f(x,y)

която определя координатите x и y, на които ще бъде изрисувано изображението.
Накрая остава извикването на glBitmap(), приемаща 7 аргумента. Първите два определят ширината и височината на изображението, следващите два - x и y координатите (спрямо самото изображение), които да се използват за определяне на местоположението на изображението, следващите два определят с колко да се премести позицията (определена от glRasterPos2f()) на изображението по x и по y за да се използват за следващо извикване на функцията и последния - масив, съдържащ самия Bitmap (в терминологията на OpenGL bitmap е масив от единици и нули, а това, което в windows се нарича bitmap е pixmap). Едно пояснение за втората двойка аргументи: glRasterPos2f() определя една точка, използвана за позиционирането на изображението. Втората двойка аргументи на glBitmap() определят коя точка от изображението да лежи на точката, определена от glRasterPos2f(). Обикновено се използва или 0,0 - горния ляв ъгъл, или центъра на изображението. Важно е да отбележим едно нещо - или трябва да извикате glBitnap() с отрицателна височина, или да дефинирате изображението с главата надолу (тъй като при извикване на glBitmap() изображението се рендерира, започвайки от долния ляв ъгъл и продължавайки нагоре) .

Но как да изведем пълноцветно изображение на екрана? Това не е много по-трудно. Отново имате нужда от масив, съдържащ вече цвета на всеки пиксел, и функцията glDrawPixels. Но докато създаването на масив от единици и нули за представянето на просто изображение може да не е много трудна задача, какво ще кажете за представянето на снимка като масив от RGB стойности? Едно е да създадете изображението с Photoshop, Paint Shop Pro или друго графично приложение, но OpenGL няма как да го използва пряко. За щастие съществуват достатъчно библиотеки, превръщащи изображение в масив от RGB стойности. Лично аз съм попадал в Inetrenet на програми, конвертиращи bmp и pcx в такива масиви. Трябва само да потърсите. А ако не ви се търси, glaux предоставя функцията auxDIBImageLoad(), приемаща като аргумент името на .bmp файл и връща указател на масив. Пример за използването на тази функция е:

AUX_RGBImageRec *image;
image = auxDIBImageLoad("photo.bmp");

За позициониране на изображението отново се използва функцията glRasterPos2f(), а самото изрисуване става чрез извикване на функцията glDrawPixels(), приемаща 5 аргумента. Първите два са ширината и височината на изображението, следващия е константа GL_RGB или GL_COLOR_INDEX, указваща дали подадения масив съдържа RGB стойности или индекси на цветове, следващия е типа данни в масива (GL_BYTE, GL_UNSIGNED_BYTE, GL_BITMAP - последния съдържа единица или нула) и накрая е самия масив. Ако ползвате функцията auxDIBImageLoad(), то тя всъщност връща обект AUX_RGBImageRec. Ширината на изображението, съдържащо се в сами обект, можете да получите от свойството sizeX, височинара - от sizeY, а самия масив от стойности се съдържа в свойството data. Типа на данните в масива е GL_UNSIGNED_BYTE.

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

OpenGL предоставя множество функции за изрисуване на пиксели на екрана. Една от интересните функции е glPixelTransferf(), която може да "мащабира" всеки един от трите RGB елемента. Под "мащабиране" се има предвид, че можете да умножите всяка една от тези стойности с число, при което във всеки пиксел съответния елемент ще бъде умножен с това число и ще пиксела ще бъде изрисуван с новия му цвят. Така можете да постигнете различни ефекти, като например увеличите стойността R на цвета за да получите едно изображение, в което цветовете клонят към различни нюанси на червеното. Функцията приема два аргумента, първия определящ кой компонент ще се изменя и втория - число, с което той да бъде умножен. За първия аргумент можете да използвате константите GL_RED_SCALE, GL_GREEN_SCALE и GL_BLUE_SCALE. Като промените и трите компопнента наведнъс с едно число, вие можете да направите изображението по-ярко или по-тъмно. Също така можете да премащабирате изображението, преди да го изрисувате, като определите "размера" на пиксела по хоризонтала и вертикала чрез функцията glPixelZoom(). Например glPixelZoom(1.0, 2.0) ще направи изображението два пъти по-високо, запазвайки неговата ширина, а glPixelZoom(-1,0, 1.0) ще го направи огледално.

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

Функцията приема 7 аргумента - първите два определят долния ляв ъгъл на правоъгълника, който трябва да бъде прочетен; следващите два определят неговата ширина и височина; петия определя какво да бъде прочетено, 6-тия типа на данните в масива (възможни са същите стойности, както и в glDrawPixels(), а последния - самия масив, в който да се запишат стойностите. Възможни стойности за петия елемент са:

- GL_COLOR_INDEX (в Color-Index режим) - индекса на цвета
- GL_RGB - RGB стойността на цвета
- GL_RGBA - RGBA стойността на цвета
- GL_RED - количеството на червения цвят
- GL_GREEN - количеството на зеления цвят
- GL_BLUE - количеството на синия цвят
- GL_ALPHA - Alpha стойността
- GL_DEPTH_COMPONENT - стойността на Depth буфера за съответния компонент

5. Текстури

Текстурите са едно от основните неща, създаващи реалистичната графика. Но е и едно от най-сложните за разбиране неща в OpenGL.

Първото нещо, което трябва да имаме предивид, е, че всяка текстура е изображение, което се налага върху полигони за да им придаде по-реалистичен вид. Текстурата може да е едноизмерна (има само ширина или само височина) или двуизмерна (има ширина и височина)
Преди да можете да използвате текстури, трябва да дефинирате изображението, което ще се използва като тектура. Дефинирането на изображения вече беше обсъдено в "Изображения" - начина им е еднакъв независимо дали ще се изрисуват на екрана или наслагват като тектури. Само че размерите на текстурата трябва да са степен на 2. Т.е. може да имате ширина от 1, 2, 4, 8 16 или 32 пиксела, но не от 3 или 5 примерно.

Първо ще разгелдаме използването на едноизмерните текстури, което е значително по-просто. Може и да не виждате приложението им, но макар че те не добавят кой знае какво качество към графиката, често са полезни за по-бързо изпълнение на програмата. Типичен пример за това е изрисуването на дъга. Можете да дефинирате текстура със седемте цвята във височина, но без ширина (или по-точно широка 1 пиксел) и да я използвате за дъгата; алтернативата е изрисуването на 7 различни полигона. Едноизмерната текстура се създава чрез функцията glTexImage1D(), която приема 8 аргумента. Първия от тях е GL_TEXTURE_1D, следващия аргумент обозначава нивото на детайлите и обикновено е 0 (нивото на детайлите е разгледано по-нататък). Следващия аргумент обозначава броя на цветовите компоненти, използвани за всеки пиксел (1 в Color-Index режим, 3 за RGB и 4 за RGBA цвят). Четвъртия аргумент съдържа ширината на текстурата и трябва да е степен на 2; петия дефинира ширината на рамката - брой пиксели, които да бъдат пропуснати преди повторното извеждане на изображението. Ширината на рамката може да бъде 0, 1 или 2. Следващия аргумент указва типа на цветовете, които се използват - GL_COLOR_INDEX, GL_RGB или GL_RGBA. Последните два аргумента са съответно типа на данните, които се съдържат в масива и указател към масив, съдържащ изображението.

Двуизмерната текстура има и ширина и височина. Тя се дефинира с функцията glTextImage2D(), която приема 9 аргумента. Първите три са като на glTexImage1D, четвъртия определя ширината, а петия височината на текстурата. Следващите параметри са идентични с тези на glTexImage1D(). Ширината и височината трябва да бъдат степени на 2.
За да използвате тектури трябва да ги разрешите, използвайки функцията glEnable(). За едноизмерни текстури трябва да я извикате с аргумент GL_TEXTURE_1D, за двуизмерни - GL_TEXTURE_2D. Също така, можете да определите начина, по който тектурата се наслагва на полигона. Това става чрез извикване на функцията glTexEnvi() с GL_TEXTURE_ENV и GL_TEXTURE_ENV_MODE, като първи два аргумента и една от следния константи като трети:

- GL_DECAL - цвета на пикселите от текстурата заменя цвета на полигона
- GL_MODULATE - цвета на пикселите от полигона се умножава по цвета на пикселите от текстурата
- GL_BLEND - цвета на пикселите от полигона се умножава по цвета на пикселите от текстурата и се смесва цвета с определен цвят.

Освен да определите координатите на всеки връх, когато използвате тектури, трябва да определяте и техните координати. Координатите на върховете определят къде се намира даден връх от полигона; координатите на тектурите определят коя точка от тектурата се намира върху този връх. Определянето на координатите на текстурата се осъществява чрез функцията glTexCoord2f(), приемаща три аргуимента - x и y координатат (за 1D тектури се използва glTexCoord1f(), която приема само един аргумент). Тези координати са относително спрямо размерите на тектурата. Например, ако имате квадрат, и тектура, която трябва да бъде насложена върху него, и определяте ъглите на правоъгълника в посока, обратна на часовниковата стрелка, започвайки от горния десен ъгъл, то координатите за първия връх трябва да са 1,0 (на този връх се наслагва пиксела от последна колона, първи ред), за втория (долен десен ъгъл) - 1,1 (последна колона, първи ред); за третия (долен ляв ъгъл) - 0,1 (първа колона, последен ред) и за четвъртия (горен ляв ъгъл) 0,0 - пъвия пиксел на тектурата. Но ако имате правоъгълник, чиято височина е 2/3 от ширината, и същата тази квадратна тектура, и искате да използвате горната част на тектурата върху този правоъгълник (а не да променяте размера на тектурата, което може да я развали), трябва да използвате координати 1,0; 1,2/3; 0,2/3 и 0,0.

Следва продължение...

 

Материалът е публикуван на: 11.12.2002 г.

Автор: Максим Крижановски
адрес за кореспонденция

Начало на материала :: Процесори :: Дънни платки :: Видео :: Мултимедия :: Носители
Периферия :: Комуникации :: Софтуер :: Технологии :: Links & Downloads :: Форум
© 2001-2006 Macrolevel, Inc. Правила и условия.
При цитиране на материала линк към сайта и посочване на автора са задължителни.
За контакти използвайте адреса ни за електронна поща