(Окончание, начало см. в МК № 36, 40, 43 (207, 211, 214)). Текстуры одна из самых эффектных и интересных возможностей библиотеки OpenGL. Ведь именно они позволяют придать объектам соответствующий вид и сделать сцену более реалистичной. Кроме того, в большинстве существующих сегодня 3D-играх текстуры используются для создания эффектов взрывов, огня, воды, магии и проч. Текстуры обеспечивают самый быстрый вывод как обычного растрового изображения, так и изображения с прозрачностью, с деформацией. В этой статье речь пойдет только лишь об основах и приемах работы с текстурами. Но кроме того, для создания действительно сложных и впечатляющих эффектов вам понадобятся еще и знания различных алгоритмов, реализующих их. Если вы действительно интересуетесь программированием 3D-графики, не ленитесь и побольше экспериментируйте. В крайнем случае, для нахождения ответов на свои вопросы используйте Интернет. Кстати, на сайтах http://www.scene.orgи http://www.demoscene.ruразмещены различные демо-программы, наглядно иллюстрирующие возможности программирования динамической графики с помощью OpenGL или Direct3D. Советую посмотреть. Ну а теперь перейдем все же непосредственно к текстурам.
Наложение текстуры
В библиотеке OpenGL предусмотрены возможности работы с несколькими типами текстур: одномерной и двумерной. Как правило, одномерные текстуры используются для нанесения штриховки или узоров в виде полосок (Рис. 1). Источником изображения для текстуры в данном случае является одномерный массив со значениями составляющих цвета (RGB) для каждой полоски. В двумерной же текстуре для этих целей используется соответственно двумерный массив, что позволяет наносить на объекты прямоугольные образы, в том числе, например, фотографии и прочее. Давайте все же ограничимся рассмотрением основ работы именно с двумерной текстурой, потому как, изучив материал по этой теме, вы без особых усилий сможете самостоятельно научиться использовать и одномерные текстуры (изменив буквально несколько параметров в функциях (см. хелп). Итак, начнем с самого начала, а именно с простого покрытия графического примитива текстурой. Как вы уже, наверное, знаете, текстура представляет собой некий графический образ, наносящийся на графический объект и располагающийся соответственно его форме (рельефу). Для использования текстуры потребуется создать и заполнить массив образа для хранения текстурной картинки, а затем выполнить ряд действий по ее нанесению. При этом следует учитывать тот факт, что массив должен иметь размерности, кратные степени двойки (2, 4, 16, 32 и т.д.) Благо, в OpenGL есть механизм уменьшения/увеличения текстуры с коррекцией качества (см. статью Davert’a «Алгоритмы текстурирования», МК №47(218)). Вот пример нанесения текстуры на полигональную поверхность (текст процедуры SetDCpixelFormat см. в предыдущих статьях) в качестве текстуры используется картинка 128х128 (сделано в PhotoShop :-)); разместите на форме компонент TImage с картинкой размером 128х128 и кнопку компонент TButton:
Давайте посмотрим, как все это работает. Сначала при запуске программы происходит обработка события OnCreate формы с помощью уже знакомых вам команд OpenGL устанавливаются начальные настройки (получение контекстов, настройка рабочей области и пр.) После этого при нажатии кнопки выполняется пользовательская процедура CreateTexture. В ней первым делом заполняется массив образа текстуры. Для этого из помещенной ранее в компонент Timage картинки считываются значения RGB-составляющих каждого пиксела и помещаются в трехмерный массив image. Затем выполняется минимальный необходимый набор команд, обеспечивающий нанесение текстуры. Командой glTexParameteri задаются параметры нанесения текстуры на объект: GL_TEXTURE_2D указывает, что используется именно двухмерная текстура (GL_TEXTURE_1D для одномерной), далее следует какой-либо параметр текстуры и его значение. В нашем случае двумя вызовами команды glTexParameteri задаются правила нанесения текстуры на объект для случаев, когда: поверхность, на которую будет помещаться текстура, меньше размера самой текстуры (GL_TEXTURE_MAG_FILTER) (по количеству пикселов) и наоборот (GL_TEXTURE_MIN_FILTER). Параметр GL_NEAREST определяет метод расчета соответствия пикселов текстуры пикселам текстурированной поверхности, в данном случае не очень точный, но более быстрый, чем при GL_LINEAR (с большей точностью) Следующей командой (gl_TexImage2D) выбирается картинка (вернее, массив, содержащий ее) для текстуры, после чего команда glTexEnvi определяет характер взаимодействия цвета примитива с цветом текстуры. Если указан параметр GL_DECAL, цвет примитива не влияет на цвет текстуры, если же GL_MODULATE цветовое содержание текстуры будет зависеть от яркости и составляющих цвета примитива (например, если примитив будет иметь красный цвет, то в текстуре красный уберется; если же примитив окрасить, к примеру, в 50%-й серый, то тогда яркость пикселов текстуры уменьшится на 50%); GL_BLEND выполняет функцию, противоположную GL_MODULATE. По завершению всех этих действий командой glEnable разрешается использование двумерной текстуры. Но это еще не все. Для корректного нанесения текстуры надо определить ее координаты. Их следует рассматривать с одной стороны как некие масштабные коэффициенты, с помощью которых можно определить количество повторений текстуры на поверхности объекта, а с другой стороны, как параметры, определяющие расположение текстуры на объекте. В нашем случае работа происходит только с двумя координатами (текстура кладется на плоскость, при том что сам объект расположен в трехмерном пространстве), хотя их всего 4 (называются s,t,r,q). В процедуре drawpoly после указания каждой вершины примитива также указываются и координаты текстуры (glTexCoord2f) они существуют не сами по себе, а имеют некоторую связь с координатами примитива. Попробуйте в процедуре в параметрах команды glTexCoord заменить единицы, например, на двойки вместо одной большой картинки текстуры на примитиве появятся 4 маленькие. Также можно, расставив координаты текстуры соответствующим образом, повернуть текстуру на 90 или перевернуть. К примеру, поменяйте параметры команд glTexCoord на соответствующие (0,0),(1,0),(1,1),(0,1). Вот и все. Результат работы программы на Рис. 2 (две текстурированные поверхности). Теперь поговорим о некоторых эффектах.
Прозрачность текстуры
Это один из самых распространенных эффектов. Большинство (если не все) сегодняшних трехмерных игр так или иначе используют его для создания красивых и впечатляющих эффектов. Суть эффекта заключается в использовании так называемого альфа-канала четвертой составляющей цвета (Red, Green, Blue, Alfa). С помощью него можно определять степень прозрачности цвета. Давайте рассмотрим пример на эту тему. Для этого измените в предыдущем примере объявление массива image: var image:array[0..127,0..127,0..3]of glubyte теперь в нем будет присутствовать 4 цветовые составляющие. Кроме того, приведите процедуру CreateTexture к следующему виду:
Обратите внимание: здесь исчезла команда glTexEnv, также в параметрах команды glTexImage2D присутствует только формат GL_RGBA и, наконец появились две новые строчки, завершающие код процедуры. Первая (glenable(gL_blend)) включает режим смешения цветов объектов сцены (не только текстурированных). Вторая (glBlendFunc) определяет характер этого смешения. Все цвета, близкие к черному, делаются прозрачными, а остальные нет. Думаю, здесь все понятно. Результат на Рис. 3 через одну текстурированную поверхность видна часть другой.
Эффект зеркальной поверхности
К сожалению, используемая в данной статье плоскость не дает возможности реализовать этот эффект во всей его красе. Если хотите, можете скачать модуль dglut.pas по адресу http://www.torry.ru/samples/samples/primscr.zip в нем есть процедура рисования чайника (не подумайте чего лишнего :-) просто чайник считается сложным объектом и удобен для различных демонстраций, особенно в данном случае). Вы сможете легко заменить плоскость на этот чайник и вдоволь полюбоваться эффектом (Рис. 4). И все же я приведу строки, реализующие этот эффект на плоскости (допишите их в конец вышеприведенной процедуры CreateTexture):
Попробуйте самостоятельно добавить анимацию поворота будет видно, что плоскость, подобно зеркалу, отражает картинку текстуры так, как если бы та была размещена перед ней. С поворотом плоскости текстурный рисунок на ней изменяется (как бы «съезжает»). Качество эффекта почти не зависит от используемой текстуры, а достигается путем использования так называемых команд генерации текстурных координат (gltexgeni) с параметром GL_SPHERE_MAP.
В данном материале рассмотрены лишь базовые принципы работы с текстурами, т.к. эта тема довольно обширна. Но надеюсь, это поможет вам понять суть и в дальнейшем расширять свои знания, имея уже хоть какую-нибудь основу и практику.