opengl
Текстурирование
Поиск…
Основы текстурирования
Текстура - это форма хранения данных, которая обеспечивает удобный доступ не только к конкретным записям данных, но также к смешению точек (интерполяции) нескольких элементов вместе.
В OpenGL текстуры могут использоваться для многих вещей, но чаще всего это сопоставление изображения с полигоном (например, треугольником). Чтобы сопоставить текстуру с треугольником (или другим полигоном), мы должны сообщить каждой вершине, какой части текстуры она соответствует. Мы назначаем координату текстуры каждой вершине многоугольника и затем будем интерполировать между всеми фрагментами в этом многоугольнике. Координаты текстуры обычно варьируются от 0 до 1 по оси x и y, как показано на рисунке ниже:
Координаты текстуры этого треугольника будут выглядеть так:
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
Поместите эти координаты в VBO (объект буфера вершин) и создайте новый атрибут для шейдера. У вас должно быть хотя бы один атрибут для вершинных позиций, чтобы создать другое для координат текстуры.
Создание текстуры
Первое, что нужно сделать, - создать объект текстуры, на который будет ссылаться идентификатор, который будет сохранен в неподписанной структуре int:
GLuint texture;
glGenTextures(1, &texture);
После этого он должен быть привязан, поэтому все последующие команды текстуры будут настраивать эту текстуру:
glBindTexture(GL_TEXTURE_2D, texture);
Загрузка изображения
Чтобы загрузить изображение, вы можете создать свой собственный загрузчик изображений, или вы можете использовать библиотеку загрузки изображений, такую как SOIL (простая библиотека изображений OpenGL) в c ++ или PNGDecoder TWL в java.
Пример загрузки изображения с помощью SOIL:
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
Теперь вы можете назначить это изображение текстурному объекту:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
После этого вы должны развязать объект текстуры:
glBindTexture(GL_TEXTURE_2D, 0);
Параметр Wrap для координат текстуры
Как видно выше, нижний левый угол текстуры имеет УФ (st) координаты (0, 0), а верхний правый угол текстуры имеет координаты (1, 1), но координаты текстуры сетки могут быть в любой диапазон. Чтобы справиться с этим, нужно определить, как текстурные координаты обертываются текстурой.
Параметр обертки для текстуры координаты может быть установлен с glTextureParameter использования GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
и GL_TEXTURE_WRAP_R
.
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
Возможные параметры:
GL_CLAMP_TO_EDGE
вызываетGL_CLAMP_TO_EDGE
текстурных координат к диапазону [1 / 2N, 1 - 1 / 2N] , где N - размер текстуры в направлении.GL_CLAMP_TO_BORDER
делает то же самое, что иGL_CLAMP_TO_EDGE
, но в случаях, когда зажим, выбранные данныеGL_TEXTURE_BORDER_COLOR
заменяются цветом, заданнымGL_TEXTURE_BORDER_COLOR
.GL_REPEAT
вызываетGL_REPEAT
целочисленной части координаты текстуры. Текстура облицована плиткой .
-
GL_MIRRORED_REPEAT
: если целая часть координаты текстурыGL_MIRRORED_REPEAT
, то она игнорируется. В отличие от того, если целочисленная часть координаты текстуры нечетна, то координата текстуры устанавливается в 1 - frac (s) . fract (s) - это дробная часть координаты текстуры. Это заставляет текстуру отражать каждый второй раз.
-
GL_MIRROR_CLAMP_TO_EDGE
вызывает повторение координаты текста как дляGL_MIRRORED_REPEAT
для одного повторения текстуры, после чего координата должна быть зажата, как вGL_CLAMP_TO_EDGE
.
Обратите внимание на значение по умолчанию для GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
и GL_TEXTURE_WRAP_R
- GL_REPEAT
.
Применение текстур
Последнее, что нужно сделать, это привязать текстуру до вызова рисования:
glBindTexture(GL_TEXTURE_2D, texture);
Текстура и фреймбуфер
Вы можете прикрепить изображение в текстуре к фреймбуферу, чтобы вы могли визуализировать непосредственно эту текстуру.
glGenFramebuffers (1, &framebuffer);
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0);
Примечание. Вы не можете читать и писать из одной и той же текстуры в той же задаче рендеринга, потому что она вызывает неопределенное поведение. Но вы можете использовать: glTextureBarrier()
между вызовами рендеринга для этого.
Чтение данных текстуры
Вы можете читать данные текстуры с помощью функции glGetTexImage
:
char *outBuffer = malloc(buf_size);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
outBuffer);
Примечание: тип и формат текстур - это только, например, и могут быть разными.
Использование PBO
Если вы привязываете буфер к GL_PIXEL_UNPACK_BUFFER
тогда параметр data
в glTexImage2D
является смещением в этом буфере.
Это означает, что glTexImage2D не нужно ждать, пока все данные будут скопированы из памяти приложения, прежде чем он сможет вернуться, уменьшив накладные расходы в основном потоке.
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glBufferData(GL_PIXEL_UNPACK_BUFFER, width*height*3, NULL, GL_STREAM_DRAW);
void* mappedBuffer = glMapBuffer(GL_PIXEL_UNPACK_BUFFER, GL_WRITE_ONLY);
//write data into the mapped buffer, possibly in another thread.
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
memcpy(mappedBuffer, image, width*height*3);
SOIL_free_image(image);
// after reading is complete back on the main thread
glBindBuffer(GL_PIXEL_UNPACK_BUFFER, pbo);
glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, 0);
Но там, где действительно светится PBO, вам нужно прочитать результат рендеринга обратно в память приложения. Чтобы считывать данные пикселов в буфер, привяжите его к GL_PIXEL_PACK_BUFFER
тогда параметр данных glGetTexImage
будет смещением в этот буфер:
glGenBuffers(1, &pbo);
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
glBufferData(GL_PIXEL_PACK_BUFFER, buf_size, NULL, GL_STREAM_COPY);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
glGetTexImage(GL_TEXTURE_2D,
0,
GL_RGBA,
GL_UNSIGNED_BYTE,
null);
//ensure we don't try and read data before the transfer is complete
GLsync sync = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
// then regularly check for completion
GLint result;
glGetSynciv(sync, GL_SYNC_STATUS, sizeof(result), NULL, &result);
if(result == GL_SIGNALED){
glBindBuffer(GL_PIXEL_PACK_BUFFER, pbo);
void* mappedBuffer = glMapBuffer(GL_PIXEL_PACK_BUFFER, GL_READ_ONLY);
//now mapped buffer contains the pixel data
glUnmapBuffer(GL_PIXEL_PACK_BUFFER);
}
Использование текстур в шейдерах GLSL
Вершинный шейдер принимает только координаты текстуры как атрибут вершины и пересылает координаты в шейдер фрагмента. По умолчанию он также гарантирует, что фрагмент получит правильно интерполированную координату на основе ее положения в треугольнике:
layout (location = 0) in vec3 position;
layout (location = 1) in vec2 texCoordIn;
out vec2 texCoordOut;
void main()
{
gl_Position = vec4(position, 1.0f);
texCoordOut = texCoordIn;
}
Затем шейдер фрагмента принимает выходную переменную texCoord
в качестве входной переменной. Затем вы можете добавить текстуру в шейдер фрагмента, объявив uniform sampler2D
. Чтобы пробовать фрагмент текстуры, мы используем встроенную texture
функции, которая имеет два параметра. Сначала это текстура, которую мы хотим отбирать, а вторая - координата этой текстуры:
in vec2 texCoordOut;
out vec4 color;
uniform sampler2D image;
void main()
{
color = texture(image, texCoordOut);
}
Обратите внимание: image
не является прямым идентификатором текстуры. Это идентификатор единицы текстуры, который будет отбираться. В свою очередь, текстуры напрямую не связаны с программами; они привязаны к текстурным единицам. Это достигается за счет того, что сначала блок текстуры активен с glActiveTexture
, а затем вызов glBindTexture
повлияет на конкретную текстуру. Однако, поскольку блок текстуры по умолчанию является блоком текстур 0
, программы, использующие одну текстуру, могут быть упрощены, если не считать этого вызова.