Atributos de vértices do OpenGL - Normalização

8

Infelizmente, procurei e não encontrei resposta definitiva.

Quando você normalizaria os dados do vértice no OpenGL usando o seguinte comando:

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

Ou seja, quando seria normalize == GL_TRUE; que situações e por que você escolheria deixar a GPU fazer os cálculos em vez de pré-processá-la? Todos os exemplos que eu já vi, definem isso como GL_FALSE; e eu pessoalmente não vejo utilidade para isso. Mas Khronos não é estúpido, então deve estar lá para algo útil (e provavelmente comum).

deceleratedcaviar
fonte

Respostas:

9

O parâmetro "normalizado" afeta o uso de valores de pontos fixos.

Este documento informa o parâmetro "Especifica se os valores de dados de ponto fixo devem ser normalizados (GL_TRUE) ou convertidos diretamente como valores de ponto fixo (GL_FALSE) quando forem acessados".

Além disso, este indica "Para glVertexAttribPointer, se normalizado for definido como GL_TRUE, indica que os valores armazenados em um formato inteiro devem ser mapeados para o intervalo [-1,1] (para valores assinados) ou [0,1] ( para valores não assinados) quando eles são acessados ​​e convertidos em ponto flutuante. Caso contrário, os valores serão convertidos em flutuadores diretamente sem normalização ".

Em resumo, se você não estiver usando valores de pontos fixos, não precisará se preocupar. Se você fizer isso, esse sinalizador controla se (por exemplo) o valor de bytes 128 deve ser 0,5 ou 128,0.

Jari Komppa
fonte
3
Isso realmente não responde à pergunta, eu já entendi o que faz, mas queria saber quando seria realmente aplicável a uma implementação 'comum'.
deceleratedcaviar
2
Tenho certeza de que era relevante quando muitos aplicativos móveis usavam principalmente matemática de ponto fixo e não queriam tocar em matemática de ponto flutuante. Outra possibilidade é que, às vezes, você pode querer armazenar dados de uma maneira mais compacta, digamos, em bytes, se a precisão for suficiente e permitir que o OpenGL converta os bytes no intervalo 0..1.
Jari Komppa
Ainda é relevante. Pense em cabos UV, por exemplo. Você pode não querer desperdiçar espaço e armazená-lo como um float2; portanto, armazene-o como 2 uint8 como uma otimização e defina o sinalizador de normalização para obter o intervalo [0, 1] em vez de [0, 256] no programa.
vazio
128 vai se tornar 0,5 ou 128/255 = um pouco mais alto?
riv
11

Esta é uma pergunta antiga, mas a resposta atual não explica realmente para que você as usaria.

É tudo sobre economia de espaço. E com atributos de vértice, menos espaço pode significar maior desempenho (se você estiver vinculado à transferência de vértices).

As cores normalmente não precisam de muito mais do que 8 bits por componente. Às vezes você precisa de 16 bits, se for um valor de luz HDR ou algo assim. Mas para as características da superfície (que é o que a maioria dos atributos de vértice são), 8 bits é bom. Portanto, os bytes normalizados não assinados são um bom formato de vértice.

As coordenadas de textura não precisam de precisão de 32 bits de ponto flutuante. Um valor de 16 bits de [0, 1] é suficiente. Portanto, os shorts não assinados normalizados são um formato de vértice razoável.

Os normais nunca precisam de 32 bits de precisão. São instruções. Os bytes normalizados assinados de 8 bits tendem a ser um pouco pequenos, mas os valores normalizados de 10 bits são bons o suficiente na maioria das vezes. O OpenGL (3.3+) ainda permite que você use normais de 10 bits por meio de um formato compactado de 10/10/10/2 bits, armazenado em um único inteiro não assinado de 32 bits.

Você pode até jogar com posições de vértice, se precisar de mais memória.

Sem normalização, você teria que desperdiçar ciclos preciosos no seu sombreador, dividindo os atributos de byte por 255,0. Por que fazer isso, quando o hardware pode fazer isso de graça?

Nicol Bolas
fonte
Uma boa resposta. Mas a sobrecarga da matemática de ponto flutuante da divisão é comparável a uma diminuição do tamanho normal de 50%? (Ou seja, 32 bits para dizer 16 bits); no hardware MODERNO. Especialmente porque não precisamos nos preocupar com muita memória (pelo menos no meu caso).
Deceleratedcaviar
11
@ Daniel: Bem, vamos pensar sobre isso. No "hardware MODERNO", a sobrecarga de passar valores normalizados é ... zero. A sobrecarga de fazer a divisão no shader é ... diferente de zero. Por menor que seja, ainda é maior que zero. Sem mencionar a economia de largura de banda do envio de um bloco de 32 bytes de dados de vértice, em vez de um bloco de 64 + bytes. Ah, e o shader não precisa ser especialmente programado; você pode usar o mesmo sombreador para obter valores normalizados ou flutuantes. Se você colocar a divisão no shader, agora precisará de novos shaders para diferentes tamanhos de dados de vértice.
Nicol Bolas
Obrigado pela resposta agradável. Definitivamente vou ter isso em mente, não é necessário no momento, pois estou enviando dados de vértice alinhados, mas à medida que adiciono mais informações, pode não ser o mesmo, e isso pode ser um grande benefício, tanto no cache e rendimento.
Deceleratedcaviar
Alguma estratégia sobre como converter o normal (atualmente armazenado em uma sequência de (3x) flutuações de 32 bits) em um int de 32 bits (10/10/10/2)?
Deceleratedcaviar
@ Daniel: Se você está perguntando como usar a extensão ARB_vertex_type_2_10_10_10_rev , sugiro que você leia a especificação da extensão que acabei de vincular. Se você está perguntando como fazer a manipulação de bits pessoalmente, sugiro uma estrutura no estilo C que contenha campos de bits .
Nicol Bolas
0

Imagine que você tem uma malha com normais de alta precisão e outra malha com baixa. Com a normalização de atributo, você pode usar o mesmo sombreador, mas compactar os normais da segunda malha em bytes.

Stepan Zastupov
fonte