texture texturas shaders learn example opengl textures

texturas - texture in opengl c++



Diferencias y relaciĆ³n entre glActiveTexture y glBindTexture (3)

Todo sobre objetos OpenGL

El modelo estándar para objetos OpenGL es el siguiente.

Los objetos tienen estado. Piense en ellos como una struct . Entonces, podrías tener un objeto definido así:

struct Object { int count; float opacity; char *name; };

El objeto tiene ciertos valores almacenados y tiene estado . Los objetos OpenGL también tienen estado.

Cambio de estado

En C / C ++, si tiene una instancia de tipo Object , cambiaría su estado de la siguiente manera: obj.count = 5; Haría referencia directamente a una instancia del objeto, obtendría la pieza de estado particular que desea cambiar y le insertaría un valor.

En OpenGL, no haces esto.

Por razones heredadas que quedan mejor sin explicar, para cambiar el estado de un objeto OpenGL, primero debe vincularlo al contexto. Esto se hace con algunos de la llamada glBind* .

El C / C ++ equivalente a esto es el siguiente:

Object *g_objs[MAX_LOCATIONS] = {NULL}; void BindObject(int loc, Object *obj) { g_objs[loc] = obj; }

Las texturas son interesantes; ellos representan un caso especial de encuadernación. Muchas llamadas glBind* tienen un parámetro "objetivo". Esto representa diferentes ubicaciones en el contexto de OpenGL donde se pueden vincular objetos de ese tipo. Por ejemplo, puede vincular un objeto framebuffer para lectura ( GL_READ_FRAMEBUFFER ) o para escritura ( GL_DRAW_FRAMEBUFFER ). Esto afecta cómo OpenGL usa el buffer. Esto es lo que representa el parámetro loc anterior.

Las texturas son especiales porque cuando las vincula por primera vez a un objetivo, obtienen información especial. Cuando vincula por primera vez una textura como GL_TEXTURE_2D , en realidad está configurando un estado especial en la textura. Usted dice que esta textura es una textura 2D. Y siempre será una textura 2D; este estado no puede cambiarse nunca . Si tiene una textura que primero se vinculó como GL_TEXTURE_2D , siempre debe vincularla como GL_TEXTURE_2D ; intentar GL_TEXTURE_1D como GL_TEXTURE_1D dará lugar a un error (durante el tiempo de ejecución).

Una vez que el objeto está vinculado, su estado se puede cambiar. Esto se hace a través de funciones genéricas específicas para ese objeto. Ellos también toman una ubicación que representa qué objeto modificar.

En C / C ++, esto se ve así:

void ObjectParameteri(int loc, ObjectParameters eParam, int value) { if(g_objs[loc] == NULL) return; switch(eParam) { case OBJECT_COUNT: g_objs[loc]->count = value; break; case OBJECT_OPACITY: g_objs[loc]->opacity = (float)value; break; default: //INVALID_ENUM error break; } }

Observe cómo esta función establece lo que sucede que está en el valor de loc actualmente encuadernado.

Para objetos de textura, las funciones principales de cambio de estado de textura son glTexParameter . Las únicas otras funciones que cambian el estado de la textura son las funciones glTexImage y sus variaciones ( glCompressedTexImage , glCopyTexImage , glTexStorage reciente). Las diversas versiones de SubImage cambian el contenido de la textura, pero técnicamente no cambian su estado . Las funciones de Image asignan almacenamiento de textura y configuran el formato de la textura; las funciones de SubImage solo copian píxeles alrededor. Eso no se considera el estado de la textura.

Permítame repetir: estas son las únicas funciones que modifican el estado de la textura. glTexEnv modifica el estado del entorno; no afecta nada almacenado en objetos de textura.

Textura activa

La situación de las texturas es más compleja, de nuevo por razones heredadas que mejor se dejan sin revelar. Aquí es donde entra glActiveTexture .

Para las texturas, no solo hay objetivos ( GL_TEXTURE_1D , GL_TEXTURE_CUBE_MAP , etc.). También hay unidades de textura. En términos de nuestro ejemplo de C / C ++, lo que tenemos es esto:

Object *g_objs[MAX_OBJECTS][MAX_LOCATIONS] = {NULL}; int g_currObject = 0; void BindObject(int loc, Object *obj) { g_objs[g_currObject][loc] = obj; } void ActiveObject(int currObject) { g_currObject = currObject; }

Tenga en cuenta que ahora, no solo tenemos una lista 2D de Object s, sino que también tenemos el concepto de un objeto actual. Tenemos una función para establecer el objeto actual, tenemos el concepto de un número máximo de objetos actuales, y todas nuestras funciones de manipulación de objetos se ajustan para seleccionar del objeto actual.

Cuando cambia el objeto activo actualmente, cambia todo el conjunto de ubicaciones de destino. Entonces puedes unir algo que va al objeto actual 0, cambiar al objeto actual 4 y modificar un objeto completamente diferente.

Esta analogía con los objetos de textura es perfecta ... casi.

Ver, glActiveTexture no toma un entero; toma un enumerador . Lo que en teoría significa que puede tomar cualquier cosa desde GL_TEXTURE0 hasta GL_TEXTURE31 . Pero hay una cosa que debes entender:

¡ESTO ES FALSO!

El rango real que glActiveTexture puede tomar se rige por GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS . Esa es la cantidad máxima de multitexturas simultáneas que permite una implementación. Cada uno de ellos está dividido en diferentes grupos para diferentes etapas de sombreado. Por ejemplo, en el hardware de clase GL 3.x, obtienes 16 texturas de sombreado de vértice, 16 texturas de sombreado de fragmento y 16 texturas de sombreado de geometría. Por lo tanto, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS será 48.

Pero no hay 48 enumeradores. Por eso glActiveTexture realmente no toma los enumeradores. La forma correcta de llamar a glActiveTexture es la siguiente:

glActiveTexture(GL_TEXTURE0 + i);

donde i es un número entre 0 y GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS .

Representación

Entonces, ¿qué tiene que ver todo esto con el renderizado?

Al usar sombreadores, configura sus uniformes de muestra en una unidad de imagen de textura ( glUniform1i(samplerLoc, i) , donde i es la unidad de imagen). Eso representa el número que glActiveTexture con glActiveTexture . La muestra elegirá el objetivo en función del tipo de muestra. Entonces, sampler2D elegirá el objetivo GL_TEXTURE_2D . Esta es una de las razones por las que los samplers tienen diferentes tipos.

Ahora esto suena sospechosamente como si tuvieras dos samplers GLSL, con diferentes tipos que usan la misma unidad de imagen de textura. Pero no puedes; OpenGL prohíbe esto y le dará un error cuando intente procesar.

Por lo que he glActiveTexture , glActiveTexture establece la "unidad de textura" activa. Cada unidad de textura puede tener múltiples objetivos de textura (generalmente GL_TEXTURE_1D, 2D, 3D o CUBE_MAP).

Si entiendo correctamente, debes llamar a glActiveTexture para establecer primero la unidad de textura (inicializada en GL_TEXTURE0 ), y luego unir (uno o más) "objetivos de textura" a esa unidad de textura.

La cantidad de unidades de textura disponibles depende del sistema. Veo enumeraciones de hasta 32 en mi biblioteca. Supongo que esto esencialmente significa que puedo tener el límite menor de mi GPU (que creo que es dieciséis 8) y 32 texturas en la memoria GPU en un momento dado? Supongo que hay un límite adicional que no excedo la memoria máxima de mi GPU (supuestamente 1 GB).

¿Estoy comprendiendo correctamente la relación entre los objetivos de textura y las unidades de textura? Digamos que tengo 16 unidades y 4 objetivos cada una, ¿eso significa que hay espacio para 16 * 4 = 64 objetivos, o no funciona así?

A continuación, normalmente desea cargar una textura. Puedes hacerlo a través de glTexImage2D . El primer argumento de que es un objetivo de textura. Si esto funciona como glBufferData , entonces glBufferData esencialmente el "mango" / "nombre de la textura" con el objetivo de la textura, y luego cargamos los datos de textura en ese objetivo, y así lo asociamos indirectamente con ese asa.

¿Qué pasa con glTexParameter ? Tenemos que unir un objetivo de textura, y luego elegir ese mismo objetivo de nuevo como el primer argumento? ¿O no es necesario vincular el objetivo de textura siempre que tengamos la unidad de textura activa correcta?

glGenerateMipmap opera en un objetivo ... ¿ese objetivo tiene que seguir vinculado al nombre de la textura para que tenga éxito?

Entonces, cuando queremos dibujar nuestro objeto con una textura, ¿debemos elegir una unidad de textura activa y luego un objetivo de textura? ¿O elegimos una unidad de textura, y luego podemos tomar datos de cualquiera de los 4 objetivos asociados con esa unidad? Esta es la parte que realmente me está confundiendo.


Imagine la GPU como una planta de procesamiento de pintura.

Hay una serie de tanques, que entrega tinte a una máquina de pintura. En la máquina de pintura, el tinte se aplica al objeto. Esos tanques son las unidades de textura

Esos tanques pueden estar equipados con diferentes tipos de tinte. Cada tipo de tinte requiere algún otro tipo de solvente. El "solvente" es el objetivo de textura . Por conveniencia, cada tanque está conectado a algún suministro de solvente, pero solo se puede usar un tipo de solvente a la vez en cada tanque. Entonces hay una válvula / interruptor TEXTURE_CUBE_MAP , TEXTURE_3D , TEXTURE_2D , TEXTURE_1D . Puede llenar todos los tipos de tinte en el tanque al mismo tiempo, pero dado que solo entra un tipo de solvente, "diluirá" solo el tipo de tinte que coincida. Por lo tanto, puede hacer que se una cada textura, pero la unión con el solvente "más importante" irá al tanque y se mezclará con el tipo de colorante al que pertenece.

Y luego está el tinte en sí, que proviene de un depósito y se llena en el tanque "encuadernándolo". Esa es tu textura.


Lo probaré ! Todo esto no es tan complicado, solo una cuestión de términos, espero que me aclare.

Puede crear aproximadamente tantos Objetos de Textura como memoria disponible en su sistema. Estos objetos contienen los datos reales (texels) de sus texturas, junto con los parámetros, proporcionados por glTexParameter (consulte las FAQ ).

Cuando se crea, debe asignar un Objetivo de textura a un objeto de textura, que representa el tipo de textura ( GL_TEXTURE_2D , GL_TEXTURE_3D , GL_TEXTURE_CUBE , ...).

Estos dos elementos, objeto de textura y objetivo de textura representan los datos de textura. Volveremos a ellos más tarde.

Unidades de textura

Ahora, OpenGL proporciona una matriz de unidades de textura , que se pueden usar simultáneamente al dibujar. El tamaño de la matriz depende del sistema OpenGL, el tuyo tiene 8.

Puede vincular un objeto de textura a una unidad de textura para usar la textura dada al dibujar.

En un mundo simple y fácil, para dibujar con una textura dada, vincularías un objeto de textura a la unidad de textura, y lo harías (pseudocódigo):

glTextureUnit[0] = textureObject

Como GL es una máquina de estado, por desgracia, no funciona de esta manera. Suponiendo que nuestro textureObject tenga datos para el objetivo de textura GL_TEXTURE_2D , expresaremos la tarea anterior como:

glActiveTexture(GL_TEXTURE0); // select slot 0 of the texture units array glBindTexture(GL_TEXTURE_2D, textureObject); // do the binding

Tenga en cuenta que GL_TEXTURE_2D realmente depende del tipo de textura que desea vincular.

Objetos de textura

En pseudo código, para establecer los datos de textura o los parámetros de textura, harías por ejemplo:

setTexData(textureObject, ...) setTexParameter(textureObject, TEXTURE_MIN_FILTER, LINEAR)

OpenGL no puede manipular directamente objetos de textura, actualizar / establecer su contenido o cambiar sus parámetros, primero debe vincularlos a la unidad de textura activa (cualquiera que sea). El código equivalente se convierte en:

glBindTexture(GL_TEXTURE_2D, textureObject) // this ''installs'' textureObject in texture unit glTexImage2D(GL_TEXTURE_2D, ...) glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR)

Shaders

Los sombreadores tienen acceso a todas las unidades de textura, no les importa la textura activa.

Los uniformes del muestreador son valores int que representan el índice de la unidad de textura que se usará para la muestra (y no el objeto de textura para usar).

Entonces debes unir tus objetos de textura a las unidades que quieras usar.

El tipo de la muestra coincidirá con el objetivo de textura que se usa en la unidad de textura: Sampler2D para GL_TEXTURE_2D , y así sucesivamente ...