studio programacion para móviles libro edición desarrollo desarrollar curso aprende aplicaciones opengl

opengl - para - manual de programacion android pdf



Concepto detrás de las funciones ''Bind'' de OpenGL (5)

Desde la sección Introducción: ¿Qué es OpenGL?

Los agregados complejos como las estructuras nunca se exponen directamente en OpenGL. Cualquiera de estos constructos está oculto detrás de la API. Esto hace que sea más fácil exponer la API de OpenGL a lenguajes distintos de C sin tener una capa de conversión compleja.

En C ++, si quería un objeto que contuviera un entero, un flotador y una cadena, lo crearía y accedería de esta manera:

struct Object { int count; float opacity; char *name; }; //Create the storage for the object. Object newObject; //Put data into the object. newObject.count = 5; newObject.opacity = 0.4f; newObject.name = "Some String";

En OpenGL, usaría una API que se parece más a esto:

//Create the storage for the object GLuint objectName; glGenObject(1, &objectName); //Put data into the object. glBindObject(GL_MODIFY, objectName); glObjectParameteri(GL_MODIFY, GL_OBJECT_COUNT, 5); glObjectParameterf(GL_MODIFY, GL_OBJECT_OPACITY, 0.4f); glObjectParameters(GL_MODIFY, GL_OBJECT_NAME, "Some String");

Ninguno de estos son comandos de OpenGL reales, por supuesto. Esto es simplemente un ejemplo de cómo se vería la interfaz con un objeto así.

OpenGL posee el almacenamiento para todos los objetos OpenGL. Debido a esto, el usuario solo puede acceder a un objeto por referencia. Casi todos los objetos OpenGL son referidos por un entero sin signo (el GLuint). Los objetos se crean mediante una función de la forma glGen *, donde * es el tipo del objeto. El primer parámetro es la cantidad de objetos para crear, y el segundo es una matriz GLuint * que recibe los nombres de los objetos recién creados.

Para modificar la mayoría de los objetos, primero deben estar vinculados al contexto. Muchos objetos pueden vincularse a diferentes ubicaciones en el contexto; esto permite que el mismo objeto se use de diferentes maneras. Estas diferentes ubicaciones se llaman objetivos; todos los objetos tienen una lista de objetivos válidos, y algunos tienen solo uno. En el ejemplo anterior, el objetivo ficticio "GL_MODIFY" es la ubicación donde objectName está vinculado.

Así es como funcionan la mayoría de los objetos OpenGL, y los objetos del búfer son "la mayoría de los objetos OpenGL".

Y si eso no es lo suficientemente bueno, el tutorial lo cubre nuevamente en el Capítulo 1: Siguiendo los datos :

void InitializeVertexBuffer() { glGenBuffers(1, &positionBufferObject); glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject); glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0); }

La primera línea crea el objeto buffer, almacenando el handle del objeto en la variable global positionBufferObject. Aunque el objeto ahora existe, aún no posee ningún recuerdo. Eso es porque no hemos asignado ninguno con este objeto.

La función glBindBuffer vincula el objeto de búfer recién creado al destino de enlace GL_ARRAY_BUFFER. Como se mencionó en la introducción, los objetos en OpenGL generalmente tienen que estar vinculados al contexto para que puedan hacer algo, y los objetos del búfer no son una excepción.

La función glBufferData realiza dos operaciones. Asigna memoria para el búfer actualmente enlazado a GL_ARRAY_BUFFER, que es el que acabamos de crear y enlazar. Ya tenemos algunos datos de vértices; el problema es que está en nuestra memoria en lugar de en la memoria de OpenGL. El sizeof (vertexPositions) utiliza el compilador de C ++ para determinar el tamaño de byte de la matriz vertexPositions. Luego pasamos este tamaño a glBufferData como el tamaño de memoria para asignar para este objeto de búfer. Por lo tanto, asignamos suficiente memoria GPU para almacenar nuestros datos de vértice.

La otra operación que realiza glBufferData es copiar datos de nuestra matriz de memoria en el objeto de memoria intermedia. El tercer parámetro controla esto. Si este valor no es NULO, como en este caso, glBufferData copiará los datos a los que hace referencia el puntero en el objeto de memoria intermedia. Después de esta llamada de función, el objeto de almacenamiento intermedio almacena exactamente lo que vertexPositions almacena.

El cuarto parámetro es algo que veremos en futuros tutoriales.

La segunda llamada de buffer de enlace es simplemente limpieza. Al vincular el objeto de memoria intermedia 0 a GL_ARRAY_BUFFER, ocasionamos que el objeto de memoria intermedia previamente vinculado a ese objetivo se desata de él. Cero en este caso funciona muy parecido al puntero NULL. Esto no era estrictamente necesario, ya que cualquier enlace posterior a este objetivo simplemente desvinculará lo que ya está allí. Pero a menos que tenga un control muy estricto sobre su representación, generalmente es una buena idea desvincular los objetos que vincula.

Estoy aprendiendo OpenGL de este tutorial . Mi pregunta es sobre la especificación en general, no sobre una función o tema específico. Al ver código como el siguiente:

glGenBuffers(1, &positionBufferObject); glBindBuffer(GL_ARRAY_BUFFER, positionBufferObject); glBufferData(GL_ARRAY_BUFFER, sizeof(vertexPositions), vertexPositions, GL_STATIC_DRAW); glBindBuffer(GL_ARRAY_BUFFER, 0);

Estoy confundido acerca de la utilidad de llamar a las funciones de vinculación antes y después de configurar los datos del buffer. Me parece superfluo, debido a mi inexperiencia con OpenGL y Computer Graphics en general.

La página man dice que:

glBindBuffer te permite crear o usar un objeto de buffer nombrado. Llamar a glBindBuffer con el objetivo establecido en GL_ARRAY_BUFFER, GL_ELEMENT_ARRAY_BUFFER, GL_PIXEL_PACK_BUFFER o GL_PIXEL_UNPACK_BUFFER y el búfer configurado en el nombre del nuevo objeto de búfer vincula el nombre del objeto de búfer al destino. Cuando un objeto de memoria intermedia está vinculado a un objetivo, el enlace anterior para ese objetivo se rompe automáticamente.

¿Cuál es exactamente el concepto / utilidad de ''atar'' algo a un ''objetivo''?


Enlazar un búfer a un objetivo es algo así como establecer una variable global. Las llamadas a funciones posteriores operan luego sobre esos datos globales. En el caso de OpenGL, todas las "variables globales" juntas forman un contexto GL . Prácticamente todas las funciones GL se leen desde ese contexto o lo modifican de alguna manera.

La llamada a glGenBuffers() es algo así como malloc() , asignando un buffer; establecemos un global para señalarlo con glBindBuffer() ; llamamos a una función que opera en ese global ( glBufferData() ) y luego establecemos el global en NULL para que no opere inadvertidamente en ese búfer usando glBindBuffer() .


La técnica OpenGL puede ser increíblemente opaca y confusa. ¡Lo sé! He estado escribiendo motores 3D basados ​​en OpenGL durante años (intermitentemente). En mi caso, parte del problema es que escribo el motor para ocultar la API 3D subyacente (OpenGL), así que una vez que obtengo algo que funciona, nunca vuelvo a ver el código OpenGL.

Pero aquí hay una técnica que ayuda a mi cerebro a comprender el "modo OpenGL". Creo que esta forma de pensar es verdad (pero no toda la historia).

Piense en los gráficos de hardware / tarjetas de GPU. Tienen ciertas capacidades implementadas en el hardware. Por ejemplo, la GPU solo puede actualizar (escribir) una textura a la vez. No obstante, es obligatorio que la GPU contenga muchas texturas dentro de la RAM dentro de la GPU, ya que la transferencia entre la memoria de la CPU y la GPU es muy lenta.

Entonces, lo que hace la API de OpenGL es crear la noción de una "textura activa". Luego, cuando llamamos a una función API de OpenGL para copiar una imagen en una textura, debemos hacerlo de esta manera:

1: generate a texture and assign its identifier to an unsigned integer variable. 2: bind the texture to the GL_TEXTURE bind point (or some such bind point). 3: specify the size and format of the texture bound to GL_TEXTURE target. 4: copy some image we want on the texture to the GL_TEXTURE target.

Y si queremos dibujar una imagen en otra textura, debemos repetir el mismo proceso.

Cuando finalmente estemos listos para mostrar algo en la pantalla, necesitamos nuestro código para hacer que una o más de las texturas que hemos creado y copiado se vuelvan accesibles a través de nuestro sombreador de fragmentos.

Como resultado, el sombreador de fragmentos puede acceder a más de una textura a la vez accediendo a múltiples "unidades de textura" (una textura por unidad de textura). Por lo tanto, nuestro código debe vincular las texturas que queremos poner a disposición de las unidades de textura a las que los sombreadores de fragmentos esperan que estén vinculadas.

Entonces debemos hacer algo como esto:

glActiveTexture (GL_TEXTURE0); glBindTexture (GL_TEXTURE_2D, mytexture0); glActiveTexture (GL_TEXTURE1); glBindTexture (GL_TEXTURE_2D, mytexture1); glActiveTexture (GL_TEXTURE2); glBindTexture (GL_TEXTURE_2D, mytexture2); glActiveTexture (GL_TEXTURE3); glBindTexture (GL_TEXTURE_2D, mytexture3);

Ahora, debo decir que amo OpenGL por muchas razones, pero este enfoque me vuelve loco. Eso es porque todo el software que he escrito durante años se vería así:

error = glSetTexture (GL_TEXTURE0, GL_TEXTURE_2D, mytexture0); error = glSetTexture (GL_TEXTURE1, GL_TEXTURE_2D, mytexture1); error = glSetTexture (GL_TEXTURE2, GL_TEXTURE_2D, mytexture2); error = glSetTexture (GL_TEXTURE3, GL_TEXTURE_2D, mytexture3);

Bamo. No es necesario establecer todo este estado una y otra vez. Simplemente especifique a qué unidad de textura unirá la textura, más el tipo de textura para indicar cómo acceder a la textura, más el ID de la textura que quiero adjuntar a la unidad de textura.

Tampoco necesitaría enlazar una textura como la textura activa para copiarle una imagen, solo daría la ID de la textura en la que quería copiar. ¿Por qué debería estar obligado?

Bueno, está la trampa que obliga a OpenGL a estructurarse de la manera loca que es. Debido a que el hardware hace algunas cosas, y el controlador del software hace otras cosas, y porque lo que se hace donde es una variable (depende de la tarjeta GPU), necesitan cierta forma de mantener la complejidad bajo control. Su solución es, esencialmente, tener un solo punto de vinculación para cada tipo de entidad / objeto, y requerir que unamos nuestras entidades a esos puntos de vinculación antes de llamar funciones que los manipulen. Y como segundo objetivo, las entidades vinculantes es lo que las hace disponibles para la GPU, y nuestros diversos sombreadores que se ejecutan en la GPU.

Al menos así es como mantengo el "camino OpenGL" directamente en mi cabeza. Francamente, si alguien realmente, realmente, REALMENTE comprende todas las razones por las que OpenGL está (y debe estar) estructurado tal como es, me encantaría que publicaran su propia respuesta. Creo que esta es una pregunta y un tema importante, y la razón de ser rara vez se describe, y mucho menos de una manera que mi pequeño cerebro pueda comprender.


OpenGL es lo que se conoce como una "máquina de estados", para lo cual OpenGL tiene varios "objetivos vinculantes", cada uno de los cuales solo puede tener una cosa vinculada a la vez. Encuadernación de algo más reemplaza el enlace actual y, por lo tanto, cambia su estado. Por lo tanto, al vincular los almacenamientos intermedios está (re) definiendo el estado de la máquina.

Como una máquina de estado, cualquier información que haya enlazado tendrá un efecto en la siguiente salida de la máquina, en OpenGL, que es su próxima llamada de sorteo. Una vez hecho esto, podría vincular nuevos datos de vértices, enlazar nuevos datos de píxeles, enlazar nuevos objetivos, etc. y luego iniciar otra llamada de sorteo. Si quisieras crear la ilusión de movimiento en tu pantalla, cuando estuvieras satisfecho de haber dibujado toda tu escena (un concepto de motor en 3D, no un concepto de OpenGL) podrías voltear el framebuffer.


los comandos en opengl no existen de forma aislada. ellos asumen la existencia de un contexto. Una forma de pensar en esto es que hay, escondido en el fondo, un objeto opengl, y las funciones son métodos sobre ese objeto.

así que cuando llamas a una función, lo que hace depende de los argumentos, por supuesto, pero también del estado interno de opengl - en el contexto / objeto.

esto es muy claro con bind, que dice "configurar esto como la X actual". luego, las funciones modifican la "X actual" (donde X podría ser un buffer, por ejemplo). [update:] y como dices, lo que se está configurando (el atributo en el objeto, o el "miembro de datos") es el primer argumento para enlazar. entonces GL_ARRAY_BUFFER nombra una cosa particular que está configurando.

y para responder a la segunda parte de la pregunta, establecerla en 0 simplemente borra el valor para que no realice accidentalmente cambios no planeados en otro lugar.