Usando um LUT para acelerar um sombreador pesado trigonométrico para dispositivos móveis

10

Estou tentando fazer com que esse sombreador seja executado em um iDevice realmente antigo, bem como eventualmente em Androids.

Mesmo quando reduzo o código para 2 funções senoidais por fragmento, o sombreador é executado a cerca de 20fps.

Eu considerei tirar uma folha do livro de técnicas antigas de sombreamento e criar uma matriz que contém vários valores trigonométricos predefinidos e, de alguma forma, usá-los para aproximar o sombreador.

No shader que vinculei acima, já estou simulando que, arredondando os valores enviados para a função trigonométrica, quanto mais à esquerda o mouse (enquanto estiver pressionado), menor a qualidade do shader. Na verdade, é bem legal porque, bem perto do lado esquerdo, parece um sombreador completamente diferente e bem legal.

Enfim, tenho dois dilemas:

  1. Não sei qual é a maneira mais eficiente de ter uma matriz de 360 ​​valores em um sombreador GLSL, constante ou uniforme?
  2. Não consigo descobrir como colocar um número em um intervalo, como geralmente se quisesse um ângulo entre 0 e 360 ​​(sim, eu sei que as GPUs usam radianos).

    func range(float angle)
    {
       float temp = angle
       while (temp > 360) {temp -= 360;}
       while (temp < 0)   {temp += 360;}
       return temp;
    }
    

    No entanto, o GLSL não permite loops while ou funções recursivas.

J.Doe
fonte
Não sei se seria prático implementá-lo, mas ajudaria a ter os valores pré-computados do seno espaçados de maneira desigual, com valores agrupados mais próximos onde a inclinação da curva senoidal é mais acentuada e menos valores nos níveis e não muda tanto? Isso permitiria maior precisão onde for necessário, sem a necessidade de armazenar um grande número de valores?
Trichoplax
5
Em relação ao item 2, a modfunção interna é o que você deseja. Você escreveria mod(angle, 360.0).
Nathan Reed
1
@trichoplax brilhante idéia, mas eu não sei como você seria capaz de procurar valores na mesa então. Digamos que os colocamos em uma matriz com alguns deles mais concentrados. Como podemos encontrar o índice certo?
J.Doe
6
Que tal colocar seus valores em uma textura 1D de 3 canais? Dessa forma, você pode obter o pecado, o cos e o bronzeado pelo preço de uma única pesquisa de textura. Se você mapear 0 - 2pi de ângulo para 0 - 1 UV e usar o modo de textura repetida, nem precisará da chamada de modificação, ela será 'quebrada' automaticamente e você também poderá obter aproximações filtradas linearmente entre os valores armazenados, em vez de encaixando no mais próximo.
russ
3
Na maioria das vezes, você pode eliminar as funções trigonométricas quando usado para geometria, não usando o ângulo, mas começa e termina com o par sin / cos e usa identidades trigonométricas para meios ângulos e assim por diante.
catraca aberração

Respostas:

8

[0 0,360)[0 0,2π)

  • [0 0,360][0 0,1]
  • Você obtém o benefício adicional de não precisar fazer o ajuste do intervalo do tipo loop (embora, de qualquer maneira, você não precisaria de loops e poderia apenas usar uma operação de módulo). Basta usar GL_REPEATcomo modo de quebra automática para a textura e ela será iniciada automaticamente novamente no início ao acessar com argumentos> 1 (e da mesma forma para argumentos negativos).
  • E você também tem o benefício de interpolar linearmente entre dois valores da matriz basicamente de graça (ou, digamos, quase de graça) usando GL_LINEARcomo filtro de textura, dessa forma obtendo valores que você nem mesmo armazenou. É claro que a interpolação linear não é 100% precisa para funções trigonométricas, mas certamente é melhor do que nenhuma interpolação.
  • Você pode armazenar mais de um valor na textura usando uma textura RGBA (ou quantos componentes você precisar). Dessa forma, você pode obter, por exemplo, sin e cos com uma única pesquisa de textura.
  • [-1,1][0 0,1]float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;(ou ainda mais componentes para grãos mais finos). Isso permite que você lucre com texturas de vários componentes novamente.

É claro que ainda precisa ser avaliado se essa é uma solução melhor, pois o acesso à textura também não é totalmente gratuito, e também qual seria a melhor combinação de tamanho e formato da textura.

Para preencher a textura com dados e endereçar um de seus comentários, você deve considerar que a filtragem de textura retorna o valor exato no centro texel , ou seja, uma coordenada de textura reduzida pela metade do tamanho texel. Então, sim, você deve gerar valores em .5texels , ou seja, algo parecido com isto no código do aplicativo:

float texels[256];
for(unsigned int i = 0; i < 256; ++i)
    texels[i] = sin((i + .5f) / 256.f) * TWO_PI);
glTexImage1D(GL_TEXTURE_1D, 0, ..., 256, 0, GL_RED, GL_FLOAT, texels);

uniform float sinTable[361]glUniform1fv[0 0,360)mod

angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];
Chris diz Restabelecer Monica
fonte
1
Aqui está uma extensão interessante para armazenar tabelas de pesquisa em texturas. Ele (ab) usa interpolação de textura N-linear para obter uma interpolação de ordem superior (também conhecida como melhor que linear) de pontos de dados, superfícies, volumes e hipervolumes. blog.demofox.org/2016/02/22/…
Alan Wolfe