После лабораторных работ бывает и курсовая работа.
СОДЕРЖАНИЕ
ВЫБОР ГРАФИЧЕСКОЙ БИБЛИОТЕКИ.. 4
Создание реалистичного отражения окружающего мира на объекте. 8
Создание пола с попиксельным освещением.. 10
ПРИЛОЖЕНИЕ. ТЕКСТ ОСНОВНОЙ ЧАСТИ ПРОГРАММЫ… 18
ЗАДАНИЕ
Суть задания – разработать программу с использованием библиотек OpenGL или DirectX, создающую сложную графическую сцену (сцены).
Возможные варианты достижения цели:
- Компьютерная игра. Типичный пример — автогонки. При этом необходимо чтобы движение автомобиля было реалистичным и подчинялось законам физики. Другой пример – спортивные игры. Здесь не обойтись без искусственного интеллекта (в данном случае это подразумевает необходимость симулировать действия спортсменов-соперников.) В любом случае основной упор следует сделать на графике, а не на искусственном интеллекте.
- Сложная сцена, в которой реализуются сложные графические эффекты (например, детализированное лицо человека, реалистичные волосы на голове человека, и т. д.)
- ……..
В целом, характер программы определяется вашей фантазией. Самое главное – своей работой вы должны показать, что научились создавать качественные приложения трёхмерной графики, провели серьезную работу и научились решать задачи заметно более сложные, чем при выполнении лабораторных работ по компьютерной графике.
Для создания сложных графических моделей можно (и, возможно, необходимо) использовать специальные программы, такие как 3D Studio MAX, Maya или Blender.
Важную роль в оценке вашей работы играет отчет (пояснительная записка). Что написать в нем? Необходимо предоставить всю информацию, которая может понадобиться человеку, который захочет повторить достижение, то есть создать такую же программу как и ваша. В отчёте следует так же показать, насколько большой труд был приложен лично вами для создания тех или иных частей программы.
Например, трёхмерные модели 3D Max можно создать собственноручно «с нуля» или загрузить готовую из Интернета. Если модель создана лично вами, в отчёте необходимо объяснить, как у вас это получилось, чтобы читающий ваш отчёт мог (при наличии некоторого опыта работы в 3D Max, разумеется) понять, как он может сделать это. Если модель была позаимствована, необходимо рассказать, какие изменения и как были внесены вами.
Если вы используете специальные библиотеки поверх OpenGL (например, GLScene), или классы С++, созданные не вами, необходимо объяснить в отчёте откуда это взять и как это работает. Необходимо объяснить назначение и принцип работы используемых классов, чтобы было видно, что вы хорошо понимаете то, чем пользуетесь.
Работа будет оцениваться по трем основным критериям:
- Общее эстетическое впечатление от отображаемой трёхмерной сцены. Первый, но не обязательно самый важный фактор.
- Сложность сцены, используемых в ней спецэффектов и т. д. Иными словами, должно быть очевидно, что человек изучил что-то новое, научился реализовывать заметно более сложные вещи, чем в прошлом семестре.
- Степень приложенного студентом труда. Если человек создал сложную сцену, но при этом сам написал только десять строк программного кода, это не очень хорошо. Если есть хорошие заготовки программ из Интернета и модели 3D MAX, значит нужно поставить планку ещё выше и сделать ещё лучше.
ВЫБОР ХАРАКТЕРА ПРОГРАММЫ
Нарисовать просто какую-нибудь трёхмерную сцену не слишком интересно. Гораздо лучше сделать то, для чего собственно и создавались OpenGL и DirectX. Естественно, они создавались для создания трёхмерных компьютерных игр. Но создать полноценную игру достаточно трудоёмкое занятие, слишком объёмное для курсовой работы. Поэтому будет создана некое подобие игры, то есть нечто вроде заготовки.
В этой курсовой была создана программа, которая рисует пол с использованием «объёмной» текстуры (bump mapping). На этом полу находятся небольшие горки, между которыми летает самолет. На поверхности самолета отражаются все окружающие пейзажи.
ВЫБОР ГРАФИЧЕСКОЙ БИБЛИОТЕКИ
Программа будет написана с использованием библиотеки OpenGL. Этот выбор основан на том, что:
- OpenGL – межплатформенная библиотека
- По мощности OpenGL сравнима с DirectX, но гораздо легче в использовании
- Для OpenGL легче найти примеры и легче найти статьи про OpenGL.
В программе также для облегчения работы будет использоваться glTools (написанная Richard S. Wright Jr.), которая облегчает работу с OpenGL, а также собственные библиотеки CGL (облегчает инициализацию, установку освещения и позволяет создавать прозрачные элементы пользовательского интерфейса) и myutils (в которой реализована загрузка изображений из файлов bmp, tga).
ВЫПОЛНЕНИЕ
Создание фигур
Фигуры горок и корабля, которым управляет пользователь были нарисованы в примитивном редакторе 3D_Editor и загружены с помощью библиотеки Edit, поставляемой вместе с ним. Собственно такие фигурки можно было сделать и в любом другом редакторе. Это не составляет особой сложности.
Небо было сделано с помощью стандартных функций OpenGL. С помощью функций glVertex3f был нарисован куб, таким образом, чтобы его грани были обращены лицевой стороной к наблюдателю, находящемуся в центре куба. Размер куба сделан достаточно большим.
Далее стороны куба были обёрнуты текстурами (каждая сторона – свое текстурой). Текстуры для этих целей можно скачать из internet. Текстуры созданы так, что при натягивании их на куб они стыкуются краями и получается единая цельная картина, такая, что наблюдателю не видно, что на самом деле она состоит из кучи мелких текстур.
Тут следует заметить, что для текстур необходимо включить режим CLAMP_TO_EDGE, чтобы она обязательно дотягивалась до края полигона и на границе полигонов не было видно полосок, не охваченных текстурой (без такого режима этого не получится).
Кубическая текстура
Поддержку кубических текстур осуществляет расширение GL_ARB_texture_cube_map (также как и расширение GL_EXT_texture_cube_map). Значит, OpenGL поддерживает следующие типы текстур: 1D, 2D, 3D и cube maps.
Для каждого из этих типов есть константа, которая передаётся в такие функции как glBindTexture, glTexParameter и glEnable в параметр texture target. Константа для кубических текстур — GL_TEXTURE_CUBE_MAP_ARB (или равная ей по значению GL_TEXTURE_CUBE_MAP_EXT).
Однако в отличие от традиционных (одно-, двух- и трехмерных) текстур, каждая кубическая текстура состоит из набора шести текстур одинакового размера, которые как бы наложены на грани куба.
При работе с одной из этих шести текстур (например для загрузки в память графического ускорителя) следует использовать константы, однозначно идентифицирующие соответствующую текстуру (грань куба, на которую эта текстура должна быть наклеена).
Вот эти константы:
1 2 3 4 5 6 |
GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB |
Каждая из этих констант идентифицирует одну из граней куба, расположенном в положительном или отрицательном направлении соответствующей координатной оси (перпендикулярной данной грани).
Значения всех этих констант идут последовательно одно за другим, поэтому, например
1 |
GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + 1 = GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB |
При работе с функциями, использующими именно с конкретные текстуры (например, glTexImage2D), нужно использовать именно эти константы.
Загрузка кубической текстуры:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
unsigned createCubeMap ( bool mipmap, const char * maps [] ) { unsigned textureId; glGenTextures ( 1, &textureId ); glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, textureId ); glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); loadTexture ( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, mipmap, maps [0] ); loadTexture ( GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, mipmap, maps [1] ); loadTexture ( GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, mipmap, maps [2] ); loadTexture ( GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, mipmap, maps [3] ); loadTexture ( GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, mipmap, maps [4] ); loadTexture ( GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, mipmap, maps [5] ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); if ( mipmap ) { glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } else { glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); } return textureId; } |
В этом примере функция принимает в качестве параметра список из шести имён текстур.
При работе с кубическими текстурами используются три текстурные координаты (s, t, r). Фактически эти координаты задают направление прямой, выходящей из начала координат. Отсюда точка на текстуре является точкой, где пересекается эта прямая с единичным кубом, описанным вокруг начала координат (с гранями, параллельными осям координат).
Можно заметить, что пересекаемая прямой грань определяется той текстурной координатой, у которой наибольшее значение по модулю.
Так, если наибольшее по модулю значение имеет координата r, то в зависимости от знака r будет выбрана либо GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, либо GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB.
Текстурные координаты (u,v), использующиеся для обращения к текстуре соответствующей грани, вычисляются по следующим формулам:
1 2 |
u = (s/|r| + 1) / 2 v = (t/|r| + 1) / 2 |
Явное задание текстурных координат:
1 |
glTexCoord3f ( s, t, r ); |
Есть возможно автоматически вычислять текстурные координаты. Поддерживается два способа автоматической генерации текстурных координат: отражение и вектор нормали.
Автоматическая генерация текстурных координат, соответствующих отражению:
1 2 3 4 5 6 |
glEnable ( GL_TEXTURE_GEN_S ); glEnable ( GL_TEXTURE_GEN_T ); glEnable ( GL_TEXTURE_GEN_R ); glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB ); glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB ); glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB ); |
Текстурные координаты, соответствующие нормали:
1 2 3 4 5 6 |
glEnable ( GL_TEXTURE_GEN_S ); glEnable ( GL_TEXTURE_GEN_T ); glEnable ( GL_TEXTURE_GEN_R ); glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB ); glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB ); glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, GL_NORMAL_MAP_ARB ); |
Для предотвращения появления артефактов в случаях, когда точка на поверхности куба, соответствующая текстурным координатам, подходит слишком близко к какому-либо из ребер куба, следует использовать режим GL_CLAMP_TO_EDGE.
Задание этого режима осуществляется следующими командами:
1 2 |
glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); |
Данный режим устанавливается только для s и t, поскольку texture wrapping происходит в пределах одной из граней куба, т.е. в 2D.
OpenGL по умолчанию использует режим GL_REPEAT, при работе с кубическими картами нужно явно указать режим GL_CLAMP_TO_EDGE для всех кубических карт.
Перед использованием кубических текстур их следует сперва разрешить при помощи следующей команды
1 |
glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); |
Кубические текстуры имеет более высокий приоритет по сравнению с обычными, поэтому если одновременно разрешить и кубическую и обычную текстуры, то будет использована кубическая.
Создание реалистичного отражения окружающего мира на объекте
С помощью описанной предыдущей главе кубической текстуры можно реализовать отражение на поверхности произвольного объекта окружающего мира. Для этого необходимо обернуть объект кубической текстурой с автоматической генерацией текстурных координат, соответствующей отражению, но при этом генерировать текстуры для каждой грани автоматически.
При генерации текстуры используется следующая функция:
1 2 3 4 5 6 7 8 9 10 |
void glCopyTexSubImage2D( GLenum target, GLint level, GLint xoffset, GLint yoffset, GLint x, GLint y, GLsizei width, GLsizei height ); |
Эта текстура позволяет сохранить изображение, нарисованное в данный момент на экране в текстуру. Чтобы получить реалистичное отражение на объекте нужно обхватить этот объект кубической текстурой, каждая грань которой сформирована с помощью этой функции.
Ниже приведена функция, сохраняющая текущее изображение на экране в обыкновенную плоскую текстуру:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
void CreateScreenTexture(unsigned textureId,GLubyte * textureData,int x,int y,int width,int height) { GLenum err; glBindTexture(GL_TEXTURE_2D,textureId); glTexImage2D(GL_TEXTURE_2D,0, 3, width, height,0, GL_RGB, GL_UNSIGNED_BYTE, textureData); if ((err=glGetError())!=GL_NO_ERROR) printf("Error building 2DMipmaps: %i\n",err); glCopyTexSubImage2D( GL_TEXTURE_2D, 0, 0, 0, x, y, width, height ); if ((err=glGetError())!=GL_NO_ERROR) { fprintf(stderr,"Error copying tex image: %i\n",err); throw 1; } } |
В качестве параметров этой функции передаётся идентификатор текстуры, инициализированный массив типа GLubyte, размер которого достаточен для хранения изображения размером width на height.
Для создания отражения в качестве параметров width и height лучше всего передать 128 и 128 соответственно.
Далее с помощью функции glGetTexImage можно получить байты этой текстуры и после всех необходимых преобразований с ними поставить их как грань кубической текстуры: Под преобразованиями имеется в виду то, что в некоторых случаях возможно текстуру нужно будет зеркально отразить слева направо и сверху вниз так как мы делаем именно отражение.
Основная сложность с созданием отражения таким способом состоит в правильном преобразовании матрицы MODELVIEW. Например, для левой грани её нужно преобразовать таким образом, чтобы камера как будто смотрела влево, при правой – вправо. Это всё делается очень просто, если объект стоит на месте. А если он ещё и двигается в пространстве, то необходимо ещё сначала перенести систему координат в место его расположения, повернуть на угол, на который повёрнут объект и т. д.
Создание пола с попиксельным освещением
Каким образом можно простейшим образом реализовать попиксельное освещение средствами OpenGL?
В качестве модели освещения мы возьмем простейшую модель только диффузного освещения — освещенность каждого видимого пикселя задается следующей формулой:
1 |
I = max ( (l,n), 0 ) |
Где через l и n обозначены единичные вектора направления от точки к источнику света и нормали к поверхности в этой точке. Функция вычисления максимального значения избавляет от «отрицательной» освещенности (когда нормаль направлена от источника света и свет падать на эту точку просто не может).
В OpenGL значения освещенности вычисляются лишь в вершинах граней, а затем полученные значения интерполируются вдоль граней. Поэтому при использовании стандартной модели освещения OpenGL блики внутри граней не появляются.
Для попиксельного освещения стандартная модель освещения OpenGL не подходит.
Нам понадобятся значения векторов l и n для каждого пикселя.
Можно задать единичные значения вектора l в каждой вершине грани (например, как цвет или текстурные кординаты).
Если в каждой из вершин A, B, C и D задать значение вектора l, то можно стандартными средствами OpenGL произвести билинейную интерполяцию этого вектора (вдоль всего многоугольника ABCD), и для каждого пикселя получить проинтерполированное значение.
Самый простой способ для этого — задание вектора l как текстурных координат.
У этого подхода есть серьезный недостаток — если значения, заданные в вершинах и были единичными векторами, то значения, полученные в результате интерполяции, скорее всего единичными уже не будут (см. рис.).
Для нормализации полученных значений (для их использования в скалярном произведении) можно использовать нормализирующие кубические карты (normalization cube map).
Кубическая карта задаёт функцию от направления трехмерного вектора (т.е. функцию, которая зависит только от направления и не зависит от его длины).
Построим следующую карту — каждому вектору (s,t,r) сопоставим его нормированное значение — (s,t,r)/sqrt(s*s+t*t+r*r) .
Тогда если значения вектора l в вершинах задавать как текстурные координаты для такой кубической карты (она называется нормирующей или нормализирующей), то на выходе операции текстурирования мы будем получать единичный трехмерный вектор.
Так как на выходе операции текстурирования мы получаем цвет (3-х или 4-х компонентный), то для построения такой карты нужен способ кодирования почти единичных (поскольку длина вектора, получающиеся в результате интрерполячции единичных, не сильно отличается от единицы) векторов в RGB цвета.
Сопоставим вектору (x,y,z), каждая компонента которого лежит на отрезке [-1,1], цвет (r,g,b), по следующему правилу:
1 2 3 |
r = (x + 1)/2 g = (y + 1)/2 b = (z + 1)/2 |
Полученный вектор (r,g,b) содержится в единичном кубе и является допустимым значением цвета (в рельных приложениях каждый компонент нужно умножить на 255 для перевода в диапазон значения байта).
Процедура построения нормирующей кубической карты:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
static void getCubeVector ( int side, int cubeSize, int x, int y, Vector3D& v ) { float s = ((float) x + 0.5f) / (float) cubeSize; float t = ((float) y + 0.5f) / (float) cubeSize; float sc = 2*s - 1; float tc = 2*t - 1; switch ( side ) { case 0: v = Vector3D ( 1, -tc, -sc ); break; case 1: v = Vector3D ( -1, -tc, sc ); break; case 2: v = Vector3D ( sc, 1, tc ); break; case 3: v = Vector3D ( sc, -1, -tc ); break; case 4: v = Vector3D ( sc, -tc, 1 ); break; case 5: v = Vector3D ( -sc, -tc, -1 ); break; } v.normalize (); } unsigned createNormalizationCubemap ( int cubeSize ) { Vector3D v; byte * pixels = (byte *) malloc ( 3 * cubeSize * cubeSize ); if ( pixels == NULL ) return 0; unsigned textureId; glGenTextures ( 1, &textureId ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, textureId ); glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); for ( int side = 0; side < 6; side++ ) { for ( int x = 0; x < cubeSize; x++ ) for ( int y = 0; y < cubeSize; y++ ) { int offs = 3 * (y * cubeSize + x); getCubeVector ( side, cubeSize, x, y, v ); pixels [offs ] = 128 + 127 * v.x; pixels [offs + 1] = 128 + 127 * v.y; pixels [offs + 2] = 128 + 127 * v.z; } glTexImage2D ( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side, 0, GL_RGB, cubeSize, cubeSize, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels ); } free ( pixels ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glDisable ( GL_TEXTURE_CUBE_MAP_ARB ); return textureId; } |
Если в вершинах грани задать нормированные значения вектора направления на источник света l, то в каждом пикселе грани мы получим нормированное значение вектора l, соответствующее данному пикселю. Правда это значение будет представлено в виде цвета.
Тогда можно нормаль для каждого пикселя задать в виде текстуры, где каждому значению единичного вектора нормали сопоставляется цвет.
Обычно считается, что грань расположена параллельно плоскости Оху, поэтому неискаженному вектору нормали (0,0,1) будет соответствовать цвет (0.5,0.5,1), имеющий голубоватый оттенок.
Обычно карта нормалей содержит значения, не сильно отличающиеся от (0,0,1), то обычные карты нормалей имеют голубовато-розоватый оттенок.
Для вычисления скалярного произведения можно использовать расширение ARB_texture_env_dot3, которое вычисляет следующую величину:
1 |
4*((Arg0.r-0.5)*(Arg1.r-0.5)+(Arg0.g-0.5)*(Arg1.g-0.5)+(Arg0.b-0.5)*(Arg1.b-0.5)) |
Если считать, что оба аргумента являются векторами, записанными в виде цвета, то с учетом формул преобразования векторов в цвет, это значение соответствует скалярному произведению двух векторов — Arg0 и Arg1.
Это расширение добавляет два режима к расширению ARB_texture_env_combine — GL_DOT3_RGB_ARB и GL_DOT3_RGBA_ARB. В первом случае вычисленное значение записывается в первые три компоненты цвета (RGB), а во втором — во все четыре (RGBA).
Рассмотрим теперь как можно реализовать простейшее попиксельное освещение с использованием нормирующих кубических карт и расширения ARB_texture_env_dot3.
За счет использования мультитекстурирования можно реализовать вычисление выражения max((l,n),0) за один проход.
В нулевом текстурном блоке располагается текстура, задающая карту нормалей. В качестве режима наложения используется GL_REPLACE. В качестве текстурных координат для него задаются нормальные координаты для текстуры с нормалями.
В первом текстурном блоке размещается нормирующая кубическая карта. Режимом наложения для нее является GL_DOT3_RGB_ARB. В качестве текстурных координат выступает нормированный вектор направления на источник света.
В качестве Arg0 используется GL_TEXTURE, а в качестве Arg1 — GL_PREVIOUS_ARB (в данном случае это будут значения с предыдущего текстурного блока, т.н. значение вектора нормали).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
// bind bump (normal) map to texture unit 0 glActiveTextureARB ( GL_TEXTURE0_ARB ); glBindTexture ( GL_TEXTURE_2D, bumpMap ); glEnable ( GL_TEXTURE_2D ); glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); glTexEnvi ( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE ); // bind normalization cube map to texture unit 1 glActiveTextureARB ( GL_TEXTURE1_ARB ); glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, normCubeMap ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); glTexEnvi ( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB ); |
С помощью второго прохода можно реализовать более сложную модель освещения:
1 |
I = С * max ( (l,n), 0 ) |
Здесь С соответствует собственному цвету пиксела, т.е. является еще одной текстурой. Собственный цвет пиксела модулируется освещенностью данного пиксела.
Для этого достаточно повторно вывести грань с использованием текстуры С и режимом смешения (blending) (GL_DST_COLOR, GL_ZERO).
РЕЗУЛЬТАТ КУРСОВОЙ РАБОТЫ
Внешний вид
ЛИТЕРАТУРА
- MSDN Library for Visual Studio 6.0
- Франк Луна: Введение в программирование трехмерных игр с DX9
- сайт http://www.steps3d.narod.ru
- Microsoft DirectX SDK (August 2006)
ПРИЛОЖЕНИЕ. ТЕКСТ ОСНОВНОЙ ЧАСТИ ПРОГРАММЫ
Отрывки из главного файла «kurs.cpp»:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 |
#include "gl.h" #include "floor.h" #include "spacesheep.h" #include "sky.h" #include "3dobject.h" #include "landscape.h" #include "myutils.h" #include "time.h" #include <float.h> #include "glTools.h" #include <fstream.h> void InitUserInterface(void); GLfloat g_fx,g_fy,g_fz,g_fAngle,g_fRotate; GLfloat g_fSpaceSheepY=-7.0; enum SITUATION{ S_GAME, S_EXPLOSION, }; SITUATION g_Situation=S_GAME; GLfloat g_fExplosionCounter; void OnKeyboard(char c, int x, int y); void OnSpecialKey(int key,int x, int y); void OnRender(void); void NewGame(void); unsigned textureCubeMapId; unsigned textureId; unsigned int textureBlastId; extern unsigned decalMap; GLubyte * textureData=NULL; GLubyte *scaledData=NULL; int g_ScaledWidth=128,g_ScaledHeight=128; int g_CopyWidth=g_ScaledWidth,g_CopyHeight=g_ScaledHeight,g_CopyX,g_CopyY; int main(int argc, char * argv[]) { try{ CGL::InitOpenGL("Курсовая работа по комптьютерной графике.",&argc,argv); ifstream infile("config.txt"); int nGlobalGraphQuality=40; int nSpacesheepGraphQuality=100; if (infile) { infile>>nGlobalGraphQuality; infile>>nSpacesheepGraphQuality; infile.close(); } InitSky(); InitFloor(); C3DObject::SetGlobalGraphQuality(nSpacesheepGraphQuality); InitSpacesheep(); C3DObject::SetGlobalGraphQuality(nGlobalGraphQuality); InitLandscape(); glGenTextures(1,&textureBlastId); glBindTexture(GL_TEXTURE_2D,textureBlastId); loadBmpTexture(GL_TEXTURE_2D,true,"blast2.bmp"); glGenTextures(1,&textureCubeMapId); glBindTexture(GL_TEXTURE_CUBE_MAP_ARB,textureCubeMapId); const char * maps [6] = { "cm_left.tga", "cm_right.tga", "cm_top.tga", "cm_bottom.tga", "cm_back.tga", "cm_front.tga", }; glGenTextures(1,&textureId); glBindTexture(GL_TEXTURE_2D,textureId); scaledData=new GLubyte[3*g_ScaledWidth*g_ScaledHeight]; if (!scaledData) throw "Not enought memory\n"; textureData=new GLubyte[3*g_CopyWidth*g_CopyHeight]; int rwidth=CGL::GetWidth(),rheight=CGL::GetHeight(); GLenum err; if (rheight<rwidth) g_CopyWidth=rheight; else g_CopyWidth=rwidth; g_CopyWidth=int(log(g_CopyWidth)/log(2)); g_CopyWidth=pow(2,g_CopyWidth); g_CopyHeight=g_CopyWidth; g_CopyWidth=g_ScaledWidth; g_CopyHeight=g_ScaledHeight; g_CopyX=rwidth/2-g_CopyWidth/2; g_CopyY=rheight/2-g_CopyHeight/2; g_CopyX=0; g_CopyY=0; glBindTexture(GL_TEXTURE_2D,textureId); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); gluBuild2DMipmaps ( GL_TEXTURE_2D, 3, g_CopyWidth, g_CopyHeight, GL_RGB, GL_UNSIGNED_BYTE, textureData); if ((err=glGetError())!=GL_NO_ERROR) printf("Error building 2DMipmaps: %i",err); NewGame(); //CGL::SetKeyboardProc(OnKeyboard); CGL::SetSpecialKeyProc(OnSpecialKey); CGL::SetRenderProc(OnRender); InitUserInterface(); CGL::MainLoop(); } catch(char *s) { fprintf(stderr,"%s",s); } catch(...) { fprintf(stderr,"A error detected"); } return 0; } void OnKeyboard(char c, int x, int y) { } void OnSpecialKey(int key,int x, int y) { switch (key) { case GLUT_KEY_LEFT: g_fRotate=-1.0; break; case GLUT_KEY_RIGHT: g_fRotate=1.0f; break; case GLUT_KEY_F2: NewGame(); break; } if (g_fAngle<0) g_fAngle=g_fAngle+360.0; if (g_fAngle>360) g_fAngle=g_fAngle-360.0; } void DrawWorld() { DrawSky(); //Прорисовка окружающего пейзажа glPushMatrix(); DrawLandscape(); glPopMatrix(); //Прорисовка пола glPushMatrix(); //glTranslatef(n,-2.0,-1.0); glRotatef(90,-1.0,0.0,0.0); glTranslatef(0.0,0.0,0.0); DrawFloor(); glPopMatrix(); } void SaveDataToFile(GLubyte *data0,int width,int height) { FILE *file=fopen("out.bin","w"); fwrite((const void*) data0,width*height*3,1,file); fclose(file); } void ScaleImage128(GLubyte *data0,GLubyte *scaledData) { int n,m; int nmmax=128*128*3-1; for (n=0; n<128;n++) { for (m=0;m<128;m++) { int x,y; x=n*3; y=m*128*3; scaledData[nmmax-x-y-2]=data0[x+y]; scaledData[nmmax-x-y-1]=data0[x+y+1]; scaledData[nmmax-x-y]=data0[x+y+2]; } } } void CopyImage128(GLubyte *data0,GLubyte *scaledData) { memcpy((void*)scaledData,(const void*)data0,128*128*3); } GLubyte * SaveScreenToCubeMap(unsigned int textureId,unsigned int textureCubeMapId,GLenum target,int Scale=1) { try{ GLubyte *data0=textureData; CreateScreenTexture(textureId,textureData,g_CopyX,g_CopyY,g_CopyWidth,g_CopyHeight); glGetTexImage(GL_TEXTURE_2D,0,GL_RGB,GL_UNSIGNED_BYTE,data0); glBindTexture(GL_TEXTURE_CUBE_MAP_ARB,textureCubeMapId); if (Scale) { ScaleImage128(data0,scaledData); } else { CopyImage128(data0,scaledData); } gluBuild2DMipmaps ( target, 3, g_ScaledWidth, g_ScaledHeight, GL_RGB, GL_UNSIGNED_BYTE, scaledData); return scaledData; } catch(GLenum err) { err=glGetError(); fprintf(stderr,"Error saving screen to cube map: %i\n",err); } catch(...) { fprintf(stderr,"Error saving screen to cube map\n"); } return NULL; } void SetupModelViewMatrix(void) { glLoadIdentity(); glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); } void SetupMatrixToSpaceSheep(void) { //glRotatef(g_fAngle,0.0,-1.0,0.0); //glTranslatef(-g_fx,-g_fy,-g_fz); //glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(0.0,0.0,0.0); //glTranslatef(-g_fx,-g_fy,-g_fz); } void OnRender(void) { GLfloat tx,ty,tz; ty=g_fy; tx=g_fx-(20.0*sin(g_fAngle/360*2*GLT_PI)); tz=g_fz+(20.0*cos(g_fAngle/360*2*GLT_PI));; if (IsInLandscape(tx,ty,tz)) g_Situation=S_EXPLOSION; float n=0,m=0; GLenum err; const FLOOR_SIZE=100; static float fSpacesheepAngle=0.0; SetupModelViewMatrix(); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); if (g_Situation==S_EXPLOSION) { const int dr=2; glPushMatrix(); if (g_fExplosionCounter<10) glTranslatef(float(rand()%dr-dr/2),float(rand()%dr-dr/2),float(rand()%dr-dr/2)); DrawWorld(); glPopMatrix(); glDisable(GL_LIGHTING); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D,textureBlastId); if (g_fExplosionCounter<10.0) { g_fExplosionCounter+=1.0; glPushMatrix(); glLoadIdentity(); glTranslatef(0.0,g_fSpaceSheepY,-20.0); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); //glEnable(GL_TEXTURE_GEN_Q); glTexGeni(GL_S,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); glTexGeni(GL_T,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); glTexGeni(GL_R,GL_TEXTURE_GEN_MODE,GL_OBJECT_LINEAR); glTexGeni(GL_Q,GL_OBJECT_PLANE,GL_SPHERE_MAP); glutSolidSphere(g_fExplosionCounter,C3DObject::GetGlobalGraphQuality(),C3DObject::GetGlobalGraphQuality()); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_TEXTURE_GEN_Q); if (g_fExplosionCounter>5) { GLUquadricObj * pObj; pObj=gluNewQuadric(); pObj = gluNewQuadric(); gluQuadricDrawStyle(pObj, GLU_FILL); gluQuadricNormals(pObj, GLU_SMOOTH); gluQuadricOrientation(pObj, GLU_INSIDE); //gluQuadricTexture(pObj, GLU_TRUE); glRotatef(90,1.0,0.0,0.0); float radius=g_fExplosionCounter*2; gluDisk(pObj,radius-4, radius, 10, 1); gluDeleteQuadric(pObj); } } glEnable(GL_LIGHTING); glDisable(GL_TEXTURE_2D); } else { GLfloat fStep=2.0; g_fx=g_fx-(fStep*sin(g_fAngle/360*2*GLT_PI)); g_fz=g_fz+(fStep*cos(g_fAngle/360*2*GLT_PI)); if (g_fRotate!=0.0) { g_fAngle+=g_fRotate*10.0; g_fRotate=0.0; } static GLubyte *data0,*data1,*data2,*data3,*data4,*data5; glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, textureCubeMapId ); err=glGetError(); glTexParameterf ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); bool mipmap=true; glViewport(0,0,g_CopyWidth,g_CopyHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(90,1,1.0,FLT_MAX); glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glRotatef(g_fAngle,0.0,1.0,0.0); glRotatef(90,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); //glRotatef(g_fAngle,0.0,-1.0,0.0); glTranslatef(0.0,g_fSpaceSheepY,0.0); //glRotatef(g_fAngle,0.0,1.0,0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); data0=SaveScreenToCubeMap(textureId,textureCubeMapId,GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB); glPopMatrix(); glPushMatrix(); glLoadIdentity(); glRotatef(90,0.0,-1.0,0.0); glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); glTranslatef(0.0,g_fSpaceSheepY,0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); data1=SaveScreenToCubeMap(textureId,textureCubeMapId,GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB); glPopMatrix(); glPushMatrix(); glLoadIdentity(); glRotatef(90,-1.0,0.0,0.0); glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); glTranslatef(0.0,g_fSpaceSheepY,0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); data2=SaveScreenToCubeMap(textureId,textureCubeMapId,GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB,0); glPopMatrix(); glPushMatrix(); glLoadIdentity(); glRotatef(90,1.0,0.0,0.0); glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); glTranslatef(0.0,g_fSpaceSheepY,0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); data3=SaveScreenToCubeMap(textureId,textureCubeMapId,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB,0); glPopMatrix(); glPushMatrix(); glLoadIdentity(); glRotatef(180,0.0,1.0,0.0); glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); glTranslatef(0.0,g_fSpaceSheepY,0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); data4=SaveScreenToCubeMap(textureId,textureCubeMapId,GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB); glPopMatrix(); glPushMatrix(); glLoadIdentity(); glRotatef(g_fAngle,0.0,1.0,0.0); glTranslatef(g_fx,g_fy,g_fz); glTranslatef(0.0,g_fSpaceSheepY,0.0); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); data5=SaveScreenToCubeMap(textureId,textureCubeMapId,GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB); glPopMatrix(); CGL::OnResize(CGL::GetWidth(),CGL::GetHeight()); //Обыкновенная прорисовка мира glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT |GL_STENCIL_BUFFER_BIT); CGL::SetPointLight(GL_LIGHT1,CGLVector3(1000.0,100.0,0.0),CGLColor(1.0,1.0,1.0)); DrawWorld(); glPushMatrix(); glLoadIdentity(); glTranslatef(0.0,g_fSpaceSheepY,-20.0); glRotatef(180,0.0,1.0,0.0); DrawSpacesheep(textureCubeMapId); glPopMatrix(); //glPopMatrix(); } } void OnHelp(void) { CGL::RemoveAllControls(); CGLControl*p; p=new CGLTextBox(-0.5,0.5,1.0,1.0,"Для управления используёте клавиши со стрелками. \ Программа создана Урвановым Фёдором Владиславовичем в 2007 году.\ Сайт: http://www.DXGames.narod.ru \ e-mail: urvanovf@hotmail.ru"); p= new CGLButton(-0.2,-0.6,0.4,0.1,"OK",InitUserInterface); } void InitUserInterface(void) { CGL::RemoveAllControls(); CGLControl *p; p=new CGLButton(-0.9,0.9,0.5,0.1,"Заново (F2)",NewGame); if (!p) throw 1; p=new CGLButton(0.5,0.9,0.5,0.1,"Справка",OnHelp); } void NewGame(void) { g_fx=-100.0; g_fy=-15.0; g_fz=-0.0; g_fAngle=0.0; g_fRotate=0.0; g_Situation=S_GAME; g_fExplosionCounter=0.0; } |
Файл «floor.cpp»:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
#include <windows.h> #include <stdio.h> #include <string.h> #include <gl/gl.h> #include <gl/glut.h> #include <gl/glaux.h> #include "glext.h" #include "Vector3D.h" #include "Vector2D.h" #include "myutils.h" #include "gl.h" extern "C" { #include "tga.h" } static Vector3D eye ( 2, 2, 2 ); // camera position static Vector3D light ( 1000.0,0.0, 100.0); // light position static unsigned normCubeMap; // normalization cubemap id static unsigned bumpMap; // normal map unsigned decalMap; // decal (diffuse) texture static float angle = 0; static Vector3D v [4]; static Vector2D t0 [4]; static Vector3D t1 [4]; static void getCubeVector ( int side, int cubeSize, int x, int y, Vector3D& v ) { float s = ((float) x + 0.5f) / (float) cubeSize; float t = ((float) y + 0.5f) / (float) cubeSize; float sc = 2*s - 1; float tc = 2*t - 1; switch ( side ) { case 0: v = Vector3D ( 1, -tc, -sc ); break; case 1: v = Vector3D ( -1, -tc, sc ); break; case 2: v = Vector3D ( sc, 1, tc ); break; case 3: v = Vector3D ( sc, -1, -tc ); break; case 4: v = Vector3D ( sc, -tc, 1 ); break; case 5: v = Vector3D ( -sc, -tc, -1 ); break; } v.normalize (); } unsigned createNormalizationCubemap ( int cubeSize ) { Vector3D v; byte * pixels = (byte *) malloc ( 3 * cubeSize * cubeSize ); if ( pixels == NULL ) return 0; unsigned textureId; glGenTextures ( 1, &textureId ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, textureId ); glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ); // set 1-byte alignment glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); for ( int side = 0; side < 6; side++ ) { for ( int x = 0; x < cubeSize; x++ ) for ( int y = 0; y < cubeSize; y++ ) { int offs = 3 * (y * cubeSize + x); getCubeVector ( side, cubeSize, x, y, v ); pixels [offs ] = 128 + 127 * v.x; pixels [offs + 1] = 128 + 127 * v.y; pixels [offs + 2] = 128 + 127 * v.z; } glTexImage2D ( GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB + side, 0, GL_RGB, cubeSize, cubeSize, 0, GL_RGB, GL_UNSIGNED_BYTE, pixels ); } free ( pixels ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glDisable ( GL_TEXTURE_CUBE_MAP_ARB ); return textureId; } PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL; PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB = NULL; PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1f = NULL; PFNGLMULTITEXCOORD1FVARBPROC glMultiTexCoord1fv = NULL; PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2f = NULL; PFNGLMULTITEXCOORD2FVARBPROC glMultiTexCoord2fv = NULL; PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3f = NULL; PFNGLMULTITEXCOORD3FVARBPROC glMultiTexCoord3fv = NULL; bool initMultitexture () { glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress ( "glActiveTextureARB" ); glClientActiveTextureARB = (PFNGLCLIENTACTIVETEXTUREARBPROC) wglGetProcAddress ( "glClientActiveTextureARB" ); glMultiTexCoord1f = (PFNGLMULTITEXCOORD1FARBPROC) wglGetProcAddress ( "glMultiTexCoord1fARB" ); glMultiTexCoord1fv = (PFNGLMULTITEXCOORD1FVARBPROC) wglGetProcAddress ( "glMultiTexCoord1fvARB" ); glMultiTexCoord2f = (PFNGLMULTITEXCOORD2FARBPROC) wglGetProcAddress ( "glMultiTexCoord2fARB" ); glMultiTexCoord2fv = (PFNGLMULTITEXCOORD2FVARBPROC) wglGetProcAddress ( "glMultiTexCoord2fvARB" ); glMultiTexCoord3f = (PFNGLMULTITEXCOORD3FARBPROC) wglGetProcAddress ( "glMultiTexCoord3fARB" ); glMultiTexCoord3fv = (PFNGLMULTITEXCOORD3FVARBPROC) wglGetProcAddress ( "glMultiTexCoord3fvARB" ); return glActiveTextureARB != NULL && glClientActiveTextureARB != NULL && glMultiTexCoord1f != NULL && glMultiTexCoord1fv != NULL && glMultiTexCoord2f != NULL && glMultiTexCoord2fv != NULL && glMultiTexCoord3f != NULL && glMultiTexCoord3fv != NULL; } /*void init () { glClearColor ( 0, 0, 0, 1 ); glEnable ( GL_DEPTH_TEST ); glDepthFunc ( GL_LEQUAL ); glHint ( GL_POLYGON_SMOOTH_HINT, GL_NICEST ); glHint ( GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST ); } */ void DrawFloor () { glPushAttrib(GL_ALL_ATTRIB_BITS); int i; glColor3f ( 1, 1, 1 ); // make 1st pass - diffuse intensity // bind bump (normal) map to texture unit 0 glActiveTextureARB ( GL_TEXTURE0_ARB ); glEnable ( GL_TEXTURE_2D ); glBindTexture ( GL_TEXTURE_2D, bumpMap ); // setup environment for dot3 glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); glTexEnvi ( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_REPLACE ); // bind normalization cube map to texture unit 1 glActiveTextureARB ( GL_TEXTURE1_ARB ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, normCubeMap ); // setup environment for dot3 glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_SOURCE0_RGB_ARB, GL_TEXTURE ); glTexEnvi ( GL_TEXTURE_ENV, GL_SOURCE1_RGB_ARB, GL_PREVIOUS_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_COMBINE_RGB_ARB, GL_DOT3_RGB_ARB ); // compute texture coordinates for normalization cibe map for ( i = 0; i < 4; i++ ) { t1 [i] = light - v [i]; t1 [i].normalize (); } // now draw quad glBegin ( GL_QUADS ); for ( i = 0; i < 4; i++ ) { glMultiTexCoord2fv ( GL_TEXTURE0_ARB, t0 [i] ); glMultiTexCoord3fv ( GL_TEXTURE1_ARB, t1 [i] ); glVertex3fv ( v [i] ); } glEnd (); // make 2nd pass - modulate by decal glActiveTextureARB ( GL_TEXTURE0_ARB ); glBindTexture ( GL_TEXTURE_2D, decalMap ); glActiveTextureARB ( GL_TEXTURE1_ARB ); glDisable ( GL_TEXTURE_CUBE_MAP_ARB ); glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE ); glEnable ( GL_BLEND ); glBlendFunc ( GL_DST_COLOR, GL_ZERO ); // now draw quad glBegin ( GL_QUADS ); for ( i = 0; i < 4; i++ ) { glMultiTexCoord2fv ( GL_TEXTURE0_ARB, t0 [i] ); glVertex3fv ( v [i] ); } glEnd (); glDisable ( GL_BLEND ); glDisable ( GL_TEXTURE_2D ); glPopAttrib(); //glutSwapBuffers (); } /* void reshape ( int w, int h ) { glViewport ( 0, 0, (GLsizei)w, (GLsizei)h ); glMatrixMode ( GL_PROJECTION ); glLoadIdentity (); gluPerspective ( 60.0, (GLfloat)w/(GLfloat)h, 1.0, 60.0 ); glMatrixMode ( GL_MODELVIEW ); glLoadIdentity (); gluLookAt ( eye.x, eye.y, eye.z, // eye 0, 0, 0, // center 0.0, 0.0, 1.0 ); // up } void key ( unsigned char key, int x, int y ) { if ( key == 27 || key == 'q' || key == 'Q' ) //quit requested exit ( 0 ); } void animate () { angle = 0.004f * glutGet ( GLUT_ELAPSED_TIME ); light.x = cos ( angle ); light.y = sin ( angle ); light.z = 1 + 0.3 * sin ( angle / 3 ); glutPostRedisplay (); } */ void InitFloor() { printf("Initializing floor..."); WriteOk(); const char * vendor = (const char *)glGetString ( GL_VENDOR ); const char * renderer = (const char *)glGetString ( GL_RENDERER ); const char * version = (const char *)glGetString ( GL_VERSION ); const char * extension = (const char *)glGetString ( GL_EXTENSIONS ); printf ( "Vendor: %s\nRenderer: %s\nVersion: %s\n", vendor, renderer, version ); if ( !isExtensionSupported ( "GL_ARB_texture_env_combine" ) ) { printf ( "ARB_texture_env_combine NOT supported.\n" ); throw 1; } if ( !isExtensionSupported ( "GL_ARB_texture_cube_map" ) ) { printf ( "ARB_texture_cube_map NOT supported.\n" ); throw 1; } if ( !isExtensionSupported ( "GL_ARB_texture_env_dot3" ) ) { printf ( "ARB_texture_env_dot3 NOT supported.\n" ); throw 1; } if (!initMultitexture ()) throw "Error initializing multitexture\n"; // Строим пол const float FLOOR_SIZE=1000.0; v [0] = Vector3D ( -FLOOR_SIZE, -FLOOR_SIZE,0 ); v [1] = Vector3D ( FLOOR_SIZE, -FLOOR_SIZE,0 ); v [2] = Vector3D ( FLOOR_SIZE , FLOOR_SIZE,0 ); v [3] = Vector3D ( -FLOOR_SIZE, FLOOR_SIZE,0 ); // устанавливаем текстурные координаты bumpmap const float DIVIDER =10.0; t0 [0] = Vector2D ( 0, 0 ); t0 [1] = Vector2D ( FLOOR_SIZE/DIVIDER, 0 ); t0 [2] = Vector2D ( FLOOR_SIZE/DIVIDER, FLOOR_SIZE /DIVIDER); t0 [3] = Vector2D ( 0, FLOOR_SIZE /DIVIDER); bumpMap = createTexture2D ( true, "normal2.bmp" ); decalMap = createTexture2D ( true, "block.bmp" ); normCubeMap = createNormalizationCubemap ( 32 ); glDepthFunc ( GL_LEQUAL ); } |
Файл «spacesheep.cpp»:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 |
#include <windows.h> #include <stdio.h> #include <string.h> #include <gl/gl.h> #include <gl/glut.h> #include <gl/glaux.h> #include "glext.h" #include "Vector3D.h" #include "Vector2D.h" #include "3dobject.h" #include "myutils.h" C3DObject *g_p3doSpacesheep1=NULL; extern "C" { #include "tga.h" } float SpacesheepAngle = 0; float SpacesheepAngle2 = 0; int reflMode = GL_REFLECTION_MAP_ARB; //int reflMode = GL_NORMAL_MAP_ARB; int object = 0; //unsigned texture; void DrawSpacesheep (unsigned texture) { GLenum err; glPushAttrib(GL_ALL_ATTRIB_BITS); glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 ); // set 1-byte alignment glTexParameterf ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); glTexGeni ( GL_S, GL_TEXTURE_GEN_MODE, reflMode ); glTexGeni ( GL_T, GL_TEXTURE_GEN_MODE, reflMode ); glTexGeni ( GL_R, GL_TEXTURE_GEN_MODE, reflMode ); glEnable ( GL_TEXTURE_CUBE_MAP_ARB ); glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, texture ); if ((err=glGetError())!=GL_NO_ERROR) printf("Error binding texture cube map: %i",err); glTexParameterf ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE ); glTexParameterf ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE ); //glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP ); //glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP ); glEnable ( GL_TEXTURE_GEN_S ); glEnable ( GL_TEXTURE_GEN_T ); glEnable ( GL_TEXTURE_GEN_R ); glDisable(GL_LIGHTING); g_p3doSpacesheep1->Draw(); //glutSolidSphere(5,100,100); glEnable(GL_LIGHTING); glPopAttrib(); } void InitSpacesheep() { printf("Initializeing cube map..."); if ( !isExtensionSupported ( "GL_ARB_texture_cube_map" ) ) { printf ( "ARB_texture_cube_map NOT supported.\n" ); throw 1; } WriteOk(); printf("Loading spacesheep..."); g_p3doSpacesheep1=new C3DObject("spacesheep1"); WriteOk(); } |
Скачать ZIP-архив с исходными кодами курсовой работы по дисциплине «Компьютерная графика»