Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

RedBook

.pdf
Скачиваний:
20
Добавлен:
11.06.2015
Размер:
7.43 Mб
Скачать

Перечисленные ранее режимы хранения пикселей остаются полезными для описания двух из трех измерений, но для поддержки разрешения частичных объемов изображения трехмерной текстуры требуются дополнительные режимы. Новые параметры *IMAGE_HEIGHT и *SKIP_IMAGES позволяют командам glTexImage3D(), glTexSubImage3D() и glGetTexImage() получать доступ к любому желаемому частичному объему.

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

необходимо указать высоту одного частичного изображения с помощью параметра *IMAGE_HEIGHT. Также, если частичный объем начинается не с самого первого слоя, следует установить параметр *SKIP_IMAGES.

*IMAGE_HEIGHT – это параметр хранения пикселей, который определяет высоту (количество рядов) одного слоя изображения трехмерной текстуры. Если значение *IMAGE_HEIGHT равно 0 (отрицательная величина не является допустимой), число рядов в каждом двумерном прямоугольнике имеет величину height, являющуюся аргументом команд glTexImage3D() или glTexSubImage3D(). (Эта ситуация встречается часто, поскольку величиной по умолчанию для *IMAGE_HEIGHT является именно 0.) В ином случае высота одного слоя равна величине *IMAGE_HEIGHT.

Рисунок 9-4 показывает, как *IMAGE_HEIGHT задает высоту изображения (когда параметр height задает только высоту частичного изображения). На рисунке изображена трехмерная текстура с двумя слоями.

Рисунок 9-4. Режим хранения пикселей *IMAGE_HEIGHT

*SKIP_IMAGES определяет, сколько слоев нужно пройти до того, как можно будет получить доступ к данным частичного объема. Если *SKIP_IMAGES равно положительному целому числу (назовем его значением n), то указатель в данных изображения текстуры сдвигается на это число слоев (то есть на n*размер одного слоя тэкселей). Результирующий частичный объем начинается со слоя n и продолжается на несколько слоев вглубь на сколько вглубь, определяет аргумент depth, передаваемый

командам glTexImage3D() или glTexSubImage3D(). Если *SKIP_IMAGES равно 0 (значение по умолчанию), доступ к данным тэкселей начинается с самого первого слоя, описанного в тэксельном массиве.

Рисунок 9-5 показывает, как параметр *SKIP_IMAGES проходит через несколько слоев, чтобы попасть туда, где в действительности расположен нужный частичный объем. В данном примере *SKIP_IMAGES равно 3, и частичный объем начинается со слоя 3.

Рисунок 9-5. Режим хранения пикселей *SKIP_IMAGES

9.2.5Использование границ текстуры

Дополнительно: Если вам нужно использовать большую текстуру, чем позволяет ваша реализация OpenGL, вы можете эффективно создавать большие текстуры, заполняя пространство текстурами меньшего размера. Например, если вам нужно наложить на квадрат текстуру размером вдвое большим, чем максимально допустимый, нарисуйте квадрат в виде 4-ех квадратов меньшего размера, и на каждый из них наложите текстуру, представляющую собой четверть изображения исходной, большой текстуры (загружая новую четверть перед рисованием каждого из 4-ех квадратов).

Поскольку в каждый конкретный момент доступна только одна карта текстуры, такой подход может привести к проблемам на ребрах текстур, особенно если используется одна из разновидностей линейной фильтрации. Значение текстуры, используемой для пикселей на ребрах, должно вычисляться с использованием чего-то за этими ребрами, в идеале, с чем-то, находящимся в соседней карте текстуры. Если для каждой текстуры вы определите границу, чьи величины тэкселей равны величинам тэкселей на ребре соседней карты текстуры, то при линейной фильтрации будет обеспечено корректное поведение.

Чтобы сделать это правильно, имейте в виду, что каждая текстура может иметь 8 соседей по одной текстуре, прилегающей к каждому ребру и по одной, касающейся каждого из углов. Величины тэкселей в углах границы должны соответствовать

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

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

9.2.6Несколько уровней детализации

Дополнительно: Текстурированные объекты, как и все остальные объекты сцены, могут рассматриваться с разных дистанций от точки наблюдения. В динамической

сцене в процессе перемещения текстурированного объекта дальше от точки наблюдения карта текстуры должна уменьшаться в размере вместе с размером проецируемого изображения. Чтобы достичь этого OpenGL должна фильтровать карту текстуры при ее уменьшении до размера, нужного для наложения на объект, таким образом, чтобы избежать появления визуальных артефактов. Например, чтобы визуализировать каменную стену, вы можете использовать большое изображение текстуры (скажем 128x128 тэкселей), если стена находится близко к наблюдателю. Однако если стена постепенно перемещается дальше от наблюдателя до тех пор, пока не будет размером с один пиксель, фильтрованная текстура может изменяться прерывисто, в определенные моменты.

Чтобы избежать подобных неприятностей, вы можете задавать серии предварительно отфильтрованных карт текстуры с разными разрешениями (128x128, 64x64, 32x32 и так далее). Эти карты называются мипмапами и показаны на рисунке 9-6. Термин мипмап (mipmap) был введен Лансом Вильямсом (Lance Williams), когда он представлял эту идею в своей статье «Пирамидальные параметрики» («Pyramidal Parametrics») (SIGGRAPH1983). «Mip» происходит от латинского «multum in parvo», что означает «много вещей в одном месте». Мипмаппинг использует некоторые ухищренные методы упаковки данных в памяти.

Рисунок 9-6. Мипмапы

Замечание: Для полного понимания мипмапов, вам требуется понимание уменьшающих фильтров.

При использовании мипмаппинга, OpenGL автоматически определяет, какую текстурную карту нужно использовать в зависимости от размера (в пикселях) текстурируемого объекта. При таком подходе уровень детализации текстурной карты соответствует изображению, рисуемому на экране в то время, как объект на экране становится меньше, сокращается размер карты текстуры. Мипмаппинг требуется некоторых дополнительных расчетов и текстурной памяти; однако если его не использовать, текстуры, накладываемые на маленькие объекты, могут искажаться и мигать в процессе перемещения этих объектов.

Чтобы использовать мипмаппинг вы должны предоставить вашу текстуру во всех размерах, равных степеням 2, от самого большого до размера 1x1. Например, если наибольшее разрешение карты составляет 64x16, вы должны также предоставить карты с размерами 32x8, 16x4, 8x2, 4x1, 2x1 и 1x1. Меньшие карты обычно представляют собой фильтрованные и уменьшенные версии больших. Каждый тэксель в меньшей карте является средним между 4 соответствующими тэкселями в большей. (Поскольку OpenGL не накладывает никаких ограничений на метод вычисления меньших карт, карты текстуры разного размера могут быть абсолютно не связаны

между собой. На практике несвязанные мипмапы могут сделать переходы между ними визуально весьма заметными, как на рисунке 9-7.)

Чтобы задать все текстуры, вызовите команду glTexImage2D() по одному разу для каждого разрешения карты текстуры, каждый раз задавая новые значения для аргументов level, width, height и image. Начиная с 0, level идентифицирует, какая именно текстура в серии задается в текущий момент. В предыдущем примере тектура с самым высоким разрешением размера 64x16, должна быть объявлена с аргументом level=0, текстура размера 32x8 – с level=1, и так далее. Кроме того, чтобы мипмаппинг заработал, вам следует выбрать один из режимов фильтрации.

Замечание: В данном описании процесса мипмаппинга в OpenGL отсутствует

подробное обсуждение фактора масштаба (известного как ). Оно также приводится в предположении, что параметры GL_TEXTURE_MIN_LOD, GL_TEXTURE_MAX_LOD, GL_TEXTURE_BASE_LEVEL и GL_TEXTURE_MAX_LEVEL имеют значения по умолчанию. (Эти 4 параметра появились в OpenGL версии 1.2.)

Объяснение фактора и воздействия параметров приводится далее в этой главе.

Пример 9-5 иллюстрирует использование серии из 6 текстурных карт, уменьшающихся от размера 32x32 до размера 1x1. Эта программа рисует один длинный прямоугольник, который начинается на переднем плане и уходит вглубь до тех пор, пока не превращается в точку, как показано на рисунке 9-7. Обратите внимание, что координаты текстуры ранжируются от 0.0 до 8.0, таким образом, для покрытия всего прямоугольника требуется 64 копии текстуры (по 8 в каждом направлении). Для иллюстрации того, как одна карта текстуры продолжает другую, каждая из них имеет свой цвет.

Рисунок 9-7. Пример применения мипмаппинга

Пример 9-5. Мипмаппинг: файл mipmap.cpp

#include <glut.h>

GLubyte mipmapImage32[32][32][4];

GLubyte mipmapImage16[16][16][4];

GLubyte mipmapImage8[8][8][4];

GLubyte mipmapImage4[4][4][4];

GLubyte mipmapImage2[2][2][4];

GLubyte mipmapImage1[1][1][4];

GLuint texName;

void makeImages()

{

int i,j;

for (i=0;i<32;i++)

{

for (j=0;j<32;j++)

{

mipmapImage32[i][j][0]=255;

mipmapImage32[i][j][1]=255;

mipmapImage32[i][j][2]=0;

mipmapImage32[i][j][3]=255;

}

}

for (i=0;i<16;i++)

{

for (j=0;j<16;j++)

{

mipmapImage16[i][j][0]=255;

mipmapImage16[i][j][1]=0;

mipmapImage16[i][j][2]=255;

mipmapImage16[i][j][3]=255;

}

}

for (i=0;i<8;i++)

{

for (j=0;j<8;j++)

{

mipmapImage8[i][j][0]=255;

mipmapImage8[i][j][1]=0;

mipmapImage8[i][j][2]=0;

mipmapImage8[i][j][3]=255;

}

}

for (i=0;i<4;i++)

{

for (j=0;j<4;j++)

{

mipmapImage4[i][j][0]=0;

mipmapImage4[i][j][1]=255;

mipmapImage4[i][j][2]=0;

mipmapImage4[i][j][3]=255;

}

}

for (i=0;i<2;i++)

{

for (j=0;j<2;j++)

{

mipmapImage2[i][j][0]=0;

mipmapImage2[i][j][1]=0;

mipmapImage2[i][j][2]=255;

mipmapImage2[i][j][3]=255;

}

}

mipmapImage1[i][j][0]=255;

mipmapImage1[i][j][1]=255;

mipmapImage1[i][j][2]=255;

mipmapImage1[i][j][3]=255;

}

void init()

{

glEnable(GL_DEPTH_TEST); glShadeModel(GL_FLAT);

glTranslatef(0.0,0.0,-3.6); makeImages(); glPixelStorei(GL_UNPACK_ALIGNMENT,1);

glGenTextures(1,&texName); glBindTexture(GL_TEXTURE_2D,texName); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_REPEAT); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPMAP_NEAREST);

glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,32,32,0, GL_RGBA,GL_UNSIGNED_BYTE,mipmapImage32); glTexImage2D(GL_TEXTURE_2D,1,GL_RGBA,16,16,0, GL_RGBA,GL_UNSIGNED_BYTE,mipmapImage16); glTexImage2D(GL_TEXTURE_2D,2,GL_RGBA,8,8,0, GL_RGBA,GL_UNSIGNED_BYTE,mipmapImage8); glTexImage2D(GL_TEXTURE_2D,3,GL_RGBA,4,4,0, GL_RGBA,GL_UNSIGNED_BYTE,mipmapImage4); glTexImage2D(GL_TEXTURE_2D,4,GL_RGBA,2,2,0, GL_RGBA,GL_UNSIGNED_BYTE,mipmapImage2); glTexImage2D(GL_TEXTURE_2D,5,GL_RGBA,1,1,0, GL_RGBA,GL_UNSIGNED_BYTE,mipmapImage1);

glTexEnvf(GL_TEXTURE_ENV,GL_TEXTURE_ENV_MODE,GL_REPLACE); glEnable(GL_TEXTURE_2D);

}

void display()

{

glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT); glBindTexture(GL_TEXTURE_2D,texName);

glBegin(GL_QUADS);

glTexCoord2f(0.0,0.0); glVertex3f(-2.0,-1.0,0.0); glTexCoord2f(0.0,8.0); glVertex3f(-2.0,1.0,0.0); glTexCoord2f(8.0,8.0); glVertex3f(2000.0,1.0,-6000.0); glTexCoord2f(8.0,0.0); glVertex3f(2000.0,-1.0,-6000.0); glEnd();

glFlush();

}

void reshape(int w,int h)

{

glViewport(0,0,w,h); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(60.0,w/h,1.0,30000); glMatrixMode(GL_MODELVIEW); glLoadIdentity();

}

int main(int argc, char **argv)

{

glutInit(&argc,argv); glutInitDisplayMode(GLUT_SINGLE|GLUT_RGB|GLUT_DEPTH); glutInitWindowSize(500,500);

glutInitWindowPosition(100,100); glutCreateWindow("Mipmap Textures"); init();

glutDisplayFunc(display);

glutReshapeFunc(reshape);

glutMainLoop(); return 0;

}

Пример 9-5 иллюстрирует мипмаппинг с помощью мипмапов разного цвета. Таким образом, точки, в которых одна карта переходит в другую, очевидны. В реальных условиях мипмапы обычно создаются таким образом, чтобы переходы между ними были настолько плавными и незаметными, насколько это возможно. Таким образом, карты

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

9.2.6.1Автоматическое создание мипмапов

Предполагая, что вы построили текстурную карты с высочайшим разрешением уровня

0, функции gluBuild1DMipmaps(), gluBuild2DMipmaps() и gluBuild3DMipmaps()

конструируют и создают пирамиду мипмапов до разрешения 1x1 (или 1 для одномерной текстуры, или 1x1x1 для трехмерной). Если размеры вашего изображения не являются степенями 2, функции gluBuild*DMipmaps() масштабируют его до ближайших степеней 2. Кроме того, если ваша текстура слишком велика, gluBuild*DMipmaps() сократит ее до допустимого размера (с помощью механизма текстурного прокси).

int gluBuild1DMipmaps (GLenum target, GLint internalFormat, GLint width, GLenum format, GLenum type, void *texels);

int gluBuild2DMipmaps (GLenum target, GLint internalFormat, GLint width, GLint height, GLenum format, GLenum type, void *texels);

int gluBuild3DMipmaps (GLenum target, GLint internalFormat, GLint width, GLint height,

GLint depth, GLenum format, GLenum type, void *texels);

Конструирует серию мипмапов и вызывает glTexImage*D() для загрузки изображений. Назначение аргументов target, internalFormat, width, height, depth, format, type и texels в точности соответствует тем, что передаются в glTexImage1D(), glTexImage2D() и glTexImage3D(). Если все мипмапы сконструированы успешно, функция возвращает 0. В случае неудачи возвращается код ошибки GLU.

9.2.6.2Дополнительные детали мипмаппинга

Расчет мипмаппинга зависит от фактора масштаба между изображением текстуры и размером текстурируемого полигона (в пикселях). Давайте назовем этот фактор и

определим вторую величину -- , причем . (Поскольку изображения текстуры могут лежать в нескольких измерениях, важно прояснить, что -- это

максимальный фактор масштаба во всех измерениях.) Если , то текстура

меньше, чем полигон, и используется увеличивающий фильтр. Если же , используется уменьшающий фильтр. Если выбранный уменьшающий фильтр использует

мипмаппинг, то означает уровень используемого мипмапа.

Например, если размер изображения текстуры 64x64 тэкселя, а размер полигона -- 32x32 пикселя, то (а не 4.0) и . Если изображение текстуры 64x32 тэкселя, а размер полигона -- 8x16 пикселей, то (масштаб по x равен 8.0, а по y –2.0) и .

Замечание: Точкой равновесия между применением уменьшающего фильтра и

увеличивающего обычно является , но это не всегда так. Если в качестве увеличивающего фильтра выбран GL_LINEAR, а в качестве уменьшающего –

GL_NEAREST_MIPMAP_NEAREST или GL_NEAREST_MIPMAP_LINEAR, то эта точка

находится в . Это предотвращает ситуацию, когда уменьшенная текстура выглядит более резкой, чем ее увеличенная версия.

До OpenGL версии 1.2 применение мипмаппинга накладывало на программиста дополнительные ограничения. Например, нельзя было «на лету» производить такие изменения, как добавление новых мипмапов. Кроме того, в течение процесса уменьшения или увеличения полигона, текстура могла изменяться скачками в точках замены одного мипмапа другим, разрешение которого радикально выше.

В добавление к этому приходилось предоставлять мипмапы для всех уровней разрешения, даже для невероятно маленьких. При применении некоторых техник следует избегать представления данных в виде очень маленьких мипмапов. Например, вам может пригодиться техника мозаики, когда несколько небольших изображений помещены на одной текстуре. Один из примеров мозаики показан на рисунке 9-8, где изображен набор символов, помещенный на одной текстуре. использование мозаики в данном случае более эффективно, чем создание отдельной текстуры для каждого символа. Для наложения на полигон одной буквы, вам следует произвести расчет координат текстуры, дабы выделить эту букву из всего изображения.

Рисунок 9-8. Использование техники «мозаика»

Однако, если вы предоставите действительно маленькие мипмапы для мозаики, то на текстурах с низким разрешением детали из нескольких букв будут сливаться. В OpenGL версии 1.2 вы можете установить ограничение на самое низкое разрешение.

В OpenGL 1.2 появились новые параметры для управления уровнями мипмаппинга: GL_TEXTURE_MIN_LOD, GL_TEXTURE_MAX_LOD, GL_TEXTURE_BASE_LEVEL и GL_TEXTURE_MAX_LEVEL. Первые два параметра (будем для краткости называть их BASE_LEVEL и MAX_LEVEL) управляют тем, какие уровни мипмаппинг используются и, следовательно, тем, какие из них должны быть предоставлены. Оставшиеся два параметра (MIN_LOD и MAX_LOD) управляют активным диапазоном упоминавшегося

ранее фактора масштаба .

BASE_LEVEL и MAX_LEVEL используются для управления тем, какой диапазон мипмапов следует использовать. BASE_LEVEL – это уровень детализации с максимально высоким разрешением. Значением по умолчанию для BASE_LEVEL является 0. Однако, впоследствии вы можете изменять это значение, если вы добавляете мипмап с еще более высоким разрешением «на лету». Похожим образом MAX_LEVEL ограничивает диапазон сверху, не давая использовать мипмапы с уровнем детализации больше MAX_LEVEL (то есть, не давая использовать мипмапы с более низкими разрешениями).

Для того, чтобы мипмаппинг заработал, следует загрузить все мипмапы между BASEE_LEVEL и MAX_LEVEL. Самый большой возможный уровень это наименьшая из двух величин: MAX_LEVEL или уровень детализации, на котором мипмап содержит только 1 тэксель. Значение по умолчанию для MAX_LEVEL – 1000, что почти всегда означает, что наименьшее разрешение текстуры – 1 тэксель.

Замечание: Если вы забудете загрузить один из необходимых мипмапов, текстурирование может таинственно деактивироваться. Если не работают ни мипмаппинг, ни просто текстурирование, проверьте загрузили ли вы все нужные мипмапы.

Чтобы установить базовый и максимальный уровни мипмапов, используйте команду glTexParameter*() с первым аргументом, установленным в GL_TEXTURE_1D, GL_TEXTURE_2D или GL_TEXTURE_3D, в зависимости от ваших текстур. Второй аргумент должен быть одним из параметров, описанных в таблице 9-1. Третий аргумент представляет собой значение параметра.

Таблица 9-1. Параметры управления уровнями мипмаппинга

Параметр

Описание

Значения

 

уровень текстуры с наивысшим используемым

любое

GL_TEXTURE_BASE_LEVEL

неотрицательное

разрешением (наименьшее значение уровня по номеру)

 

 

целое

 

уровень текстуры с наименьшим используемым

любое

GL_TEXTURE_MAX_LEVEL

неотрицательное

разрешением (наибольшее значение уровня по номеру)

 

целое

 

 

 

 

 

Код в примере 9-6 устанавливает базовый и максимальный уровни мипмапов в 2 и 5 соответсвенно. Поскольку изображение на базовом уровне (уровне 2) имеет разрешение 64x32 тэкселя, мипмапы на уровнях 3, 4 и 5 должны иметь меньшее разрешение.

Пример 9-6. Установки базового и максимального уровней мипмапов

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_BASE_LEVEL,2); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,5); glTexImage2D(GL_TEXTURE_2D,2,GL_RGBA, 64,32,0, GL_RGBA, GL_UNSIGNED_BYTE, image1); glTexImage2D(GL_TEXTURE_2D,3,GL_RGBA, 32,16,0, GL_RGBA, GL_UNSIGNED_BYTE, image2); glTexImage2D(GL_TEXTURE_2D,4,GL_RGBA, 16,8,0,

GL_RGBA, GL_UNSIGNED_BYTE, image3); glTexImage2D(GL_TEXTURE_2D,5,GL_RGBA, 8,4,0, GL_RGBA, GL_UNSIGNED_BYTE, image4);

Позже в этой программе вам может понадобиться добавить дополнительные мипмапы с большим или меньшим разрешением. Например, вы можете добавить в этот набор текстуру с разрешением 128x64 тэкселя на уровне 1. Однако не забудьте изменить значение параметра BASE_LEVEL.

Соседние файлы в предмете Компьютерная Графика