Перейти на главную   
  helloworld.ru - документация и книги по программированию  
helloworld.ru - документация и книги по программированию
    главная     хостинг    
Поиск по сайту:  
Смотрите также
Языки программирования
C#
MS Visual C++
Borland C++
C++ Builder
Visual Basic
Quick Basic
Turbo Pascal
Delphi
JavaScript
Java
PHP
Perl
Assembler
AutoLisp
Fortran
Python
1C

Интернет-технологии
HTML
VRML
HTTP
CGI
FTP
Proxy
DNS
протоколы TCP/IP
Apache

Web-дизайн
HTML
Дизайн
VRML
PhotoShop
Cookie
CGI
SSI
CSS
ASP
PHP
Perl

Программирование игр
DirectDraw
DirectSound
Direct3D
OpenGL
3D-графика
Графика под DOS

Алгоритмы
Численные методы
Обработка данных

Сис. программирование
Драйверы

Базы данных
MySQL
SQL

Другое

Хостинг


Друзья
demaker.ru
Реклама

Лучший хостинг. Аренда серверов




helloworld.ru

Буфер


  Существуют 3 типа основных графических операций : очистка экрана ,
рисование геометрических обьектов , рисование растеризованных обьектов (текстур) .

 Очистка RGBA mode window в черный цвет :


glClearColor(0.0, 0.0, 0.0, 0.0);
glClear(GL_COLOR_BUFFER_BIT);


 Чтобы очистить одновременно 2 видеобуфера в режиме RGBA - color buffer и depth buffer - нужно :

glClearColor(0.0, 0.0, 0.0, 0.0);
glClearDepth(1.0);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);


 Вообще-то буферов больше - это :
         Color buffer
         Depth buffer
         Accumulation buffer
         Stencil buffer


 Разница между

    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

и

    glClear(GL_COLOR_BUFFER_BIT);
    glClear(GL_DEPTH_BUFFER_BIT);

в том , что первый вариант очищает быстрее .




Цвет

Перед началом прорисовки в OpenGL нужно обязательно установить т.н. coloring scheme . Следующий псевдокод : set_current_color(red); draw_object(A); draw_object(B); set_current_color(green); set_current_color(blue); draw_object(C); нарисует обьекты А и В красными , а С - синим . Цвет устанавливается командой glColor3f(), которая имеет три float-параметра в диапазоне от 0 до 1 - red, green, blue - компоненты . Вот таблица для основных цветов : glColor3f(0.0, 0.0, 0.0); black glColor3f(1.0, 0.0, 0.0); red glColor3f(0.0, 1.0, 0.0); green glColor3f(1.0, 1.0, 0.0); yellow glColor3f(0.0, 0.0, 1.0); blue glColor3f(1.0, 0.0, 1.0); magenta glColor3f(0.0, 1.0, 1.0); cyan glColor3f(1.0, 1.0, 1.0); white Команда glFlush() - она должна присутствовать в цикле прорисовки после каждого фрейма . glFinish() - аналогичная команда , которая к тому же может привести к некоторому замедлению . Но glFlush() все же предпочтительнее .

Графические примитивы

Point - в OpenGL ее еще называют vertex . Имеет 3 координаты - x,y,z . OpenGL работает в так называемых гомогенных(приведенных) координатах , поэтому у точки имеется 4-й дополнительный параметр - (x,y,z,w) . Если w не равно нулю , фактически мы имеем дело с координатой (x/w,y/w,z/w). Line - это линейный сегмент . Polygon - закрытая область , определенная с помощью vertices . Ребра в полигоне не должны пересекаться . Полигон также должен быть выпуклым . На число сегментов ограничений нет . Rectangles - этот графический примитив прорисовывается командой glRect*(). Все геометрические обьекты определяются в OpenGL с помощью набора вершин . Вершина задается командой glVertex*() . glVertex2s(2, 3); glVertex3d(0.0, 0.0, 3.1415926535898); glVertex4f(2.3, 1.0, -2.2, 2.0); GLdouble dvect[3] = {5.0, 9.0, 1992.0}; glVertex3dv(dvect); Все эти команды представляют 3-мерные вершины в различных форматах . Последняя строка вроде как наиболее эффективная по скорости , поскольку имеет единственный параметр . Теперь давайте нарисуем полигон из 5 вершин : glBegin(GL_POLYGON); glVertex2f(0.0, 0.0); glVertex2f(0.0, 3.0); glVertex2f(4.0, 3.0); glVertex2f(6.0, 1.5); glVertex2f(4.0, 0.0); glEnd(); В качестве параметра в функцию можно передавать следующие графические примитивы : GL_POINTS GL_LINES GL_LINE_STRIP GL_LINE_LOOP GL_TRIANGLES GL_TRIANGLE_STRIP GL_TRIANGLE_FAN GL_QUADS GL_QUAD_STRIP GL_POLYGON . Корректно работают в цикле glBegin()... glEnd() следующие команды : glVertex*() glColor*() glIndex*() glNormal*() glTexCoord*() glEdgeFlag*() glMaterial*() glArrayElement() glEvalCoord*() glEvalPoint*() glCallList() glCallLists() Пример прорисовки круга : #define PI 3.1415926535898 GLint circle_points = 100; glBegin(GL_LINE_LOOP); for (i = 0; i < circle_points; i++) { angle = 2*PI*i/circle_points; glVertex2f(cos(angle), sin(angle)); } glEnd(); С точки зрения OpenGL-оптимизации - это бездарный код . Необходимо , конечно , предварительно вычислить массив и потом делать на него ссылку в цикле glBegin()... glEnd() . Каждый полигон имеет 2 стороны - front и back . Это позволяет правильно рисовать обьекты , имеющие внутренние невидимые полости . Функция glPolygonMode() - контролирует прорисовку полигонов : glPolygonMode(GL_FRONT, GL_FILL); glPolygonMode(GL_BACK, GL_LINE). Полигоны , у которых вершины появляются в порядке против часовой стрелки , называются front-facing. Функция glFrontFace() контролирует этот процесс. Команда glCullFace() задает порядок прорисовки - front или back , выполняется совместно с командой glEnable() . Если рассматривать плоскость , то нормальный вектор к ней буден един для всех точек плоскости . OpenGL позволяет определить нормаль для каждого полигона или вершины . Нормаль определяет ориентацию полигона , например , относительно источника света . Нормаль определяет , сколько света падает на вершину . Нормаль устанавливается командой glNormal*() . Для каждой вершины она , как правило , различна . glBegin (GL_POLYGON); glNormal3fv(n0); glVertex3fv(v0); glNormal3fv(n1); glVertex3fv(v1); glNormal3fv(n2); glVertex3fv(v2); glNormal3fv(n3); glVertex3fv(v3); glEnd(); Как известно , при вычислении ротации или трансформации вектор нужно нормализовать , для этого команда glEnable() выполняется с параметром GL_NORMALIZE .

Vertex arrays

Возьмем ситуацию когда нужно прорисовать полигон с 20 вершинами . По обычной схеме , в цикле между командами glBegin() - glEnd() нужно поставить как минимум 20 команд , по одной на каждую вершину . Многовато что-то . К тому же другая известная проблема - это избыточность обработки смежных вершин . По-хорошему , нужно 20 вершин положить в массив , а 20 нормалей положить в другой массив и сделать по одному вызову функции к каждому из массивов . При использвании vertex arrays нужно делать следующее : 1. Инициализируем 6 (!) массивов : 1. для вершин 2. для RGBA colors 3. для color indices 4. для surface normals 5. для texture coordinates 6. для polygon edge flags. 2. Проинициализировать массивы и установить указатели на них . 3. Можно рисовать ... На практике инициализация vertex arrays выполняется с помощью glEnableClientState() . Из 6 названных массивов , как правило , создаются 4 (без 2 и 3) . 2 команды для нормалей и вершин : glEnableClientState(GL_NORMAL_ARRAY); glEnableClientState(GL_VERTEX_ARRAY); Указатели на массивы создают команды : 1. glVertexPointer() - размерность (2,3,4) 2. glColorPointer() - размерность (3,4) 3. glIndexPointer() - размерность (1) 4. glNormalPointer() - размерность (3) 5. glTexCoordPointer() - размерность (1,2,3,4) 6. glEdgeFlagPointer() - размерность (1) Пример: Здесь создаются 2 массива - один для вершин размерностью 2*6 , другой - для цвета размерности 3*6 : static GLint vertices[] = {25, 25, 100, 325, 175, 25, 175, 325, 250, 25, 325, 325}; static GLfloat colors[] = {1.0, 0.2, 0.2, 0.2, 0.2, 1.0, 0.8, 1.0, 0.2, 0.75, 0.75, 0.75, 0.35, 0.35, 0.35, 0.5, 0.5, 0.5}; glEnableClientState (GL_COLOR_ARRAY); glEnableClientState (GL_VERTEX_ARRAY); glColorPointer (3, GL_FLOAT, 0, colors); glVertexPointer (2, GL_INT, 0, vertices); В OpenGL есть такое понятие - stride , с помощью которых несколько массивов можно упаковать в один , как в данном случае - массив для 6 вершин имеет доп. информацию о цвете : static GLfloat intertwined[] = {1.0, 0.2, 1.0, 100.0, 100.0, 0.0, 1.0, 0.2, 0.2, 0.0, 200.0, 0.0, 0.2, 0.2, 1.0, 200.0, 100.0, 0.0}; Чтобы выборочно прочитать данные с такого массива , можно выбрать только цвет : glColorPointer (3, GL_FLOAT, 6 * sizeof(GLfloat), intertwined); Для выборки вершин из этого массива : glVertexPointer(3, GL_FLOAT,6*sizeof(GLfloat), &intertwined[3]) . Для получения информации о единственном элементе массива есть команда glArrayElement() . Пример : glBegin(GL_TRIANGLES); glArrayElement (2); glArrayElement (3); glArrayElement (5); glEnd(); Этот кусок эквивалентен следующему : glBegin(GL_TRIANGLES); glColor3fv(colors+(2*3*sizeof(GLfloat)); glVertex3fv(vertices+(2*2*sizeof(GLint)); glColor3fv(colors+(3*3*sizeof(GLfloat)); glVertex3fv(vertices+(3*2*sizeof(GLint)); glColor3fv(colors+(5*3*sizeof(GLfloat)); glVertex3fv(vertices+(5*2*sizeof(GLint)); glEnd(); Команда glDrawElements() прорисовывает примитивы . Следующий код делает рендер кубика , при этом вершины каждой грани должны быть положены в массив индексов : static GLubyte frontIndices = {4, 5, 6, 7}; static GLubyte rightIndices = {1, 2, 6, 5}; static GLubyte bottomIndices = {0, 1, 5, 4}; static GLubyte backIndices = {0, 3, 2, 1}; static GLubyte leftIndices = {0, 4, 7, 3}; static GLubyte topIndices = {2, 3, 7, 6}; glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, frontIndices); glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, rightIndices); glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, bottomIndices); glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, backIndices); glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, leftIndices); glDrawElements(GL_QUADS, 4, GL_UNSIGNED_BYTE, topIndices); Другой вариант - все индексы свалить в кучу - что будет покруче : static GLubyte allIndices = {4, 5, 6, 7, 1, 2, 6, 5, 0, 1, 5, 4, 0, 3, 2, 1, 0, 4, 7, 3, 2, 3, 7, 6}; glDrawElements(GL_QUADS, 24, GL_UNSIGNED_BYTE, allIndices); Тут мы не используем цикл glBegin()/glEnd() . Также имеется команда glInterleavedArrays() для продвинутого использования упакованных массивов с кучей переменных булевского типа . В OpenGL имеется механизм сохранения и восстановления данных . Имеются 2 стека : attribute stack и client attribute stack , доступ к которым возможен по командам glPushClientAttrib() , glPopClientAttrib() , glPushAttrib() , glPopAttrib() .

Хинты .

1. Все полигоны должны быть ориентированы по вершинам либо по часовой стрелке , либо против . 2. При разбиении поверхности в качестве единицы старайтесь использовать треугольный полигон , три вершины которого всегда лежат в одной плоскости . 3. Всегда приходится выбирать золотую середину между количеством треугольников , на которые мы разбиваем поверхность обьекта , и качеством изображения этого обьекта . Если полигон далеко , то количество может быть небольшим . Чем более плоская поверхность , тем меньше требуется полигонов . 4. Для более высокого качества изображения имеет смысл производить более детальное разбиение ближе к граням обьекта . Если vector dot между нормальным вектором вершины полигона и нормальным вектором поверхности близок или равен нулю - это верный признак того , что прилегающую грань нужно разбивать более детально для более рельефной прорисовки . Рассмотрим пример создания икосаедра . Его можно принять как грубое приближение к сфере . #define X .525731112119133606 #define Z .850650808352039932 static GLfloat vdata[12][3] = { {-X, 0.0, Z}, {X, 0.0, Z}, {-X, 0.0, -Z}, {X, 0.о0, -Z}, {0.0, Z, X}, {0.0, Z, -X}, {0.0, -Z, X}, {0.0, -Z, -X}, {Z, X, 0.0}, {-Z, X, 0.0}, {Z, -X, 0.0}, {-Z, -X, 0.0} }; static GLuint tindices[20][3] = { {0,4,1}, {0,9,4}, {9,5,4}, {4,5,8}, {4,8,1}, {8,10,1}, {8,3,10}, {5,3,8}, {5,2,3}, {2,7,3}, {7,10,3}, {7,6,10}, {7,11,6}, {11,0,6}, {0,1,6}, {6,1,10}, {9,0,11}, {9,11,2}, {9,2,5}, {7,2,11} }; int i; glBegin(GL_TRIANGLES); for (i = 0; i < 20; i++) { /* color information here */ glVertex3fv(&vdata[tindices[i][0]][0]); glVertex3fv(&vdata[tindices[i][1]][0]); glVertex3fv(&vdata[tindices[i][2]][0]); } glEnd(); Константы X и Z выбраны для того , чтобы расстояние от центра икосаедра до каждой вершины было одинаковым . Координаты 12 вершин определены в массиве vdata[][] . В данном куске кода нет цветовой информации о раскраске каждой грани , поэтому образ на экране получается не совсем реальным . Для этого надобно вычислить нормаль к каждой поверхности и уже на основе этого выбирать цвет . Для этого нужно подсчитать т.н. cross product . Для икосаедра это выглядит так : GLfloat d1[3], d2[3], norm[3]; for (j = 0; j < 3; j++) { d1[j] = vdata[tindices[i][0]][j] - vdata[tindices[i][1]][j]; d2[j] = vdata[tindices[i][1]][j] - vdata[tindices[i][2]][j]; } normcrossprod(d1, d2, norm); glNormal3fv(norm); В общем случае вычисление нормали : void normalize(float v[3]) { GLfloat d = sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); if (d == 0.0) { error("zero length vector"); return; } v[0] /= d; v[1] /= d; v[2] /= d; } void normcrossprod(float v1[3], float v2[3], float out[3]) { GLint i, j; GLfloat length; out[0] = v1[1]*v2[2] - v1[2]*v2[1]; out[1] = v1[2]*v2[0] - v1[0]*v2[2]; out[2] = v1[0]*v2[1] - v1[1]*v2[0]; normalize(out); } Следующий пример показывает , как сделать аппроксимацию икосаедра , приблизив его поверхность к сферической . Поверхность разбивается на 80 треугольников : void drawtriangle(float *v1, float *v2, float *v3) { glBegin(GL_TRIANGLES); glNormal3fv(v1); vlVertex3fv(v1); glNormal3fv(v2); vlVertex3fv(v2); glNormal3fv(v3); vlVertex3fv(v3); glEnd(); } void subdivide(float *v1, float *v2, float *v3) { GLfloat v12[3], v23[3], v31[3]; GLint i; for (i = 0; i < 3; i++) { v12[i] = v1[i]+v2[i]; v23[i] = v2[i]+v3[i]; v31[i] = v3[i]+v1[i]; } normalize(v12); normalize(v23); normalize(v31); drawtriangle(v1, v12, v31); drawtriangle(v2, v23, v12); drawtriangle(v3, v31, v23); drawtriangle(v12, v23, v31); } for (i = 0; i < 20; i++) { subdivide(&vdata[tindices[i][0]][0], &vdata[tindices[i][1]][0], &vdata[tindices[i][2]][0]); }

[ Назад | Оглавление | Далее ]










helloworld.ru © 2001-2021
Все права защищены