Wie konvertiere ich einen vec4 rgba Wert in einen float?

8

Ich habe einige Float-Daten in einer Textur gepackt, als unsigned_byte, meine einzige Option in webgl. Jetzt möchte ich es im Vertex-Shader entpacken. Wenn ich ein Pixel sample, bekomme ich einen vec4, der wirklich einer meiner Floats ist. Wie konvertiere ich vom vec4 in einen Float?

    
Xavier 14.08.2011, 21:51
quelle

5 Antworten

12

Der folgende Code ist speziell für die iPhone 4-GPU mit OpenGL ES 2.0. Ich habe keine Erfahrung mit WebGL, also kann ich nicht behaupten zu wissen, wie der Code in diesem Kontext funktioniert. Außerdem besteht das Hauptproblem darin, dass highp float nicht 32 Bit sondern 24 Bit ist.

Meine Lösung ist für Fragment-Shader - ich habe es nicht im Vertex-Shader versucht, aber es sollte nicht anders sein. Um das zu verwenden, müssen Sie das RGBA-Texel von einem sampler2d einheitlich erhalten und sicherstellen, dass die Werte jedes R-, G-, B- und A-Kanals zwischen 0,0 und 255,0 liegen. Dies ist leicht wie folgt zu erreichen:

%Vor%

Sie sollten sich jedoch bewusst sein, dass die Endgültigkeit Ihrer Maschine die richtige Reihenfolge Ihrer Bytes vorgibt. Der obige Code geht davon aus, dass Floats in Big-Endian-Reihenfolge gespeichert werden. Wenn Sie sehen, dass Ihre Ergebnisse falsch sind, vertauschen Sie einfach die Reihenfolge der Daten, indem Sie

schreiben %Vor%

unmittelbar nach der Zeile, in der Sie es festgelegt haben. Alternativ tauschen Sie die Indizes auf rgba aus. Ich denke, die obige Zeile ist zwar intuitiver und weniger anfällig für unvorsichtige Fehler. Ich bin mir nicht sicher, ob es für alle gegebenen Eingaben funktioniert. Ich habe für eine große Anzahl von Zahlen getestet und festgestellt, dass decode32 und encode32 NICHT exakte Inverse sind. Ich habe auch den Code weggelassen, mit dem ich ihn getestet habe.

%Vor%

Hier sind einige Links auf IEEE-Genauigkeit, die ich nützlich fand. Link1 . Link2 . Link3 .

    
twerdster 29.08.2011, 23:47
quelle
2

Da Sie uns nicht den genauen Code gegeben haben, den Sie zum Erstellen und Hochladen Ihrer Textur verwendet haben, kann ich nur raten, was Sie tun.

Sie scheinen ein JavaScript-Array mit Fließkommazahlen zu erstellen. Sie erstellen dann ein Uint8Array und übergeben dieses Array an den Konstruktor.

Entsprechend der WebGL-Spezifikation (bzw. der Spezifikation, auf die sich die WebGL-Spezifikation bezieht, wenn dieses Verhalten scheinbar angegeben wird) erfolgt die Konvertierung von Fließkommazahlen in vorzeichenlose Bytes auf eine von zwei Arten, basierend auf dem Ziel. Wenn das Ziel als "geklemmt" betrachtet wird, klemmt es die Zahl für den Zielbereich fest, nämlich [0, 255]. Wenn das Ziel nicht als "geklemmt" betrachtet wird, wird Modulo 2 8 genommen. Die WebGL "Spezifikation" ist ausreichend schlecht, so dass nicht ganz klar ist, ob die Konstruktion von Uint8Array als geklemmt angesehen wird oder nicht. Ob geklemmt oder modulo 2 8 genommen, der Dezimalpunkt wird abgeschnitten und der ganzzahlige Wert gespeichert.

Wenn Sie diese Daten jedoch Open WebGL übergeben, haben Sie WebGL angewiesen, die Bytes als normalisierte vorzeichenlose Ganzzahlwerte zu interpretieren. Dies bedeutet, dass die Eingabewerte für den Bereich [0, 255] von Benutzern der Textur als Gleitkommawerte [0, 1] aufgerufen werden.

Wenn also Ihr Eingabe-Array den Wert 183,45 hätte, wäre der Wert in Uint8Array 183. Der Wert in der Textur wäre 183/255 oder 0,718. Wenn Ihr Eingabewert 0,45 wäre, würde Uint8Array 0 enthalten und das Texturergebnis wäre 0,0.

Nun, da Sie die Daten als GL_RGBA übergeben haben, bedeutet dies, dass alle 4 unsignierten Bytes als ein einzelnes Texel genommen werden. Also ruft jeder Aufruf von texture diese bestimmten vier Werte (an der gegebenen Texturkoordinate unter Verwendung der gegebenen Filterparameter) ab und gibt somit vec4 zurück.

Es ist nicht klar, was Sie mit diesen Fließkommadaten machen wollen. Daher ist es schwierig, Vorschläge zu machen, wie Float-Daten am besten an einen Shader übergeben werden. Eine allgemeine Lösung wäre jedoch, die Erweiterung OES_texture_float zu verwenden und tatsächlich eine Textur zu erstellen, die Fließkommadaten speichert. Natürlich, wenn es nicht verfügbar ist, müssen Sie immer noch einen Weg finden, um zu tun, was Sie wollen.

BTW, Khronos sollte sich wirklich schämen, WebGL sogar als Spezifikation zu bezeichnen. Es gibt kaum etwas an; Es sind nur eine Reihe von Verweisen auf andere Spezifikationen, die es extrem schwierig machen, die Auswirkungen von etwas zu finden.

    
Nicol Bolas 14.08.2011 23:34
quelle
2

Twerdster hat in seiner Antwort einen ausgezeichneten Code geschrieben. Also gehen alle Kredite an ihn. Ich poste diese neue Antwort, da Kommentare nicht für nette Syntax farbige Codeblöcke erlauben, und ich etwas Code teilen wollte. Aber wenn Sie den Code mögen, upvote Twerdster ursprüngliche Antwort.

In Twerdsters vorherigem Post erwähnte er, dass die Decodierung und Codierung möglicherweise nicht für alle Werte funktioniert.

Um dies weiter zu testen und das Ergebnis zu validieren, habe ich ein Java-Programm erstellt. Beim Portieren des Codes habe ich versucht, so nah wie möglich am Shader-Code zu bleiben (daher habe ich einige Hilfsfunktionen implementiert). Hinweis: Ich verwende auch eine Store / Load-Funktion, um zu simulieren, was passiert, wenn Sie aus einer Textur schreiben / lesen.

Ich habe das herausgefunden:

  1. Sie benötigen einen Sonderfall für die Nullstelle
  2. Sie könnten auch einen Sonderfall für unendlich benötigen, aber ich habe das nicht implementiert, um den Shader einfach zu halten (zB: schneller)
  3. Aufgrund von Rundungsfehlern war das Ergebnis manchmal falsch:
    • subtrahiere 1 vom Exponenten, wenn die Mantisse wegen Rundung nicht richtig normalisiert ist (z. B. Mantisse & lt; 1)
    • Ändern Sie float Mantissa = (exp2(- Exponent) * F); in float Mantissa = F/exp2(Exponent); , um Präzisionsfehler zu reduzieren
    • Verwenden Sie float Exponent = floor(log2(F)); , um den Exponenten zu berechnen. (vereinfacht durch neue Mantissenprüfung)

Mit diesen kleinen Modifikationen habe ich an fast allen Eingängen die gleiche Ausgabe erhalten und nur kleine Fehler zwischen Original und encoded / decodiert, wenn Dinge schief gehen, während in der ursprünglichen Implementierung von Twerdster Rundungsfehler oft den falschen Exponenten zur Folge hatten das Ergebnis ist um den Faktor zwei).

Bitte beachten Sie, dass dies eine Java-Testanwendung ist, die ich geschrieben habe, um den Algorithmus zu testen. Ich hoffe, dass dies auch funktioniert, wenn es auf die GPU portiert wird. Wenn jemand versucht, es auf einer GPU auszuführen, hinterlasse einen Kommentar mit deiner Erfahrung.

Und für den Code mit einem einfachen Test, verschiedene Zahlen zu versuchen, bis es fehlschlägt.

%Vor%     
Arjan 22.06.2012 14:46
quelle
2

Ich versuchte Arjans Lösung, aber es gab ungültige Werte für 0, 1, 2, 4 zurück. Es gab einen Fehler mit dem Packen des Exponenten, den ich änderte, also nimmt das Exp ein 8bit Schwimmer und das Zeichen wird mit dem gepackt Mantisse:

%Vor%     
Hauke Rehfeld 06.02.2013 12:26
quelle
0

Sie können die 4 nicht vorzeichenbehafteten Bytes nicht einfach als Bits eines Gleitkommawerts (von dem ich annehme, dass Sie das wollen) in einem Shader interpretieren (zumindest nicht in GLES oder WebGL, glaube ich). Was Sie tun können, ist nicht die Gleitkomma-Bit-Repräsentation in den 4 Bytes, sondern die Bits der Mantisse (oder der Festkommadarstellung) zu speichern. Dazu müssen Sie den ungefähren Bereich der Schwimmer kennen (ich nehme hier vereinfachend [0,1] an, sonst müssen Sie natürlich anders skalieren):

%Vor%

Natürlich können Sie auch direkt mit den Mantissenbits arbeiten. Und dann im Shader können Sie es einfach so rekonstruieren, dass die Komponenten von vec4 alle in [0,1] sind:

%Vor%

Obwohl ich nicht sicher bin, ob dies genau denselben Wert ergibt, sollten die Zweierpotenzen ein wenig helfen.

    
Christian Rau 14.08.2011 22:13
quelle

Tags und Links