arrays - ¿Cuándo debería usar matrices indexadas de vértices OpenGL?
vertex indexed (1)
De esto concluyo que al renderizar la geometría que es todas las costuras o la mayoría de las costuras, al usar GL_TRIANGLE_STRIP o _FAN, entonces nunca debería usar matrices indexadas, y siempre debería usar gl [Multi] DrawArrays.
No, y la razón es bastante simple.
Su conclusión se basa en el hecho de que ha analizado un solo quad compuesto por dos triángulos. Estos dos triángulos dibujados utilizando ventilador / banda triangular no pueden simplificarse utilizando matrices indexadas.
Pero intenta pensar en una geometría de terreno grande. Cada bloque de terreno se dibuja como un cuadrángulo, usando un abanico triángulo / primitiva de banda. Por ejemplo:
Cada franja triangular de la figura tiene en común todos los vértices con franjas triangulares adyacentes, y el uso de índices permite comprimir la definición geométrica, en lugar de repetir vértices para cada franja triangular.
Básicamente, las primitivas de dibujo (triángulos, abanicos y tiras) usando índices son útiles siempre que puedas compartir la mayoría de los vértices de una primitiva con otra.
Compartir la información permite ahorrar ancho de banda de transmisión de información, pero no es la única ventaja. En realidad, las matrices indexadas permiten:
- Evite la sincronización de la información que pertenece al mismo vértice "conceptual", especificado muchas veces
- Permitir realizar la misma operación de sombreado en un solo vértice en lugar de ejecutar muchas veces, una para cada duplicación de vértices.
- Además, la combinación del uso de bandas triangulares / ventiladores e índices permite a la aplicación comprimir el tampón de índices, ya que la especificación de banda / ventilador requiere menos índices (un triángulo requiere siempre 3 índices para cada cara).
La matriz indexada no se puede usar, como se ha especificado, cuando un vértice no puede compartir toda la información asociada con él (color, coordenadas de textura, etc.) con otro vértice coincidente.
Solo para completar, el tamaño de la información necesaria para la especificación de la geometría no es el único factor que determina la operación óptima de renderizado.
De hecho, otro factor fundamental para la representación primitiva es la localización de la caché de los datos. Los datos de geometría mal especificados (objetos de búfer no intercalados, tiras de triángulos largos ...) causan muchas fallas de caché, degradando el rendimiento de la tarjeta gráfica.
Para optimizar la operación de renderizado, la especificación de vértice debe reordenarse para reutilizar los vértices previamente especificados, con la mayor probabilidad. De esta forma, la línea de caché de la tarjeta gráfica puede reutilizar vértices previamente especificados sin recuperarlos de la memoria.
Estoy tratando de tener una idea clara de cuándo debería usar matrices indexadas de vértices OpenGL, dibujadas con Gl [Multi] DrawElements y similares, frente a cuando simplemente debería usar matrices contiguas de vértices, dibujadas con gl [Multi] DrawArrays .
( Actualización: el consenso en las respuestas que obtuve es que uno siempre debería estar usando vértices indexados).
He ido y venido varias veces sobre este tema, así que voy a resumir mi comprensión actual, con la esperanza de que alguien pueda decirme que ahora soy más o menos correcto, o señalar dónde están mis malentendidos restantes. . Específicamente, tengo tres conclusiones, en negrita. Corrígelos si están equivocados.
Un caso simple es si mi geometría consiste en mallas para formar superficies curvas. En este caso, los vértices en el medio de la malla tendrán atributos idénticos (posición, normal, color, coord de la textura, etc.) para cada triángulo que use el vértice.
Esto me lleva a concluir que:
1. Para la geometría con pocas costuras, las matrices indexadas son una gran victoria.
Siga la regla 1 siempre, excepto:
Para la geometría que es muy "en bloque", en la que cada borde representa una costura, el beneficio de las matrices indexadas es menos obvio. Para tomar un cubo simple como ejemplo, aunque cada vértice se usa en tres caras diferentes, no podemos compartir vértices entre ellas, porque para un solo vértice, las normales de superficie (y otras posibles cosas, como la combinación de color y textura) ) será diferente en cada cara. Por lo tanto, necesitamos introducir explícitamente posiciones de vértices redundantes en nuestra matriz, de modo que la misma posición se pueda usar varias veces con diferentes normales, etc. Esto significa que las matrices indexadas son de menor utilidad.
Por ejemplo, al renderizar una cara única de un cubo:
0 1
o---o
|/ |
| / |
| /|
o---o
3 2
(esto se puede considerar de forma aislada, porque las uniones entre esta cara y todas las caras adyacentes significan que ninguno de estos vértices se puede compartir entre las caras)
si renderiza usando GL_TRIANGLE_FAN (o _STRIP), entonces cada cara del cubo se puede representar así:
verts = [v0, v1, v2, v3]
colors = [c0, c0, c0, c0]
normal = [n0, n0, n0, n0]
Agregar índices no nos permite simplificar esto.
De esto concluyo que:
2. Al renderizar la geometría que es todas las costuras o la mayoría de las costuras, al usar GL_TRIANGLE_STRIP o _FAN, entonces nunca debería usar matrices indexadas, y siempre debería usar gl [Multi] DrawArrays.
( Actualización: las respuestas indican que esta conclusión es incorrecta. Aunque los índices no nos permiten reducir el tamaño de las matrices aquí, aún deberían utilizarse debido a otros beneficios de rendimiento, como se explica en los comentarios)
La única excepción a la regla 2 es:
Al usar GL_TRIANGLES (en lugar de tiras o ventiladores), la mitad de los vértices se pueden volver a usar dos veces, con colores y normales idénticos, etc., porque cada cara del cubo se representa como dos triángulos separados. Nuevamente, para la misma cara de cubo único:
0 1
o---o
|/ |
| / |
| /|
o---o
3 2
Sin índices, usando GL_TRIANGLES, las matrices serían algo así como:
verts = [v0, v1, v2, v2, v3, v0]
normals = [n0, n0, n0, n0, n0, n0]
colors = [c0, c0, c0, c0, c0, c0]
Dado que un vértice y una normal suelen tener 3 flotantes cada uno, y un color suele ser de 3 bytes, eso da, para cada cara del cubo, aproximadamente:
verts = 6 * 3 floats = 18 floats
normals = 6 * 3 floats = 18 floats
colors = 6 * 3 bytes = 18 bytes
= 36 floats and 18 bytes per cube face.
(Entiendo que el número de bytes podría cambiar si se usan diferentes tipos, las cifras exactas son solo para ilustración).
Con índices, podemos simplificar esto un poco, dando:
verts = [v0, v1, v2, v3] (4 * 3 = 12 floats)
normals = [n0, n0, n0, n0] (4 * 3 = 12 floats)
colors = [c0, c0, c0, c0] (4 * 3 = 12 bytes)
indices = [0, 1, 2, 2, 3, 0] (6 shorts)
= 24 floats + 12 bytes, and maybe 6 shorts, per cube face.
Vea cómo en el último caso, los vértices 0 y 2 se usan dos veces, pero solo se representan una vez en cada una de las matrices de verts, normales y colores. Esto suena como una pequeña ganancia para usar índices, incluso en el caso extremo de que cada borde de geometría sea una costura.
Esto me lleva a concluir que:
3. Al usar GL_TRIANGLES, uno siempre debe usar matrices indexadas, incluso para la geometría que es todas las costuras.
Corrija mis conclusiones en negrita si están equivocadas.