Todo sobre los objetos OpenGL
El modelo estándar para objetos OpenGL es el siguiente.
Los objetos tienen estado. Piense en ellos como a struct
. Entonces, podría tener un objeto definido de esta manera:
struct Object
{
int count;
float opacity;
char *name;
};
El objeto tiene ciertos valores almacenados y tiene estado . Los objetos OpenGL también tienen estado.
Estado cambiante
En C / C ++, si tiene una instancia de tipo Object
, cambiaría su estado de la siguiente manera: obj.count = 5;
haría referencia directa a una instancia del objeto, obtendría la pieza de estado en particular que desea cambiar y colocará un valor en ella.
En OpenGL, no haces esto.
Por razones heredadas, es mejor dejarlo sin explicar, para cambiar el estado de un objeto OpenGL, primero debe vincularlo al contexto. Esto se hace con algunos de glBind*
llamada.
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; representan un caso especial de encuadernación. Muchas glBind*
llamadas 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 la forma en que OpenGL usa el búfer. Esto es lo loc
que representa el parámetro anterior.
Las texturas son especiales porque cuando las vincula por primera vez a un objetivo, obtienen información especial. Cuando vincula una textura por primera vez GL_TEXTURE_2D
, en realidad está configurando un estado especial en la textura. Estás diciendo que esta textura es una textura 2D. Y siempre será una textura 2D; Este estado no se puede cambiar nunca . Si tiene una textura que primero se unió como a GL_TEXTURE_2D
, siempre debe unirla como a GL_TEXTURE_2D
; Si intenta vincularlo, se GL_TEXTURE_1D
producirá un error (durante el tiempo de ejecución).
Una vez que el objeto está vinculado, su estado se puede cambiar. Esto se realiza mediante funciones genéricas específicas de ese objeto. 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 esté en el loc
valor actualmente vinculado .
Para objetos de textura, las principales funciones de cambio de estado de textura son glTexParameter
. Las únicas otras funciones que cambian de estado textura son las glTexImage
funciones y sus variaciones ( glCompressedTexImage
, glCopyTexImage
, el reciente glTexStorage
). Las diversas SubImage
versiones cambian el contenido de la textura, pero técnicamente no cambian su estado . Las Image
funciones asignan almacenamiento de textura y establecen el formato de la textura; las SubImage
funciones solo copian píxeles. Eso no se considera el estado de la textura.
Permítanme repetir: estas son las únicas funciones que modifican el estado de la textura. glTexEnv
modifica el estado del entorno; no afecta a nada almacenado en objetos de textura.
Textura activa
La situación de las texturas es más compleja, de nuevo por razones heredadas que es mejor no revelar. Aquí es donde glActiveTexture
entra.
Para texturas, no sólo no son objetivos ( GL_TEXTURE_1D
, GL_TEXTURE_CUBE_MAP
, etc). También hay unidades de textura . En términos de nuestro ejemplo 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 actualmente activo, cambia todo el conjunto de ubicaciones de destino. Por lo tanto, puede vincular 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.
Mira, glActiveTexture
no toma un número entero; Se necesita un enumerador . Lo que en teoría significa que puede llevar 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 texturas múltiples simultáneas que permite una implementación. Estos se dividen en diferentes grupos para diferentes etapas de sombreado. Por ejemplo, en el hardware de clase GL 3.x, obtiene 16 texturas de sombreador de vértices, 16 texturas de sombreador de fragmentos y 16 texturas de sombreador de geometría. Por lo tanto, GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS
será 48.
Pero no hay 48 enumeradores. Es por eso glActiveTexture
que realmente no toma enumeradores. La forma correcta de llamar 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 los uniformes de muestra en una unidad de imagen de textura ( glUniform1i(samplerLoc, i)
donde i
está la unidad de imagen). Eso representa el número que usaste glActiveTexture
. La muestra elegirá el objetivo en función del tipo de muestra. Entonces a sampler2D
elegirá del GL_TEXTURE_2D
objetivo. Esta es una razón por la cual los muestreadores tienen diferentes tipos.
Ahora esto suena sospechosamente como que puede tener dos muestreadores 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 renderizar.
GL_TEXTURE0 + i
: quería inspeccionar los valores de enumeración para ver si eso era válido o no. Y el último párrafo: no sabía si eso era legal o no. ¡Excelente! Estoy marcando todas sus respuestas para poder consultarlas nuevamente.Lo probaré ! Todo esto no es tan complicado, solo es una cuestión de términos, espero que me quede claro.
Puede crear aproximadamente tantos objetos de textura como haya 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 preguntas frecuentes ).
Al crearse, tiene que asignar una textura de destino a un objeto textura, lo que representa el tipo de la textura (
GL_TEXTURE_2D
,GL_TEXTURE_3D
,GL_TEXTURE_CUBE
, ...).Estos dos elementos, el objeto de textura y el objetivo de textura representan los datos de textura. Volveremos a ellos más tarde.
Unidades de textura
Ahora, OpenGL proporciona una variedad de unidades de textura que se pueden usar simultáneamente mientras se dibuja. El tamaño de la matriz depende del sistema OpenGL, el suyo tiene 8.
Puede vincular un objeto de textura a una unidad de textura para usar la textura dada mientras dibuja.
En un mundo simple y fácil, para dibujar con una textura dada, unirías un objeto de textura a la unidad de textura y harías (pseudocódigo):
Como GL es una máquina de estados, lamentablemente no funciona de esta manera. Suponiendo que nuestro
textureObject
tiene datos para elGL_TEXTURE_2D
objetivo de textura, expresaremos la asignación anterior como:Tenga en cuenta que
GL_TEXTURE_2D
realmente depende del tipo de textura que desea unir.Objetos de textura
En pseudocódigo, para establecer datos de textura o parámetros de textura, haría, por ejemplo:
OpenGL no puede manipular directamente los objetos de textura, actualizar / configurar su contenido o cambiar sus parámetros, primero debe vincularlos a la unidad de textura activa (lo que sea). El código equivalente se convierte en:
Sombreadores
Los sombreadores tienen acceso a todas las unidades de textura, no les importa la textura activa.
Los uniformes de
int
muestra son valores que representan el índice de la unidad de textura a usar para la muestra (y no el objeto de textura a usar).Por lo tanto, debe vincular sus objetos de textura a las unidades que desea usar.
El tipo de muestra hará la coincidencia con el objetivo de textura que se utiliza en la unidad de textura:
Sampler2D
paraGL_TEXTURE_2D
, etc.fuente
Imagine la GPU como una planta de procesamiento de pintura.
Hay varios tanques, que entregan tinte a alguna 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 equiparse 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 un suministro de solvente, y solo se puede usar un tipo de solvente a la vez en cada tanque. Así que hay una válvula / conmutador
TEXTURE_CUBE_MAP
,TEXTURE_3D
,TEXTURE_2D
,TEXTURE_1D
. Puede llenar todos los tipos de tinte en el tanque al mismo tiempo, pero como solo entra un tipo de solvente, "diluirá" solo el tipo de tinte correspondiente. Por lo tanto, puede unir cada tipo de textura, pero la unión con el solvente "más importante" en realidad irá al tanque y se mezclará con el tipo de tinte al que pertenece.Y luego está el tinte en sí, que proviene de un almacén y se llena en el tanque al "atarlo". Esa es tu textura.
fuente
Si en tu sombreador necesitas buscar en 2 texturas:
Es necesario indicar para tex1 y tex2 sus fuentes de la siguiente manera:
en el bucle de renderizado:
Con una gl_bindtexture, no es posible hacer tal cosa. Por otro lado, un posible uso de un enlace en el bucle de renderizado es el caso en el que se alimenta una textura con un contenido en secuencia (video, cámara web):
fuente