Hallo zusammen, ich möchte meine gerenderten Ergebnisse nun speichern.
Nachdem ich über einen Framebuffer meine "gerenderten Werke" auf einer Textur habe, möchte ich diese nun weiterverarbeiten.
Bitte direkt runter zu Edit5 springen!
Info: Um zu verhindern, dass jetzt durch diese Frambuffer-Aktion Wechselwirkungen/Probleme auftreten, arbeite ich hier bei diesem konkreten Problem für den Forenbeitrag einfach "nur" mit einer von Festplatte geladenen Graphik.
Ich habe ganz stupide die Funktionen von TglBitmap2D (z.B. SavePNG) verwenden wollen, allerdings erhalte ich die Meldung:
Zitat:
SavePng - the given format isn't supported by this function.
Ursache ist, dass das InternalFormat des TglBitmap2D "ifEmpty" ist. Das kann ich mir partout nicht erklären, weil ich ja genau diese Textur dauernd fehlerfrei rendere. Nach dem Laden der Datei von Festplatte ist das Format garantiert immer "ifBGRA8"; ich weiß nicht, wann/weshalb die Textur ihr Format verliert...
Unabhängig davon würde ich eh am Liebsten die Textur einfach (incl. Alpha!) wieder auf ein TBitmap32 zurückladen. Das Laden aller Bilder findet über AssignFromBitmap32 statt, ich bräuchte also genau das Gegenteil davon:
Code:
function TTextur.AssignFromBitmap32(const aBitmap: TBitmap32):Boolean;
Log('TTextur.AssignFromBitmap32', M, E.Message, ws_SEVERITY_EXCEPTION);
end;
end;
end;
Ich habe den Code soweit verstanden, dass nach dem Laden der Arbeitsspeicherbereich "pData" die Pixeldaten enthält, und die Textur diese nun nach "SetDataPointer" als direkte Datenquelle verwendet. Beim Auslesen der Textur müsste ich nun aber so einen Befehl verwenden wie "GetDataPointer", den es allerdings nicht gibt.
Wäre jemand so lieb, und würde mir die Prozedur von "Laden aus Bitmap32" umschreiben nach "Schreibe in Bitmap32"? Oder zumindest ein paar Befehle, wie ich das rüberbekomme.
Alternativ: Falls jemand die Ursache weiß, warum meine imo gültige Textur auf einmal das InternalFormat ifEmpty hat und wie ich das beheben kann, würde ich natürlich auch einfach SavePNG verwenden...
Sieht eigentlich nett aus, aber pSource, also Self.Scanline[Row] ist immer NIL. Vielleicht verliere ich wirklich die Textur irgendwo?
Edit2: Ok, direkt beim Laden setzt GenTexture das InternalFormat auf ifEmpty zurück, da FreeDataAfterGenTexture aktiv ist; ist ja auch irgenwo verständlich. D.h. wahrscheinlich ist die Textur nun im Graphikkartenspeicher, und nicht mehr im normalen RAM. FreeDataAfterGenTexture auf "False" zu setzen würde zwar meine Demo hier zum Laufen bringen, aber in der Praxis lasse ich die Textur ja über einen FrameBuffer erzeugen: damit sind ja nicht Daten im Objekt vorhanden und ich bin genauso nass wie vorher. Ich muss also wissen, wie ich die Daten der Textur wieder lesbar aus der Graphikkarte raushole?
Edit3: GetDataFromTexture - war ja auch zu einfach.
Code:
ms := TMemoryStream.Create;
ms.Position:=0;
ConvertTo(ifBGRA8);
Self.SavePNG(ms);
ms.Position:=0;
png := TPngImage.Create;
png.LoadFromStream(ms);
FreeAndNil(ms);
png.SaveToFile('c:\temp\musterpng.png');
Das hier funktioniert schon mal. Zur Not kann ich mir das PNG-Objekt an TBitmap32 zuweisen.
Mit einer Textur, in die ich vorher ein Bild geladen habe, funktioniert das auch einwandfrei. Nur wenn ich vom FrameBuffer kopiere kann ich zwar die (kopierte) Textur einwandfrei rendern, aber die abgespeicherte Datei besteht zu 100% aus transparenten Pixeln. Was mache ich falsch?
Registriert: Di Apr 29, 2008 18:56 Beiträge: 1213
Programmiersprache: Delphi/FPC
Hey,
du hast noch ein sehr alte Version der glBitmap. Die neue von der Community überarbeitet Variante hat eine DownloadData Methode, mit der du die Daten aus einer Textur wieder in den RAM laden und dann in einem beliebigen Format speichern kannst.
Ich habe leider den Spezialfall, dass Hintergrundbereiche im Framebuffer transparent bleiben müssen (also auch später in der gespeicherten PNG-Datei), und mit Deiner Methode gehen leider die "Partielle-Transparenz"-Informationen (Alpha-Scanline) verloren - soweit ich das sehen konnte(?). Auch mit rumbastelei habe ich es damit nicht hinbekommen.
Bei meinem Problem konnte ich feststellen, dass ich wohl nochmal (durch die Zeile aus dem Tutorial) "prozedural" eine andere Textur gebunden habe, als die, die ich vorher mit tmpTextur.Bind gebunden hatte: Deshalb lief der Renderdurchlauf ins Leere -oder zumindest nicht auf die Textur, die ich später tatsächlich rendere. Soweit ich mich erinnere muss man aus dem o.g. Beispiel nur die Zeile
Nach sehr vielen Hin- und Her habe ich jetzt auch das Ergebnis so wie ich es haben will, aber der Aufwand ist aktuell noch viel zu groß, vielleicht kann mir nochmal kurz jemand einen Tip geben:
1) Ich render Zweidimensional im glOrtho-Modus. Diesen muss ich für den Framebuffer ändern (auf die Größe des FBO). Kann man die Abmessungen von glOrtho auch irgendwie mit glPushAttrib or so zwischenspeichern, oder zumindest über einen Befehl die Größe des aktuellen glOrtho-Bereichs auslesen?
2) Im Framebuffer wird -trotz imo korrekten ViewPorts/glOrtho- "auf dem Kopf" gerendert, so dass ich nun meine "normalen" Texturen habe, und die zur Laufzeit erstellten, die nun leider auf dem Kopf stehen. Was muss ich wie ändern, dass im FBO richtig herum gerendert wird, oder wie kann ich die Textur einfach und schnell auf den Kopf stellen/spiegeln (nicht erst beim Rendern!)
FrameBuffer für meine 2d-Ansprüche, Objektorientiert:
Ich habe leider den Spezialfall, dass Hintergrundbereiche im Framebuffer transparent bleiben müssen (also auch später in der gespeicherten PNG-Datei), und mit Deiner Methode gehen leider die "Partielle-Transparenz"-Informationen (Alpha-Scanline) verloren - soweit ich das sehen konnte(?). Auch mit rumbastelei habe ich es damit nicht hinbekommen.
Eine blöde Frage, hast du Alphablending aktiviert ?
Code:
glEnable(GL_BLEND);// Alphablending an
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);// Sortierung der Primitiven von hinten nach vorne.
Zitat:
Im Framebuffer wird -trotz imo korrekten ViewPorts/glOrtho- "auf dem Kopf" gerendert, so dass ich nun meine "normalen" Texturen habe, und die zur Laufzeit erstellten, die nun leider auf dem Kopf stehen. Was muss ich wie ändern, dass im FBO richtig herum gerendert wird, oder wie kann ich die Textur einfach und schnell auf den Kopf stellen/spiegeln (nicht erst beim Rendern!)
Kann man die Abmessungen von glOrtho auch irgendwie mit glPushAttrib or so zwischenspeichern, oder zumindest über einen Befehl die Größe des aktuellen glOrtho-Bereichs auslesen?
Ja dies sollte gehen, ich weis nur nicht mehr wie der Befehl heisst, man kann die Matrix speichern und laden.
PS:Kannst du bitte die Code-Tags für Pascal verwenden.
Code:
(code=pascal)Writeln('Hello World');(/code)
Die () einfch durch [] ersetzen.
_________________ OpenGL
Zuletzt geändert von mathias am Do Jun 21, 2018 21:16, insgesamt 1-mal geändert.
Zunächst mal: Vielen Dank für die Rückmeldungen und Ideen!
@Bergmann89: Update werde ich zeitnah in einer ruhigen Minuten testen und installieren.
@mathias:
Zitat:
Eine blöde Frage, hast du Alphablending aktiviert ?
Die Frage ist zwar generell berechtigt, aber hier glaube ich nicht zutreffend. Zu Erinnerung: Nehmen wir an, ich will eine farbige Kreisfläche auf eine quadratische Textur zeichnen, will ich ja nur den Kreishaben, nicht den freien Bereich bis in die Ecken des Quadrads. Soweit klar. Wenn ich die Textur als PNG auf Festplatte speichere (PNG unterstützt ja partielle Transparenz) und in einem Graphikprogramm lade, werden die freien Flächen (also die Ecken, "das, was nicht Kreis ist") je nach Graphikprogramm z.B. mit einem dezenten Schachbrettmuster hinterlegt um zu zeigen: Dieser Bereich ist durchsichtig.
Das von dir beschriebene Verfahren lässt sich mit Delphi 2010 leider nicht verwenden, da ich hier auf "RawImage" bzw. "RawImage.Data" nicht zugreifen kann; das gibt es hier nicht.
Bei dem im Tutorial verlinkten zweiten Verfahren, glaube ich, werden wirklich 1:1 die Pixel "vom Bildschirm" gelesen, oder vom Framebuffer die Farbe, die für glClearColor verwendet wurde. Korrigiere mich, wenn ich falsch liege.
Das "auf den Kopf stellen" der Textur erledige ich nun bei FrameBuffer.Unbind automatisch durch die TglBitmap2d.FlipVertical. Tolle Sache, aber dran denken: Ohne Daten keine Texturbearbeitung! Also:
Code:
procedure TglBitmap2dNachfahre.FlipV;
begin
GetDataFromTexture;// Daten laden
FlipVert;// Vertikal spiegeln
GenTexture;// Daten "speichern"
FreeData;// Speicher freigeben, passiert mit GenTexture (je nach Einstellung) ggf. automatisch
end;
Der Tip mit glScalef(1.0, -1.0, 0.0) war das, was ich ursprünglich die ganze Zeit im Hinterkopf hatte, aber nicht abrufen konnte. Ich meine, es war mal in irgend einem Einsteigertutorial genannt. Leider stellt es im laufenden Betrieb die komplette Scene auf den Kopf, so dass ich in jeden Renderdurchlauf das Bild rumgedreht bekomme. Ich meine zwar eigentlich, den Befehl nur innerhalb der Arbeiten mit dem Framebuffer verwendet zu haben, und danach -testweise mit dem selben Befehl, testweise mit glScalef(1.0, 1.0, 0.0)- wieder für Ordnung gesorgt zu haben, aber ich konnte kein Befriedigendes Ergebnis erziehlen. Wie gesagt, Textur spiegeln hat sich ja bereits erledigt, also da keine Energie mehr reinstecken.
Das mit glOrtho ist halt noch ein ziemlich krasser Knackpunkt für mich. Entweder muss ich allen meinen glObjekten eine Callback-Möglichkeit geben, um den Orthomodus wieder korrekt setzen zu lassen, oder jemand hat noch einen umwerfenden Tip. Mit Matrizen kenne ich mich zu wenig aus, und ich weiß auch nicht, ob alle PCs, die jünger als 5 Jahre sind wirklich OpenGL 3.3 können. Deshalb mache ich es lieber umständlicher, aber dafür kompatibel.
Mit Matrizen kenne ich mich zu wenig aus, und ich weiß auch nicht, ob alle PCs, die jünger als 5 Jahre sind wirklich OpenGL 3.3 können.
Es könnte auch OpenGL 2.0 reichen, die Manuelle Behandlung von Matrizen wird meistens verwendet, wen man auch mit Shader arbeitet.
Zitat:
Das von dir beschriebene Verfahren lässt sich mit Delphi 2010 leider nicht verwenden, da ich hier auf "RawImage" bzw. "RawImage.Data" nicht zugreifen kann; das gibt es hier nicht.
Gibt es bei Delphi keine Möglichkeit direkt auf die Raw-Daten der Bitmap zu zugreifen ?
glOrtho erzeugt eine parallele projektionsmatrix und multipliziert diese mit der aktuellen matrix. Wenn du die aktuelle matrix speichern moechtest kannst du das mit glGet machen:
Code:
float matrix[16];
glGetFloatv(GL_PROJECTION_MATRIX, matrix);
glOrtho(....);
[...]
glLoadMatrix(matrix);
Ist C++, aber denke das prinzip wird verstaendlich. Habe leider Delphi seit ewigkeiten nichtmehr genutzt und erinnere mich kaum daran wie die syntax ist.
Zu deinem problem der auf dem kopf stehenden Bilder: Natuerlich funktioniert die flipVertical() funktion, allerdings vermute ich (ich hab sie nicht angeschaut) das diese das Bild im RAM flipt. Funktioniert, ist aber langsam und prinzipiell ueberfluessig. Du kannst einfach den top und bottom parameter in der glOrtho funktion austauschen - damit drehst du sozusagen deinen Viewport auf den kopf und damit die Textur.
glClear(GL_COLOR_BUFFER_BIT or GL_DEPTH_BUFFER_BIT);
end;
procedure TglFrameBuffer.UnBind;
begin
Textur.FlipV;
glPopAttrib;
glLoadMatrixf(@FOriginalMatrix);
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glBindFramebufferEXT(GL_FRAMEBUFFER_EXT,0);
end;
Wie ist denn der korrekte Datentyp aus der dglOpenGL, so dass ich den TMatrix-Typ jetzt nicht nochmal neu deklarieren muss?
Ich hatte FOriginalMatrix mal mit dem Typ TVector4f und TMatrix4f aus der dglOpenGL.pas versucht, da diese zumindest dem Namen nach (TMatrix4f) und vom Datentyp her (TGLVectorf4 = array[0..3] of GLfloat) für diesen Zweck vorgesehen sein sollten. Dies hat auch generell funktioniert, aber das Programm ist nach einigen Sekunden mit Speicherverletzung abgestürzt, also hat er wohl beim Speichern der Matrix wohingeschrieben, wo es eigentlich nicht vorgesehen war(?).
Ist der Code nun soweit richtig? Mit puren Pointern kenne ich mich nicht so aus, wann nun das @ davor soll, und wann nicht etc. Muss FOriginalMatrix irgendwie initialisiert oder freigegeben werden?
---
Der Tip mit glOrtho auf den Kopf stellen ist gut, aber irgendwie ist dann nichts mehr zu sehen. Ich gehe davon aus, dass meine anderen Komponenten die ich rendern lasse dann irgendwo "OffScreen" landen, weil nun doch der Ursprung anders ist, bzw. nun das Render im Uhrzeigersinn oder so erfolgen müsste, oder ich Culling an/aus machen müsste? Da meine mit dem Framebuffer erzeugte Textur nur ca. 1x / Stunde aktualisiert wird, nehme ich den Mehraufwand durch das spiegeln im RAM erst Mal hin.
Mitglieder in diesem Forum: 0 Mitglieder und 14 Gäste
Du darfst keine neuen Themen in diesem Forum erstellen. Du darfst keine Antworten zu Themen in diesem Forum erstellen. Du darfst deine Beiträge in diesem Forum nicht ändern. Du darfst deine Beiträge in diesem Forum nicht löschen. Du darfst keine Dateianhänge in diesem Forum erstellen.