![]() |
|||||
| Процесори :: Дънни платки
:: Видео :: Мултимедия
:: Носители :: Периферия Комуникации :: Софтуер :: Технологии :: Links & Downloads :: Форум |
|||||
| Въведение в OpenGL | |||||
|
|
|||||
| Съдържание на материала:
OpenGL (Open Graphic Library) е една от основните графични библиотеки, използване за създаване на графични приложения или игри. Тя се явява и основния (ако не и единствения) конкурент на DirectX. Предимствата на OpenGL пред DirectX са: - OpenGL (както показва и името й) е open-source библиотека. На първо
място това означава че е тя безплатна Разбира се, OpenGL си има и свойте недостатъци: - DirectX е значително по-функционална библиотека - OpenGL предоставя
функции единствено за определяне на прости фигури, такива като линии и
полигони. Съществуват множество допълнителни библиотеки, като GLU (OpenGL
Utiliti Library), които улесняват често изпълними задачи, като рендериране
на кубове, сфери и конуси. Но все пак функционалността на DirectX е на
съвсем друго ниво. С появата на OpenGL 2 вероятно разликата във функционалностите
ще намелее значително, но това ще може да се каже единствено, когато излезе
финалната версия на OpenGL 2 - 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 за единичен или двоен буфер. Препоръчва
се използването на двоен буфер, освен ако не рендерирате нещо наистина
просто или ако не рендерирате единично (статично) изображение. Разликате
в двата режима е, че при наличието на двоен буфер само единия е видим.
Изрисуването се осъществява в невидимия буфер след което двата буфера
се разменят Стандартно обръщение към функцията изглежда така: 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 са координатите на края на
вектора - неговото начало съвпада с върха glVertex{234}f[v](x,y,z,w) Така се определят точките. Как се интерпретират определя параметърът, подаден към glBegin(). Възможностите са: = GL_POINTS - точките остават такива, без да се свързват - Изрисуване Преди да започнете да рисувате, вероятно ще искате да изчистите буфера от предишното изображение. 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 дефиниция : = GL_AMBIENT R,G,B,A Определя интензцивността на Ambient излъчването
на източника със съответната RGBA стойност -Определяне на материала Определелянето на материал става с функцията: glMaterialfv(face, pname, params) Където face е една от константите GL_BACK, GL_FONT или GL_FRONT_AND_BACK, които определят съответно дефиниране на материала на предната страна, на задната страна или и на двете едновременно. Рname е параметърът, който определяме (отново както при осветлението можем да определим множество параметри чрез поредни извиквания на функцията), а params е масив, съдържащ съответните стойности. Параметрите, с които можете да извикате функцията, и съответните стойности са, както следва: Параметър Стойност Дефиниция = GL_AMBIENT RGBA определя количеството отразена светлина тип AMBIENT -Преобразования на матрицата: ротация, транслация и мащабиране; проекции Може да звучи сложно, но не е. За тези които знаят какво е матрица ще спомена само, че в OpenGL се използват матрици 4x4. За тези които не знаят - да не се притесняват. Не е необходимо да учат висша математика, за да ползват OpenGL, трябва само да разберат няколко основни концепции. Аз също не бих могъл да работя с матрици извън употребата им в OpenGL. Така че няколко основни неща за матриците: - В OpenGL съществуват три матрици и три различни типа трансформации:
MODELVIEW, PROJECTION и TEXTURE. Последната се отнася за текстурите, тук
ще разгледаме първите две матрици Последното сигурно звучи доста сложно. За какво става въпрос. Ако определите ротация от 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 ToolkitOpenGL предоставя функции от най-ниско ниво. Това си има свойте предимства, но често създаването на комплексни обекти изисква трърде много работа. Освен това 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(), разгледана малко
по-нататък. - 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 - стрелка надясно Точните изображения, ползвани като курсор, се определят от операционната система 2. Създаване на меню GLUT предоставя функции, позволяващи създаването на различни контекстни
менюта за програмата. - void glutSetMenu(int menu) определя текущото контекстно меню. Единствения параметър е идентификационния номер на менюто - int glutGetMenu(void) връща идентификационния номер на текущото контекстно меню - void glutDestroyMenu(int menu) унищожава менюто, чийто идентификационнен номер е подаден кто параметър - void glutAddSubMenu(char *name, int menu) добавя разширяващо се меню към текущото. name определя текста, който да бъде изписан; ако курсора се задържи над елемента ще се покаже подменю, определено от идентификатора menu. Трябва първо да създадете контекстно меню за да можете после да го добавите
към друго като подменю. - 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 glutTimerFunc(unsigned int msecs, void (*func)(int value), int passvalue) определя функция, която да се извиква на определен интервал от време. msecs определя интервала в милисекунди. Функцията трябва да приема един аргумент от тип int; passvalue се подава като аргумент на функцията. И така, GLUT. Тя е безплатна, платформонезависима библиотека с отворен код. Позволява използването на голямо количество възможности. В този кратък материал съм се постарал да ви обясня и представя основните й функции, ако съм ви заинтригувал, можете да се прехвърлите на http://www.opengl.org и да видите спецификациите на GLUT. OpenGL - Технологии за напреднали 1.Полупрозрачност 1. Полупрозрачност Цветовете в OpenGL (когато не си използва Color-Index режим) имат четири компонента, отговарящи на четирите елемента на RGBA модела. Именно четвъртата стойност се използва за създаване на полупрозрачност, известна още като Alpha blending. Това е технология при която цветовете на предния и задния обект се сливат, за да се получи един среден цвят, създаващ ефект на прозрачност на прендния обект. Тази прозрачност може да варира в различни степени, като позволява създаването на ефект от ползването на цветни стъкла. За да има ефект четвъртата стойност на цвета обаче, трябва първо да разрешите използването на alpha blending, което по подразбиране е изключено (тъй като допълнително натоварва процесора). Това става чрез подаване на GL_BLEND нафункцията glEnable(). Слеващото нещо, което трябва да направите, е определяне на алгоритъм за смесване на цветовете. Това става чрез извикване на функцията glBlendFunc(), приемаща два аргумента, първия от които определя как се изменя цвета на предния обект, а втория - на задния. Възможните стойности на първия аргумент са: - GL_ZERO - цвета на обекта въобще не се взима предвид За втори параметър се използват същите константи, с тази разлика че
вместо GL_DST_COLOR се използва GL_SRC_COLOR (и съответно цвета се умножава
по този на предния обект) Стандартния вид на функция за използване на прозрачност е glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA) като тя се извиква само за предния обект (за задния GL_BLENDING трябва
да е забранен) Важно е да се отбележи, че реда на изрисуване на обектите има значение. Винаги трябва да изрисувате задния обект преди предния, за да получите необходимия обект. 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, на които ще бъде изрисувано изображението. Но как да изведем пълноцветно изображение на екрана? Това не е много по-трудно. Отново имате нужда от масив, съдържащ вече цвета на всеки пиксел, и функцията glDrawPixels. Но докато създаването на масив от единици и нули за представянето на просто изображение може да не е много трудна задача, какво ще кажете за представянето на снимка като масив от RGB стойности? Едно е да създадете изображението с Photoshop, Paint Shop Pro или друго графично приложение, но OpenGL няма как да го използва пряко. За щастие съществуват достатъчно библиотеки, превръщащи изображение в масив от RGB стойности. Лично аз съм попадал в Inetrenet на програми, конвертиращи bmp и pcx в такива масиви. Трябва само да потърсите. А ако не ви се търси, glaux предоставя функцията auxDIBImageLoad(), приемаща като аргумент името на .bmp файл и връща указател на масив. Пример за използването на тази функция е: AUX_RGBImageRec *image; За позициониране на изображението отново се използва функцията 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 режим) - индекса на цвета 5. Текстури Текстурите са едно от основните неща, създаващи реалистичната графика. Но е и едно от най-сложните за разбиране неща в OpenGL. Първото нещо, което трябва да имаме предивид, е, че всяка текстура е
изображение, което се налага върху полигони за да им придаде по-реалистичен
вид. Текстурата може да е едноизмерна (има само ширина или само височина)
или двуизмерна (има ширина и височина) Първо ще разгелдаме използването на едноизмерните текстури, което е значително по-просто. Може и да не виждате приложението им, но макар че те не добавят кой знае какво качество към графиката, често са полезни за по-бързо изпълнение на програмата. Типичен пример за това е изрисуването на дъга. Можете да дефинирате текстура със седемте цвята във височина, но без ширина (или по-точно широка 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. - GL_DECAL - цвета на пикселите от текстурата заменя цвета на полигона Освен да определите координатите на всеки връх, когато използвате тектури, трябва да определяте и техните координати. Координатите на върховете определят къде се намира даден връх от полигона; координатите на тектурите определят коя точка от тектурата се намира върху този връх. Определянето на координатите на текстурата се осъществява чрез функцията 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 :: Форум |
|||||
|
|||||