Diferencia en glDrawArrays y glDrawElements

12

Mientras refrescaba mi mente en OpenGL ES, me encontré glDrawArraysy glDrawElements.

Entiendo cómo se usan y entiendo por qué son diferentes.

Lo que parece que no entiendo es que no veo cómo se glDrawElementspueden guardar las llamadas de sorteo ( guardar las llamadas de sorteo es una descripción que se menciona en la mayoría de los libros que he leído, de ahí mi mención aquí).

Imagine un escenario simple en el que traté de dibujar un cuadrado usando 2 triángulos.

Necesitaría tener un conjunto de 6 vértices usando glDrawArraysmientras uso glDrawElements, necesitaría solo 4 además de una matriz de índice que tiene 6 elementos.

Dado lo anterior, esto es lo que no entiendo:

  1. ¿Cómo podría glDrawElementsguardar las llamadas de sorteo, si todavía necesita usar la matriz de índice (6 índices, en el caso de un cuadrado) para indexar en mi matriz de vértices que tenía 4 elementos (6 veces)? En otras palabras, ¿significa glDrawElementsque todavía tendría que tener un total de 6 llamadas de extracción como glDrawArrays?
  2. ¿Cómo usaría el glDrawElementsahorro de espacio, si todavía se necesitaran 2 matrices, una para los vértices y otra para los índices?
  3. En el caso de dibujar un cuadrado a partir de 2 triángulos, para simplificar, ¿cuántas llamadas de sorteo necesitan glDrawElements(4 elementos en la matriz de vértices y 6 elementos en la matriz de índice) y glDrawArrays(6 elementos solo en la matriz de vértices), individualmente?

Gracias.

Unheilig
fuente

Respuestas:

16

Cada glDraw * es un sorteo.

1 glDrawArrays es 1 llamada de sorteo.
1 glDrawElements es 1 llamada de sorteo.

No importa (en lo que respecta al recuento de llamadas de extracción) cuántos vértices o índices utiliza, 1 glDraw * es 1 llamada de extracción.

Los casos simples de dibujar quads (suponiendo que la versión GL que usa no ha quitado quads) o triángulos no son un buen ejemplo para compararlos, porque una lista de quads o una lista de triángulos se puede dibujar con una sola llamada a cualquiera de ellos, y glDrawArrays parecerá más eficiente porque no tiene la sobrecarga adicional de indexación.

Tomemos un caso un poco más complejo, pero representativo de un escenario más real. Imagine que tiene un modelo compuesto por múltiples tiras triangulares y ventiladores:

glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);
glDrawArrays (GL_TRIANGLE_STRIP, .....);
glDrawArrays (GL_TRIANGLE_FAN, .....);

En este ejemplo, el uso de glDrawArrays le proporciona un total de 6 llamadas de sorteo para el modelo. Sin embargo, tanto las tiras como los ventiladores se pueden convertir fácilmente en triángulos indexados, así que agregue índices y se convertirá en:

glDrawElements (GL_TRIANGLES, .....);

Esa es una llamada de sorteo, para todo el modelo.


Las ventajas de glDrawElements sobre glDrawArrays son más que solo ahorro de memoria, que es la forma ingenua de medirlo. Existe la posibilidad de ahorrar memoria donde los vértices se pueden reutilizar, porque un índice suele ser más pequeño que un vértice (por lo que, incluso si tiene muchos índices de equilibrio, serán más pequeños), pero otras ventajas incluyen:

  • Recuento reducido de llamadas de empate. Cada llamada de sorteo incurre en una sobrecarga al validar el estado, configurar cosas, etc., y gran parte de esta sobrecarga ocurre en el lado de la CPU. Al reducir el número de llamadas de sorteo, evitamos gran parte de esta sobrecarga.

  • Reutilización de vértices. Esto es mucho más que solo ahorrar memoria. Es probable que su GPU tenga un caché de vértices de hardware, donde se pueden almacenar vértices recientemente transformados; si el mismo vértice vuelve a aparecer y si está en la memoria caché, puede reutilizarse desde la memoria caché en lugar de tener que transformarse nuevamente. Su hardware verificará el caché comparando índices, por lo que, en términos de OpenGL, la única forma de usar el caché es usar glDrawElements.


Entonces, para responder a sus preguntas específicas:

  1. ¿Cómo podría glDrawElements guardar llamadas de sorteo ...? En el ejemplo que usa, no lo hace. Cada uno tiene un sorteo. Como discutí anteriormente, este ejemplo es muy malo para comparar los dos.

  2. ¿Cómo ahorraría espacio usando glDrawElements ...? Porque los índices son más pequeños que los vértices. Un índice de 16 bits tiene dos bytes, un vértice de posición / color / texcoord es de 24 bytes. 6 vértices son 144 bytes, 4 vértices más 6 índices son 108 bytes.

  3. En el caso de dibujar un cuadrado a partir de 2 triángulos ...? Uno y uno. Una llamada glDraw * es una llamada de extracción, el número de vértices o índices utilizados es irrelevante para esta medición. Pero debo volver a enfatizar, este es un muy mal ejemplo para comparar los dos.


Finalmente, y solo para complicar un poco las cosas, mientras que glDrawElements con triángulos es la ruta óptima en las GPU de escritorio, en dispositivos móviles puede ser muy diferente. Algunas GPU móviles pueden preferir glDrawArrays con GL_TRIANGLE_STRIP (en este caso agregaría triángulos degenerados para concatenar primitivas), y ni siquiera he tocado temas más avanzados como reinicio primitivo o dibujo múltiple.

Maximus Minimus
fuente
Muchas gracias por sus comentarios. Me estoy tomando un poco de tiempo leyendo lo que has compartido. Solo quería hacerle saber que reconocí su respuesta. Gracias de nuevo.
Unheilig
En su ejemplo de conversión de 6 llamadas DrawTriangleFans y DrawTriangleStrips a 1 sola llamada DrawTriangles. ¿Esto realmente mejora algo de rendimiento? Según tengo entendido, dibujar una tira de triángulos compuesta por 100 triángulos es mucho más eficiente que dibujar 100 triángulos individualmente, y el beneficio compensaría fácilmente cualquier penalización producida por el uso de 6 llamadas de función en lugar de 1.
Samuel Li
@SamuelLi 6-a-1 no será una gran mejora, solo pretende ilustrar el principio. Además, y como indiqué, las tiras generalmente no son una ganancia de rendimiento sobre las listas indexadas en el hardware de escritorio posterior a 1998. Verifique la fecha en cualquier documento que le aconseje usar tiras.
Maximus Minimus
Entendido, gracias por la aclaración! @MaximusMinimus
Samuel Li