Войти
ПрограммированиеСтатьиГрафика

Использование CubeMap техники в OpenGL.

Текстурирование — это технология, изобретенная уже бог знает в каком году, никогда не потеряет свою актуальность, так как это единственный способ сделать трехмерный мир на экране монитора более красивым. Самым красивым как это только можно себе представить. Поэтому, если и говорить слово «красота» в трехмерном мире, то обязательно подразумевать под этим словом — «текстура».

Автор: terror

Генерация CubeMap
Заполняем и рисуем CubeMap

Генерация CubeMap

Что собственно представляет собой текстура? Текстура - это массив цветовых точек, образующих изображение. Текстуры бывают разного формата, есть RGB, есть с альфа каналом (RGBA), монохромные и много других. Изобретений в области текстуринга не меньше чем в любой другой области. Мультитекстурирование, сжатие текстур, полупрозрачность, прозрачность. Много было придумано способов наложения текстур: прижатие текстур, масштабирование текстур, проекционные текстуры, текстуры окружения (Environment mapping). А сколько было приложено усилий для совершенствования текстурных фильтров. Точечная выборка, линейная фильтрация, техника mip-map, билинейная фильтрация, трилинейная и анизотропная фильтрации. А также текстуры бывают одномерными(1D), двумерными(2D), трехмерными(3D), а бывают CubeMap текстуры. Вот об этом и поговорим.

Прежде чем приступим, хотелось бы напомнить, что техника CubeMap может не поддерживаться старыми видео картами. Поэтому смотрите строку glGetString(GL_EXTENSIONS); И ищите там расширение GL_ARB_texture_cube_map. (Что такое расширение для OpenGL можно узнать из статьи OpenGL: Extensions (Расширения))

Если текстура помечена как CubeMap, то она представляет собой шесть различных текстур:

Изображение

Каждой части куба соответствует своя картинка. Они могут быть и одинаковые, правда тогда уже не будет смысла в CubeMap технике. При этом идентификатор CubeMap'а всего один. Это обычная переменная типа unsigned int, которая и будет хранить id всех шести текстур. При генерации обычной 2D (или другой) текстуры OpenGL знает о том, что текстура есть 2D. При ее создании в функцию glBindTexture() передается параметр GL_TEXTURE_2D и идентификатор текстуры. В нашем случае нужно передавать в качестве первого аргумента функции glBindTexture() параметр GL_TEXTURE_CUBE_MAP_ARB. Теперь OpenGL знает о том, что мы хотим использовать CubeMap текстуры, и, разумеется, будет ждать от нас дальнейших корректных по отношению к данной технике действий.

Поэтому генерация CubeMap выглядит так:

glGenTextures ( 1, &CubeMap );
glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, CubeMap );

Где CubeMap  является идентификатором текстуры.
Дальше все как обычно. Настраиваем нужные нам параметры у текстуры, ставим нужные фильтры.

Теперь подошли к самому главному в процессе генерации CubeMap текстур.

Это процесс создания mip-map уровней для текстуры. Дело в том, что на этапе генерации текстур для CubeMap'а мы не знаем какими именно данными его заполнять. В нашем случае данные мы будем получать по ходу выполнения программы. Поэтому использовать обычный gluBuild2Dmipmaps() мы не можем, так как он не принимает в качестве последнего параметра значение NULL. А если так сделать, то программа вылетит с ошибкой.

Поэтому нужно использовать стандартное glTexImage2D(). А как генерировать mip-map спросите вы? В данном случае за генерацию mip-map будет отвечать параметр GL_GENERATE_MIPMAP_SGIS. Также стоит заметить, что первым аргументов в функции glTexImage2D() идет значение "target". Так вот при генерации каждой пары шести текстур нужен разный параметр target.

У первых двух он равен GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB.
У вторых двух он равен GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB
У третьих двух он равен GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB

А теперь весь процесс генерации CubeMap:

CubeMapTarget[0] = GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB;
CubeMapTarget[1] = GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB;
CubeMapTarget[2] = GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB;
CubeMapTarget[3] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB;
CubeMapTarget[4] = GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB;
CubeMapTarget[5] = GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB;
glEnable ( GL_TEXTURE_CUBE_MAP_ARB );
glGenTextures ( 1, &texCubeMap );
glPixelStorei ( GL_UNPACK_ALIGNMENT, 1 );
glBindTexture ( GL_TEXTURE_CUBE_MAP_ARB, texCubeMap );
glTexEnvi ( GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE );
glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB,  GL_TEXTURE_WRAP_S,GL_REPEAT);
glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB,  GL_TEXTURE_WRAP_T,GL_REPEAT);
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 );
glTexParameteri ( GL_TEXTURE_CUBE_MAP_ARB, GL_GENERATE_MIPMAP_SGIS, GL_TRUE );
for ( int i = 0; i < 6; i++ )
  glTexImage2D  ( CubeMapTarget[i], 0, GL_RGB, CUBEMAP_SIZE, CUBEMAP_SIZE, 0, GL_RGB,
         GL_UNSIGNED_BYTE, NULL);
glDisable(GL_TEXTURE_CUBE_MAP_ARB);

Заполняем и рисуем CubeMap

После того как OpenGL получил все необходимые сведенья о будущей CubeMap текстуре, он будет ждать от вас дальнейших не менее корректных действий. Процесс инициализации всего, что нам было нужно уже закончен. А что идет после этого? Правильно, после этого или программа вылетает с ошибкой, или программа переходит в цикл обработки сообщений.

Если все прошло успешно, то давайте говорить о том, как заполнить CubeMap данными.

Фишка CubeMap'а заключается в том, что путем получения всех шести текстур, OpenGL может генерировать текстурные координаты для любого объекта, который будет использовать CubeMap. Так как получить эти шесть текстур?

Все шесть текстур CubeMap'а представляют собой текстуры простейшего окружения для той точки пространства, в которой находится центр куба. Проще говоря, нам нужно сфотографировать то, что находится во всех шести направлениях куба. То есть снизу, сверху, слева, справа, впереди, сзади. Получив все шесть изображений, мы можем строить наш CubeMap.

Чтобы запечатлеть все шесть изображений можно воспользоваться известным методом под названием "рендер в текстуру". Суть его состоит в том, что мы рисуем нашу сцену в маленькую текстуру (допустим 256x256), а затем снимаем полученное изображение и записываем его в видеопамять. Для того чтобы рендер в текстуру имел силу нужно установить новый ViewPort, который будет равен размеру CubeMap текстуры. Также желательно делать сдвиг ViewPort'а, так как в противном случае на текстурах начинают появляться нежелательные артефакты, а исправно будет работать только полноэкранный режим. Затем устанавливаем перспективу. Желательно zNear в параметрах перспективы делать больше единицы.

Затем мы пробегаемся по всем шести текстурам и заполняем их.

А делается это так:
1. Чистим буфер цвета и буфер глубины (glClear).
2. Устанавливаем единичную матрицу (glLoadIdentity).
3. Затем мы должны выполнить повороты камеры во все шесть направлений. Еще раз повторю снизу, сверху, слева, справа, впереди, сзади. Делается это обычным glRotate(). Сразу же нужно перенестись в точку соответствующую центру куба.
4. Рисуем ВСЮ сцену.
5. Активизируем работу с CubeMap'ами glEnable ( GL_TEXTURE_CUBE_MAP_ARB ). Затем нужно сделать текстурный идентификатор CubeMap'а активным через glBindTexture(). Снимаем все данные с frame buffer'а и заносим их в видеопамять активной текстуры через glCopyTexSubImage2D(). Заканчиваем работу с CubeMap'ами вызвав glDisable ( GL_TEXTURE_CUBE_MAP_ARB ).
6. Восстанавливаем  перспективу для камеры.

Данные получены и OpenGL готов к дальнейшей работе.

Для того чтобы нарисовать наш CubeMap, нужно воспользоваться одним из двух способов генерации текстурных координат.

Первый способ это Reflection map (карта отражения). Прекрасно подходит для создания эффекта окружения (Environment mapping). Другими словами аля GL_SPHERE_MAP, но только жутко продвинутый вариант. Второй способ это Normal map (карта нормали). Более строгий вариант Reflection map. В данном случае будет обычное отражение без эффекта искажения.

При генерации текстурных координат OpenGL использует специальные s, t и r текстурные координаты. Которые в свою очередь обрабатываются программой как векторы направления (direction vector), которые берут начало в центре куба. То есть другими словами каждая модель, которая будет использовать Cube Map представляется OpenGL как некий куб из центра, которого идут вектора направления rx, ry и rz. Далее OpenGL использует выражение для нахождения текстурных координат:

s = ( sc / |ma| + 1 ) / 2 

t = ( tc / |ma| + 1 ) / 2

Значения s и t это есть те самые текстурные координаты которые нужно посчитать. Значение sc, tc и ma берется по таблице:

major axis
direction
targetsctcma
+rx GL_TEXTURE_CUBE_MAP_POSITIVE_X_EXT -rz -ry rx
-rx GL_TEXTURE_CUBE_MAP_NEGATIVE_X_EXT +rz -ry rx
+ry GL_TEXTURE_CUBE_MAP_POSITIVE_Y_EXT +rx +rz ry
-ry GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_EXT +rx -rz ry
+rz GL_TEXTURE_CUBE_MAP_POSITIVE_Z_EXT +rx -ry rz
-rz GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_EXT -rx -ry rz

То есть для каждого
POSITIVE_X, NEGATIVE_X,
POSITIVE_Y, NEGATIVE_Y,
POSITIVE_Z, NEGATIVE_Z
берется выражение и считаются текстурные координаты.

Стоит заметить, что при рисовании CubeMap'а нужно немного порыться в их текстурных координатах. Если оставить "все как есть", то наша сфера (в примере к этой статье на сферу накладывается CubeMap) будет похожа на спрайт все время поворачивающийся к камере. Но мы проделывали такие усилия не для того, чтобы создать спрайт таким извращенским методом. Поэтому будем добиваться своего. А чтобы этого добиться, нужно получить матрицу модели, транспонировать ее и поместить в текстурную матрицу для текстурных координат нашей сферы, делается это через glLoadMatrixf().

Вот что получилось:

Изображение

Более подробно об использовании CubeMap'ов можно узнать из примера прилагаемого к этой статье. Все как обычно, VC++7.0, glut приложение. Также пример базируется на моей предыдущей статье Вершинное освещение совместно с LightMaps. Но объектов на сцене теперь четыре. Текстуры взяты из UT2003.

Ну вот и все. Пора закругляться.
Если есть комментарии, пишите
Всем спасибо за внимание.

Иходник к статье 20030729.zip (193 kB).

#CubeMap, #расширения OpenGL, #OpenGL, #основы, #текстурирование

30 июля 2003 (Обновление: 17 сен 2009)

Комментарии [17]