Verwenden einer LUT, um einen trig-lastigen Shader für mobile Geräte zu beschleunigen


10

Ich versuche, diesen Shader auf einem wirklich alten iDevice sowie schließlich auf Androids zum Laufen zu bringen.

Selbst wenn ich den Code auf 2 Sinusfunktionen pro Fragment reduziere, läuft der Shader mit etwa 20 fps.

Ich habe darüber nachgedacht, ein Blatt aus dem Buch der alten Schattierungstechniken herauszunehmen und ein Array zu erstellen, das eine Reihe vordefinierter Triggerwerte enthält, und diese irgendwie zu verwenden, um den Shader zu approximieren.

In dem oben verlinkten Shader simuliere ich bereits, dass durch Runden der an die Triggerfunktion gesendeten Werte die Qualität des Shaders umso geringer ist, je weiter links die Maus (während der Abwärtsbewegung) ist. Es ist eigentlich ziemlich cool, weil es ganz in der Nähe der linken Seite wie ein völlig anderer und ziemlich cooler Shader aussieht.

Jedenfalls habe ich zwei Dilemmata:

  1. Ich weiß nicht, wie ich ein Array mit 360 Werten in einem GLSL-Shader, der konstant oder einheitlich ist, am effizientesten speichern kann.
  2. Ich kann nicht herausfinden, wie man eine Zahl in einen Bereich wie gewöhnlich einfügt, wenn ich einen Winkel zwischen 0 und 360 haben möchte (ja, ich weiß, dass GPUs Bogenmaß verwenden). Ich würde es so machen.

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

    GLSL erlaubt jedoch keine while-Schleifen oder rekursiven Funktionen.


Ich weiß nicht, ob es praktisch wäre, dies zu implementieren, aber würde es helfen, die vorberechneten Sinuswerte ungleichmäßig zu verteilen, mit enger gruppierten Werten, bei denen die Steigung der Sinuskurve am steilsten ist, und weniger Werten, bei denen sie sich ausgleichen und ändert sich nicht so viel? Würde dies eine höhere Genauigkeit ermöglichen, wo dies erforderlich ist, ohne dass eine große Anzahl von Werten gespeichert werden muss?
Trichoplax

5
In Bezug auf # 2 ist die eingebaute modFunktion genau das, was Sie wollen. Du würdest schreiben mod(angle, 360.0).
Nathan Reed

1
@trichoplax geniale Idee, aber ich weiß nicht, wie Sie dann Werte in der Tabelle nachschlagen können. Nehmen wir an, wir platzieren sie in einem Array, von denen einige konzentrierter sind. Wie können wir den richtigen Index finden?
J. Do

6
Wie wäre es, wenn Sie Ihre Werte in eine 3-Kanal-1D-Textur einfügen? Auf diese Weise können Sie Sünde, Cos und Bräune zum Preis einer einzelnen Textur-Suche herausholen. Wenn Sie den Winkel 0 - 2pi auf 0 - 1 UV abbilden und den Wiederholungstexturmodus verwenden, benötigen Sie nicht einmal den Mod-Aufruf. Er wird automatisch "umbrochen", und Sie können auch linear gefilterte Näherungen zwischen Ihren gespeicherten Werten anstatt erhalten auf den nächsten schnappen.
Russ

3
In vielen Fällen können Sie Triggerfunktionen eliminieren, wenn Sie sie für die Geometrie verwenden, indem Sie nicht den Winkel verwenden, sondern mit dem Sin / Cos-Paar beginnen und enden und Triggeridentitäten für Halbwinkel und dergleichen verwenden.
Ratschenfreak

Antworten:


8

[0,360)[0,2π)

  • [0,360][0,1]
  • Sie erhalten den zusätzlichen Vorteil, dass Sie Ihre schleifenartige Intervallanpassung nicht vornehmen müssen (obwohl Sie ohnehin keine Schleifen benötigen und nur eine Moduloperation verwenden könnten). Verwenden Sie einfach den GL_REPEATUmbruchmodus für die Textur und sie beginnt automatisch wieder am Anfang, wenn Sie mit Argumenten> 1 (und ähnlich für negative Argumente) zugreifen.
  • Außerdem erhalten Sie den Vorteil, dass Sie zwischen zwei Werten im Array im Grunde genommen kostenlos (oder fast kostenlos) linear interpolieren, indem Sie sie GL_LINEARals Texturfilter verwenden. Auf diese Weise erhalten Sie Werte, die Sie nicht einmal gespeichert haben. Natürlich ist die lineare Interpolation für trigonometrische Funktionen nicht 100% genau, aber sie ist sicherlich besser als keine Interpolation.
  • Sie können mehr als einen Wert in der Textur speichern, indem Sie eine RGBA-Textur verwenden (oder wie viele Komponenten Sie auch benötigen). Auf diese Weise können Sie zB sin und cos mit einer einzigen Textur-Suche erhalten.
  • [1,1][0,1]float sin = 2.0 * (texValue.r + texValue.g / 256.0) - 1.0;(oder noch mehr Komponenten für feineres Korn). So können Sie wieder von Mehrkomponenten-Texturen profitieren.

Natürlich muss noch geprüft werden, ob dies eine bessere Lösung ist, da der Texturzugriff auch nicht ganz kostenlos ist und die beste Kombination aus Texturgröße und Format vorliegt.

Um die Textur mit Daten zu füllen und einen Ihrer Kommentare zu adressieren, müssen Sie berücksichtigen, dass die Texturfilterung den genauen Wert in der Texelmitte zurückgibt , dh eine Texturkoordinate, die um die Hälfte der Texelgröße abweicht . Also ja, Sie sollten Werte bei .5Texels generieren , dh so etwas im Anwendungscode:

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,360)mod

angle = mod(angle, 360.0);
float value = sinTable[int(((angle < 0.0) ? (angle + 360.0) : angle) + 0.5)];

1
Hier ist eine interessante Erweiterung zum Speichern von Nachschlagetabellen in Texturen. Es (ab) verwendet die N-lineare Texturinterpolation, um eine Interpolation höherer Ordnung (auch bekannt als besser als linear) von Datenpunkten, Oberflächen, Volumina und Hypervolumina zu erhalten. blog.demofox.org/2016/02/22/…
Alan Wolfe
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.