Урок 7. Текстуры в OpenGL4
В этом уроке вы узнаете: какие бывают типы текстур в 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
