Урок 7. Текстуры в OpenGL4

20.08.2012

В этом уроке вы узнаете: какие бывают типы текстур в OpenGL; каким образом осуществляется загрузка текстур в OpenGL; каким образом устанавливаются текстуры для использования их в шейдере.

Типы данных текстур OpenGL

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

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

Загрузка текстур в OpenGL:FreeImage

Итак, вы подготовили вершинный и индексный буфер, загрузив меш из файла или заполнив процедурно-сгенерированными данными. Теперь мы наложим на этот меш текстуру. Загрузка текстуры будет происходит в два шага: сначала нужно собственно загрузить текстуру из файла, а затем установить её в OpenGL. Для загрузки текстуры потребуется библиотека FreeImage, которую можно найти по адресу freeimage.sourceforge.net. Как и остальные библиотеки, её нужно установить в папку GL. Разберем код загрузки текстуры:

FREE_IMAGE_FORMAT fif = FIF_UNKNOWN; 
FIBITMAP* dib(0); 
 
fif = FreeImage_GetFileType(a_sPath.c_str(), 0); //Определениетипа
if(fif == FIF_UNKNOWN) //Неизвестный, просмотр по расширению
      fif = FreeImage_GetFIFFromFilename(a_sPath.c_str()); 
 
if(fif == FIF_UNKNOWN) //Если неподдерживаемый формат
      return false; 
 
if(FreeImage_FIFSupportsReading(fif)) //Загрузкаизображения
      dib = FreeImage_Load(fif, a_sPath.c_str()); 
if(!dib) 
      return false; 
 
BYTE* bDataPointer = FreeImage_GetBits(dib); //Получениеданных
iWidth = FreeImage_GetWidth(dib); //Получение разрешения изображения
iHeight = FreeImage_GetHeight(dib); 
iBPP = FreeImage_GetBPP(dib); 
 
//Есливозниклаошибка, возвращаемся
if(bDataPointer == NULL || iWidth == 0 || iHeight == 0) 
      returnfalse;
 
//ГенерацияOpenGL идентификатора для текстуры
...

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

FreeImage_Unload(dib);

После того, как загрузка битовых данных текстуры произведена, необходимо создать OpenGL объект для текстуры и загрузить в него пиксельные данные текстуры. Рассмотрим это в следующем разделе.

Загрузка текстур в OpenGL

После загрузки пиксельных данных текстуры необходимо создать объект OpenGL для текстуры.

//Генерация OpenGLидентификатора для текстуры
glGenTextures(1, &uiTexture); 
glBindTexture(GL_TEXTURE_2D, uiTexture); 
 
int iFormat = iBPP == 24 ? GL_BGR : iBPP == 8 ? GL_LUMINANCE : 0; 
int iInternalFormat = iBPP == 24 ? GL_RGB : GL_DEPTH_COMPONENT;  
 
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, iWidth, iHeight, 0, iFormat, GL_UNSIGNED_BYTE, bDataPointer); 
 
glGenSamplers(1, &uiSampler);

Ключевые операции, здесь такие же как при установке любых буферизируемых данных OpenGL. Сначала, в glGenTextures, нужно создать N-ое количество объектов и получить их идентификаторы. Затем при помощи glBindTexture поочередно текущим устанавливается каждый из созданных объектов. После того, как объект текстуры установлен текущим, в каждый из текущих объектов можно загружать данные с помощью glTexImage2D. В данном случае у нас создается один объект имеющий идентификатор uiTexture.

Установка текстуры в качестве константы шейдера

Для установки текстуры в OpenGL существует определенная последовательность действий. Во первых, с помощью glActiveTexture нужно установить номер текстуры, которую нужно сделать активной.

glActiveTexture(GL_TEXTURE0+iTextureUnit);
glBindTexture(GL_TEXTURE_2D, uiTexture);
glBindSampler(iTextureUnit, uiSampler);

Затем необходимо установить текстуру в качестве текущей. Для этого используется её идентификатор – uiTexture а также идентификатор сэмплера – uiSampler. Обратите внимание, что iTextureUnit это довольно важное значение, если в шейдере вы используете несколько текстур. iTextureUnit это порядковый номер текстуры в шейдере.

В шейдере вы можете использовать несколько текстур, и несколько констант для хранения идентификатора iTextureUnit, который будет разным для разных текстур. Впрочем, если в шейдере вы используете только одну текстуру (для большинства случаев это подойдет), то идентификатор будет равен 0. Установка константы шейдера для текстуры осуществляется с помощью следующего кода:

// Texture binding - we set GL_ACTIVE_TEXTURE0, and then we tell fragment shader,
// that gSampler variable will fetch data from GL_ACTIVE_TEXTURE0
int iSamplerLoc = glGetUniformLocation(spMain.getProgramID(), "gSampler");
glUniform1i(iSamplerLoc, 0);

В предыдущих уроках мы уже разбирали установку констант шейдера в OpenGL, так что этот код должен быть вам знаком. В этом фрагменте мы просто устанавливаем значение идентификатора текстуры gSampler равное 0. Если в вашемшейдере несколько текстур, то gSampler может быть несколькими переменными: gSampler0, gSampler1 и т.д., таким образом, для каждой переменной устанавливается свой идентификатор текстуры.

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

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

#version 330 
in vec2 texCoord; 
out vec4 outputColor; 
uniform sampler2D gSampler; 
 
void main() 
{ 
   outputColor = texture2D(gSampler, texCoord); 
}

Входящими данными для пиксельногошейдера является координата текстуры texCoord, а также идентификатор текстуры gSampler. Выходящими данными пиксельного шейдера является цвет, это значение типа float4, то есть цвет в формате R8G8B8A8. Рассмотрим также вершинный шейдер:

#version 330
 
uniform mat4 projectionMatrix;
uniform mat4 modelViewMatrix;
 
layout (location = 0) in vec3 inPosition;
layout (location = 1) in vec2 inCoord;
 
out vec2 texCoord;
 
void main()
{
        gl_Position = projectionMatrix*modelViewMatrix*vec4(inPosition, 1.0);
        texCoord = inCoord;
}

Код вершинного шейдера является стандартным. Вы можете видеть, что вершинный шейдер рассчитывает координаты точки а также передает входящие данные о текстурной координате пиксельному шейдеру.

Текстурный модуль для приложения

Все операции, выполняемые с текстурами в данном уроке объединены в текстурный модуль, подключаемый строкой #include “texture.h”. Все операции с текстурами выполняются через объект CTexture. Таким образом, чтобы работать с текстурами необходимо создать переменную для экземпляра текстуры объекта

//Установка переменно для экземпляра объекта
CTextureTex;

Затем необходимо создать экземпляр текстуры объекта

//Загрузка текстуры
Tex.loadTexture2D("texdiag.jpg", true);
Tex.setFiltering(TEXTURE_FILTER_MAG_BILINEAR, TEXTURE_FILTER_MIN_BILINEAR_MIPMAP);

Теперь можно устанавливать текстуру непосредственно в функции Render.

//Установка текстуры - установка константы шейдера для текстуры
int iSamplerLoc = glGetUniformLocation(spMain.getProgramID(), "gSampler");
glUniform1i(iSamplerLoc, 0);
 
//Установкатекстуры
Tex.bindTexture(0);

Этим кодом в функции Render должна быть установлена константа шейдера для текстуры а также установлена текстура в Tex.bindTexture. Метод bindTexture объекта Ctexture привязывает текстуру, ранее загруженную в экземпляр объекта текущим слотом шейдера. Не забывайте, что для того, чтобы эффективно использовать текстуры соответственно должны быть загружены и настроены шейдеры для операций с текстурами. Также, в функции Cleanup вашего приложения необходимо удалить объект CTexture:

//Очистка текстуры
Tex.releaseTexture();

Установка формата вершинного буфера, подходящего для использования текстур

Для того, чтобы текстура могла использоваться в шейдере, каждая из вершин вершинного буфера должна содержать координаты текстуры. Обычно координаты текстуры обозначаются символами u и v, обозначающими координаты текстуры. Значения этих чисел лежат в диапазоне от 0 до 1. Таки образом, координаты текстуры образуют двухкомпонентный вектор.

Из предыдущих уроков вы помните, как устанавливать данные для вершинного и индексного буферов. Нашими данными сейчас для вершинного буфера будут координаты вершины и текстурные координаты. Соответственно, обобщенный буфер модели будет содержать три суббуфера – для координат вершин, координат текстур и для индексов. Однако, так как формат для координат это трехкомпонентный вектор, а для текстур – двухкомпонентный, то нужно соответственно назначить формат для каждого из первых двух суббуферов, и указать в первом случае формат 3 компоненты для буфера координат вершин и 2 компоненты для буфера координат текстур:

// Установка вершинного суббуфера для координат модели
glBindBufferARB(GL_ARRAY_BUFFER, uiVBO[2]); 
glBufferDataARB(GL_ARRAY_BUFFER, 24*sizeof(float), fQuad, GL_STATIC_DRAW); 
glEnableVertexAttribArrayARB(0); 
glVertexAttribPointerARB(0, 3, GL_FLOAT, GL_FALSE, 0, 0); 
 
// Установка вершинного суббуфера для текстурных координат
glBindBufferARB(GL_ARRAY_BUFFER, uiVBO[4]); 
glBufferDataARB(GL_ARRAY_BUFFER, 16*sizeof(float), fQuadTex, GL_STATIC_DRAW); 
glEnableVertexAttribArrayARB(2); 
glVertexAttribPointerARB(2, 2, GL_FLOAT, GL_FALSE, 0, 0); 
 
// Установка индексного суббуфера
glBindBufferARB(GL_ELEMENT_ARRAY_BUFFER, uiVBO[5]); 
glBufferDataARB(GL_ELEMENT_ARRAY_BUFFER, sizeof(iIndices), iIndices, GL_STATIC_DRAW);

В данном коде каждая вершина вершинного буфера содержит лишь две переменные для координаты и для текстуры. Однако их может быть и большое. Более полный набор данных для вершины будет включать в себя также цвет вершины и информацию о нормали.

Обзор приложения

Теперь посмотрим, какой результат мы достигли при загрузке текстуры.

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

Заключение

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

Заполнен: OpenGL4
Присвоен тэг:

avatar

Об Авторе ()

Планета назначения, JO - 8703 - IV, обозначается светлой точкой на объемном экране корабля. К тому времени, как Робоид нанялся на юпитерианский круговой рейс, ему следовало бы стать главным инженером, но после Суховодного пионерского его вышибли, внеся в черный список, и высадили в Луна-Сити за то, что вместо слежения за приборами он провел время, программируя синтезатор пищи корабля строжайше запрещенной корабельными инструкциями последовательностью синтеза пива.

Комментирование закрыто.

Наверх