Главная Статьи Ссылки Скачать Скриншоты Юмор Почитать Tools Проекты Обо мне Гостевая |
Довольно часто возникает необходимость группировки сразу нескольких текстур в одну (в частности это может быть вызвано ограничением на количество доступных текстурных юнитов). На практике до последнего времени такая группировка осуществлялось при помощи текстурных атласов.
Рис 1. Пример организации текстурного атласа.
Однако использование текстурного атласа иметь и определенные недостатки, связанные как с построением такого атласа, необходимости изменения текстурных координат для доступа к текстуре внутри атласа, а также возможных арефтактов, связанных с пирамидальным фильтрованием, когда происходит "наползание" одной текстуры внутри атласа на другую.
Последнее обстоятельство не позволяет использовать 3D-текстуры в качестве средства группировки отдельных текстур - мипмэпы отдельных слоев будут наползать друг на друга также приводя к появлению артефактов.
Идеальным решением была бы возможность объединения группы 2D-текстур в массив, получая при этом что-то вроде 3D-текстуры, но с независимыми мипмэпами для каждого слоя.
Именно такую возможность и дает расширение EXT_texture_array. Данное расширение позволяет группировать одно- и двухмерные текстуры одинакого размера и формата в массивы. Каждый такой массив является фактически одной текстурой (т.е. занимает ровно один текстурный блок).
Во многом такие текстурные массивы похожи на 2D- и 3D-текстуры, однако есть и принципиальные отличия.
Во-первых мипмэппинг осуществляется независимо для каждого слоя, поэтому их влияние друг на друга полностью исключается.
Во-вторых, компонента текстурных координат (t или r соответственно), используемая для задания слоя задает именно номер слоя, т.е. принимает значения от 0 до n-1, где n - число слоев.
В-третьих, для чтения из текстур данного типа в шейдерах вводятся новые типы сэмплеров.
Создание текстурных массивов полностью аналогично созданию 2D- и 3D-текстур, только в качестве типа текстур (target) используются GL_TEXTURE_1D_ARRAY_EXT и GL_TEXTURE_2D_ARRAY_EXT соответственно. Для задание данных текстур используются функции glTexImage2D и glTexImage3DEXT.
GLuint tex; glGenTextures ( 1, &tex ); glBindTexture ( GL_TEXTURE_2D_ARRAY_EXT, tex ); glTexImage3DEXT ( GL_TEXTURE_2D_ARRAY_EXT, ... );
Ниже приводится пример функции, получающей на вход массив имен текстур (завершенный NULL'ом) и строящей по нему texture-array (Полностью пример можно скачать по ссылке в конце статьи).
unsigned buildTextureArray ( const char * names [] ) { if ( names [0] == NULL || names [0][0] == '\0' ) return 0; int i; unsigned id; Texture * tex = getTexture ( names [0] ); // load 1st texture int count = 0; if ( tex == NULL ) { delete tex; return 0; } while ( names [count] ) // count # of textures count++; int w = tex -> getWidth (); int h = tex -> getHeight (); int comp = tex -> getNumComponents (); int fmt = tex -> getFormat (); size_t sz = tex -> getBytesPerLine () * h; byte * buf = (byte *) malloc ( sz * count ); memcpy ( buf, tex -> getData (), sz ); delete tex; for ( i = 1; i < count; i++ ) { Texture * t = getTexture ( names [i] ); if ( t == NULL ) break; // check that all textures have same foramt/size if ( t -> getWidth () != w || t -> getHeight () != h || t -> getNumComponents () != comp || t -> getFormat () != fmt ) break; memcpy ( buf + i*sz, t-> getData (), sz ); delete t; } if ( i < count ) // some error occured { free ( buf ); return 0; } glGenTextures ( 1, &id ); glBindTexture ( GL_TEXTURE_2D_ARRAY_EXT, id ); glTexImage3DEXT ( GL_TEXTURE_2D_ARRAY_EXT, 0, comp, w, h, count, 0, fmt, GL_UNSIGNED_BYTE, buf ); glTexParameteri ( GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MAG_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_MIN_FILTER, GL_LINEAR ); glTexParameteri ( GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_S, GL_REPEAT ); glTexParameteri ( GL_TEXTURE_2D_ARRAY_EXT, GL_TEXTURE_WRAP_T, GL_REPEAT ); free ( buf ); return id; }
Обратите внимание, что данная функция проверяет на совпадение размеров и форматов всех текстур из списка. Фактически функция просто загружает все текстуры (как объекты класса Texture, строит новый массив данных и по нему создает соответствующий текстурный массив.
Для использования текстурных массивов введены новые типы сэмплеров - sampler1DArray, sampler2DArray, sampler1DArrayShadow, sampler2DArrayShadow, isampler1DArray, usampler1DArray, isampler2DArray, usampler2DArray.
В начало шейдера следует вставить следующую директиву, сообщающую компилятору, что требуется поддержка расширение EXT_texture_array:
#extension GL_EXT_texture_aray: enable
Для чтения из текстурных массивов введены новые функции - texture1DArray, texture1DArrayLod, texture2DArray, texture2DArrayLod, shadow1DArray, shadow1DArrayLod, shadow2DArray и shadow2DArrayLod.
Обратите внимание, что при чтении из 1D-текстурного массива первая текстурная координата (s) задает координату внутри слоя (принимая значения из отрезка [0,1]), а вторая координата (t) содержит номер слоя, из которого будет осуществляться чтение (0,1,2,...).
Аналогично при чтении из 2D-текстурного массива первые две текстурные координаты (s,t) задают координаты внутри слоя, а третья текстурная координата (r) задает номер слоя.
Ниже приводится пример шейдера, получающего в текстурном массиве сразу диффузный цвет, bump map и gloss map. Во всем остальном это обычный шейдер для попиксельного освещения по Фонгу (с учетом всех переданных карт).
#extension GL_EXT_texture_aray: enable varying vec3 lt; varying vec3 ht; uniform sampler2DArray map; void main (void) { vec4 clr = texture2DArray ( map, vec3 ( gl_TexCoord [0].xy, 0.0 ) ); // get color vec4 n = texture2DArray ( map, vec3 ( gl_TexCoord [0].xy, 1.0 ) ); // get normal vec4 gloss = texture2DArray ( map, vec3 ( gl_TexCoord [0].xy, 2.0 ) ); // get gloss color const vec4 specColor = vec4 ( 1 ); vec3 nt = normalize ( vec3 ( 0.0, 0.0, 1.0 ) + 2.0*n.xyz - 1.0 ); vec3 l2 = normalize ( lt ); vec3 h2 = normalize ( ht ); float diff = max ( dot ( nt, l2 ), 0.0 ) + 0.2; float spec = pow ( max ( dot ( nt, h2 ), 0.0 ), 60.0 ); gl_FragColor = diff * clr + spec * gloss; }
Отдельный слоя двухмерного текстурного массива может выступать в качестве цели для рендеринга (render target) при использование расширения EXT_framebuffer_object.
Для поддержки этой возможности введена новая функция glFramebufferTextureLayerEXT.
void glFramebufferTextureLayerEXT ( GLenum target, GLenum attachment, GLuint textureId, int level, int layer );
Данная функция очень близка как по параметрам, так и по смыслу функции glFramebufferTexture3DEXT, только параметр layer, задает номер слоя (вместо параметра zOffset).
Для построение полного набора мипмапов для текстурного массива служит функция glGenerateMipmapEXT. Обратите внимание, что данная функция строит мипмэпы сразу для всех слоев массива.
По этой ссылке можно скачать весь исходный код к этой статье. Также доступны для скачивания откомпилированные версии для M$ Windows и Linux.