opengl
Texturizado
Buscar..
Fundamentos de la texturización.
Una textura es una forma de almacenamiento de datos que permite un acceso conveniente no solo a entradas de datos particulares, sino también a puntos de muestra que mezclan (interpolan) varias entradas juntas.
En OpenGL, las texturas se pueden usar para muchas cosas, pero lo más común es mapear una imagen a un polígono (por ejemplo, un triángulo). Para mapear la textura a un triángulo (u otro polígono) tenemos que decirle a cada vértice a qué parte de la textura corresponde. Asignamos una coordenada de textura a cada vértice de un polígono y luego se interpolará entre todos los fragmentos de ese polígono. Las coordenadas de la textura suelen oscilar entre 0 y 1 en los ejes x e y como se muestra en la siguiente imagen:
Las coordenadas de textura de este triángulo se verían así:
GLfloat texCoords[] = {
0.0f, 0.0f, // Lower-left corner
1.0f, 0.0f, // Lower-right corner
0.5f, 1.0f // Top-center corner
};
Coloque esas coordenadas en VBO (objeto de búfer de vértice) y cree un nuevo atributo para el sombreador. Ya debería tener al menos un atributo para las posiciones de vértice, así que cree otro para las coordenadas de textura.
Generando textura
Lo primero que se debe hacer es generar un objeto de textura al que se hará referencia mediante una ID que se almacenará en una textura int sin signo:
GLuint texture;
glGenTextures(1, &texture);
Después de eso, debe estar vinculado para que todos los comandos de textura subsiguientes configuren esta textura:
glBindTexture(GL_TEXTURE_2D, texture);
Cargando imagen
Para cargar una imagen, puede crear su propio cargador de imágenes o puede usar una biblioteca de carga de imágenes como SOIL (Simple OpenGL Image Library) en c ++ o PNGDecoder de TWL en java.
Un ejemplo de carga de imagen con SOIL sería:
int width, height;
unsigned char* image = SOIL_load_image("image.png", &width, &height, 0, SOIL_LOAD_RGB);
Ahora puedes asignar esta imagen al objeto de textura:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image);
Después de eso debes desenlazar el objeto de textura:
glBindTexture(GL_TEXTURE_2D, 0);
Wrap parámetro para coordenadas de textura
Como se ve arriba, la esquina inferior izquierda de la textura tiene las coordenadas UV (st) (0, 0) y la esquina superior derecha de la textura tiene las coordenadas (1, 1), pero las coordenadas de textura de una malla pueden estar en cualquier rango. Para manejar esto, se debe definir cómo se ajustan las coordenadas de la textura a la textura.
El parámetro de ajuste para la coordenada de textura se puede configurar con glTextureParameter usando GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
y 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);
Los parámetros posibles son:
GL_CLAMP_TO_EDGE
hace que las coordenadas de la textura seGL_CLAMP_TO_EDGE
al rango [1 / 2N, 1 - 1 / 2N] , donde N es el tamaño de la textura en la dirección.GL_CLAMP_TO_BORDER
hace lo mismo queGL_CLAMP_TO_EDGE
, pero en los casos en que la sujeción, los datos de texel obtenidos se sustituyen con el color especificado porGL_TEXTURE_BORDER_COLOR
.GL_REPEAT
hace que seGL_REPEAT
la parte entera de la coordenada de textura. La textura es de baldosas .
-
GL_MIRRORED_REPEAT
: Si la parte entera de la coordenada de textura es par, entonces se ignora. A diferencia de, si la parte entera de la coordenada de la textura es impar, entonces la coordenada de la textura se establece en 1 - frac (s) . fract (s) es la parte fraccionaria de la coordenada de textura. Eso hace que la textura se refleje cada 2 veces.
-
GL_MIRROR_CLAMP_TO_EDGE
hace que la coordenada textue se repita como paraGL_MIRRORED_REPEAT
para una repetición de la textura, en cuyo punto la coordenada se fija como enGL_CLAMP_TO_EDGE
.
Tenga en cuenta que el valor predeterminado para GL_TEXTURE_WRAP_S
, GL_TEXTURE_WRAP_T
y GL_TEXTURE_WRAP_R
es GL_REPEAT
.
Aplicando texturas
Lo último que hay que hacer es enlazar la textura antes del dibujo:
glBindTexture(GL_TEXTURE_2D, texture);
Textura y Framebuffer
Puede adjuntar una imagen en una textura a un framebuffer, para que pueda renderizar directamente a esa textura.
glGenFramebuffers (1, &framebuffer);
glBindFramebuffer (GL_FRAMEBUFFER, framebuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER,
GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D,
texture,
0);
Nota: no puede leer y escribir desde la misma textura en la misma tarea de renderizado, ya que se trata de un comportamiento indefinido. Pero puede usar: glTextureBarrier()
entre las llamadas de render para esto.
Leer datos de textura
Puede leer datos de textura con la función 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);
Nota: el tipo y el formato de la textura son solo por ejemplo y pueden ser diferentes.
Usando PBOs
Si vincula un búfer a GL_PIXEL_UNPACK_BUFFER
entonces el parámetro de data
en glTexImage2D
es un desplazamiento en ese búfer.
Esto significa que glTexImage2D no necesita esperar a que todos los datos se copien de la memoria de la aplicación antes de que pueda regresar, lo que reduce la sobrecarga en el subproceso principal.
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);
Pero donde realmente brillan las PBO es cuando necesita leer el resultado de un render en la memoria de la aplicación. Para leer los datos de píxeles en un búfer, GL_PIXEL_PACK_BUFFER
a GL_PIXEL_PACK_BUFFER
entonces el parámetro de datos de glGetTexImage
será un desplazamiento en ese búfer:
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);
}
Usando texturas en sombreadores GLSL
El sombreador de vértices solo acepta las coordenadas de textura como un atributo de vértice y envía las coordenadas al sombreador de fragmentos. De forma predeterminada, también garantizará que el fragmento reciba la coordenada interpolada correctamente en función de su posición en un triángulo:
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;
}
El sombreador de fragmentos acepta la variable de salida texCoord
como una variable de entrada. Luego puede agregar una textura al fragmento sombreado declarando un uniform sampler2D
. Para muestrear un fragmento de la textura usamos una texture
función incorporada que tiene dos parámetros. Primero está la textura de la que queremos muestrear y la segunda es la coordenada de esta textura:
in vec2 texCoordOut;
out vec4 color;
uniform sampler2D image;
void main()
{
color = texture(image, texCoordOut);
}
Tenga en cuenta que la image
no es la identificación directa de la textura aquí. Es la identificación de la unidad de textura que se muestreará. A su vez, las texturas no están limitadas a los programas directamente; Ellos están obligados a unidades de textura. Esto se logra primero glActiveTexture
unidad de textura con glActiveTexture
, y luego llamando a glBindTexture
afectará a esta unidad de textura en particular. Sin embargo, como la unidad de textura predeterminada es la unidad de textura 0
, los programas que usan una textura pueden simplificarse omitiendo esta llamada.