OpenGL Vertex Attributes - Normalización

8

Por desgracia, he buscado y no he encontrado una respuesta definitiva.

¿Cuándo normalizaría los datos de vértice en OpenGL usando el siguiente comando:

glVertexAttribPointer(index, size, type, normalize, stride, pointer);

Es decir, cuándo lo haría normalize == GL_TRUE; ¿Qué situaciones y por qué elegiría dejar que la GPU haga los cálculos en lugar de preprocesarla? Todos los ejemplos que he visto, tienen esto configurado en GL_FALSE; y personalmente no puedo ver su uso. Pero Khronos no es estúpido, por lo que debe estar ahí para algo útil (y probablemente común).

caviar desacelerado
fuente

Respuestas:

9

El parámetro "normalizado" afecta el uso de valores de punto fijo.

Este documento dice que el parámetro "especifica si los valores de datos de punto fijo deben normalizarse (GL_TRUE) o convertirse directamente como valores de punto fijo (GL_FALSE) cuando se accede a ellos".

Además, este indica "Para glVertexAttribPointer, si normalizado se establece en GL_TRUE, indica que los valores almacenados en un formato entero deben asignarse al rango [-1,1] (para valores con signo) o [0,1] ( para valores sin signo) cuando se accede a ellos y se convierten en coma flotante. De lo contrario, los valores se convertirán en flotantes directamente sin normalización ".

En resumen, si no está utilizando valores de punto fijo, no necesita preocuparse. Si lo hace, este indicador controla si (por ejemplo) el valor de byte 128 debe ser 0.5 o 128.0.

Jari Komppa
fuente
3
Esto realmente no responde a la pregunta, ya entendí lo que hace, pero quería saber cuándo sería aplicable a una implementación 'común'.
deceleratedcaviar
2
Estoy bastante seguro de que era relevante cuando muchas aplicaciones móviles usaban principalmente matemáticas de punto fijo y no querían tocar las matemáticas de punto flotante. Otra posibilidad es que a veces desee almacenar datos de una forma más compacta, por ejemplo, en bytes, si la precisión es suficiente, y dejar que OpenGL convierta los bytes en un rango de 0..1.
Jari Komppa
Sigue siendo relevante. Piense en los cables UV, por ejemplo. Es posible que no desee desperdiciar espacio y almacenarlo como float2, por lo que lo almacena como 2 uint8 en su lugar como una optimización y establece el indicador de normalización para que obtenga el rango [0, 1] en lugar de [0, 256] en el programa.
nulo el
¿128 se convertirá en 0.5 o 128/255 = ligeramente más alto?
riv
11

Esta es una vieja pregunta, pero la respuesta actual realmente no explica para qué los usarías.

Se trata de ahorrar espacio. Y con los atributos de vértice, menos espacio puede significar un mayor rendimiento (si está vinculado a la transferencia de vértice).

Los colores generalmente no necesitan más de 8 bits por componente. A veces necesitas 16 bits, si es un valor de luz HDR o algo así. Pero para las características de la superficie (que es lo que son la mayoría de los atributos de vértice), 8 bits está bien. Por lo tanto, los bytes normalizados sin signo son un buen formato de vértice.

Las coordenadas de textura no necesitan 32 bits de precisión de punto flotante. Un valor de 16 bits de [0, 1] es suficiente. Por lo tanto, los cortos sin signo normalizados son un formato de vértice razonable.

Las normales nunca necesitan 32 bits de precisión. Son direcciones. Los bytes normalizados con signo de 8 bits tienden a ser un poco pequeños, pero los valores normalizados de 10 bits son lo suficientemente buenos la mayor parte del tiempo. OpenGL (3.3+) incluso le permite usar normales de 10 bits a través de un formato empaquetado 10/10/10/2 bits, almacenado en un único entero sin signo de 32 bits.

Incluso puedes jugar juegos con posiciones de vértice, si te encuentras con una gran necesidad de más memoria.

Sin normalización, tendrías que desperdiciar preciosos ciclos en tu sombreador dividiendo los atributos de byte por 255.0. ¿Por qué hacer esto, cuando el hardware puede hacerlo gratis?

Nicol Bolas
fuente
Una buena respuesta ¿Pero es la sobrecarga de la división de coma flotante matemática comparable a una disminución del tamaño normal del 50%? (Es decir, 32 bits para decir 16 bits); en hardware moderno. Especialmente desde entonces, realmente no necesitamos preocuparnos por demasiada memoria (al menos en mi caso de todos modos).
desaceleratedcaviar
1
@Daniel: Bueno, pensemos en ello. En "hardware MODERNO", la sobrecarga de pasar valores normalizados es ... cero. La sobrecarga de hacer la división en el sombreador es ... diferente de cero. No importa cuán increíblemente pequeño sea, sigue siendo más grande que cero. Sin mencionar el ahorro de ancho de banda de enviar un bloque de datos de vértice de 32 bytes en lugar de un bloque de 64 bytes. Ah, y el sombreador no tiene que estar especialmente programado; puede usar el mismo sombreador para tomar valores normalizados o flotantes. Si coloca la división en el sombreador, ahora necesita nuevos sombreadores para diferentes tamaños de datos de vértice.
Nicol Bolas
Gracias por la buena respuesta. Definitivamente lo tendré en cuenta, no es necesario en este momento ya que estoy enviando datos de vértices alineados, pero a medida que agrego más información puede que no sea lo mismo, y esto podría ser un gran beneficio, tanto en el almacenamiento en caché y rendimiento.
deceleratedcaviar
¿Alguna estrategia sobre cómo convertir lo normal (almacenado actualmente en una secuencia de (3x) flotantes de 32 bits) a un int 32 bits (10/10/10/2)?
Caviar desacelerado
@Daniel: Si está preguntando cómo usar la extensión ARB_vertex_type_2_10_10_10_rev , le sugiero que lea la especificación de extensión que acabo de vincular. Si está preguntando cómo hacer personalmente la manipulación de bits, sugeriría una estructura de estilo C que contiene campos de bits .
Nicol Bolas
0

Imagine que tiene una malla con normales de alta precisión y otra malla con baja. Con la normalización de atributos, puede usar el mismo sombreador pero empaquetar las normales de la segunda malla en bytes.

Stepan Zastupov
fuente