Entwickler-Ecke
Multimedia / Grafik - mit OpenGL Szene in Datei rendern
Xion - Sa 06.09.08 11:31
Titel: mit OpenGL Szene in Datei rendern
Hi,
ich möchte eine Szene in OpenGL als Bild speichern. Indem ich hier etwas rumgespielt habe (4x so große Fläche, nur ein Viertel aufm Bildschirm rendern), konnte ich ber "Druck"-Taste 4 Screenshots erstellen und diese zusammenfügen.
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7:
| glViewport(0, 0, VPanel.ClientWidth, VPanel.ClientHeight); glMatrixMode(GL_PROJECTION); glLoadIdentity; gluPerspective(45.0, VPanel.ClientWidth/VPanel.ClientHeight, NearClipping, FarClipping);
glMatrixMode(GL_MODELVIEW); glLoadIdentity; |
Ja, das ist Mist, ich weiß :mrgreen: Vor allem weil es nicht mit mehr als doeppelte Auflösung geht. Gibt es da nicht eine vernünftige Art, sowas zu machen?
Danke, Xion
BenBE - Sa 06.09.08 17:39
Ja, indem man sich einen Textur-Puffer anlegt und in diesen Rendert und dann diesen in ein Bitmap. Beispiele gibt's bei DelphiGL ...
Xion - So 07.09.08 10:44
@toenne: bei deinem Code raucht bei mir irgendwie die Grafikkarte ab oO
@BenBE: irgendwie find ich nix. ich find nie was bei DelhpiGL, kA, ich glaube ich bediene die Suche falsch :P
ich habs mal so gemacht (
von hier [
http://www.delphigl.com/forum/viewtopic.php?t=3924&highlight=snapshot] )
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| procedure SnapShot; var ATarget: TBitmap; begin ATarget:=TBitmap.Create; ATarget.PixelFormat := pfDevice; ATarget.Width := Screen.Width; ATarget.Height := Screen.Height; BitBlt(ATarget.Canvas.Handle, 0, 0, Screen.Width, Screen.Height, DC, 0, 0, SRCCOPY); ATarget.SaveToFile('test.bmp'); end; |
Das Problem ist aber, dass ich damit ja nicht über die Bildschirmgröße rauskomme. Ich hätte es eigentlich gerne so groß, dass man es drucken kann (also ca. 3*3-fache Bildschirmgröße)
toenne - So 07.09.08 15:35
Zitat: |
@toenne: bei deinem Code raucht bei mir irgendwie die Grafikkarte ab oO |
Hast du ihn doch komplett kopiert oder wie?
Du brauchst doch nur...
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9:
| glGetIntegerv(GL_VIEWPORT, @Viewport); Rohbild := TBitmap.Create; try Rohbild.Width := Viewport[2]; Rohbild.Height := Viewport[3]; Rohbild.PixelFormat := pf24bit; for i := 0 to Viewport[3] - 1 do glReadPixels(0, Viewport[3] - i, Viewport[2], 1, GL_BGR, GL_UNSIGNED_BYTE, Rohbild.ScanLine[i]); |
...und schon hast du eine Kopie in 'Rohbild'. Der ganze andere Krempel ist für dich irrelevant.
Aber wie ich dich jetzt verstehe willst du gar nicht den Bildschirminhalt kopieren sondern die ganze Szene? Hmm, was über die Bildschirmgrenzen hinausgeht wird doch gar nicht gerendert denke ich?
Gruss
Toenne
BenBE - So 07.09.08 16:18
Ich denke, er will eine Kopie eines Viewports haben, der größer als der Bildschirm ist ...
Lossy eX - Mo 08.09.08 11:18
Xion: Ich denke du hast 2 Möglichkeiten.
1) Wie es BenBE schon so vorgeschlagen hatte. Du erzeugst einen offscreen buffer und renderst dort dein Bild hinein. Das Problem ist aber Aufgrund des Formates den ein OffScreen Buffer (32Bit RGBA + Tiefenbuffer etc) hat steigt der Speicherverbrauch um ein vielfaches an. Und dessen Größe ist irgendwoe auch begrenzt. Oben drein kommt hinzu, dass du recht hohe Hardwareanforderungen hast. Aber mit einem
FrameBuffer Object [
http://wiki.delphigl.com/index.php/Tutorial_Framebufferobject] wäre es möglich einen solchen Buffer zu erstellen. Da der seine Daten in einer Textur ablegt wäre es ziemlich einfach die Daten abzufragen.
2) Du musst deine Szene X mal Rendern und immer die passenden Teile des Bildes kopieren und dir daraus ein großes Bild zusammen basteln. Es gab vor ewigen Zeiten auf delphi3d.net mal ein Demo dazu aber leider ist die Seite seit geraumer Zeit offline. Ich weiß es mittlerweile nicht mehr genau was der dort gemacht hatte deswegen kann ich nur einen eigenen Vorschlag machen. Allerdings habe ich keine Ahnung ob das immer so funktioniert.
Du kannst einen Viewport setzen der entsprechend groß ist. Allerdings der Framebuffer bekommt die maximal Größe des Fensters. Bei glViewport setzt du normal ClientHeight/ClientWidth. Wenn du aber deine gewünschte Größe einsetzt, dann wird das Bild entsprechend Groß gezeichnet. Du kannst bei X und Y dann aber entsprechende Werte setzen um den Viewport zu verschieben und einen anderen Teil in den Framebuffer zu verschieben. Der Einfachheit halber solltet du zu Begin erst mal mit einer Größe von 512x512 arbeiten. Beispiel:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10:
| glViewport(0, 0, 8192, 8192);
glViewport(512, 0, 8192, 8192);
glViewport(1024, 0, 8192, 8192);
usw. |
Das müsstest du so häufig machen bist du alle Teile kopiert hast. Kopieren musst du dann Zeilenweise. Dazu solltest du dich mal in dem Screenshot Artikel im Wiki von DGL umschauen. Also prinzipiel mit glReadPixel. Du solltest
glReadBuffer auf
GL_BACK stellen und
SwapBuffers nicht ausführen wärend du die Teile generierst. Dann wird aus dem nicht sichtbaren Backbuffer gelesen und niemand bekommt diese temporären Bilder zu sehen. Aber je nach Größe der Szene kann es dauern, da du im Falle von 8192x8192 die gesammte Szene 256 Mal zeichnen musst. Du kannst aber auch größere Stücke als 512x512 nehmen. Könnte nur beim Zusammenbauen etwas komplizierter/verwirrender werden.
Ich weiß allerdings nicht ob es zu einem Problem führen kann, wenn der Viewport zu groß wird. Mitunter können einige Karten nur einen Viewport von 4096x4096 Pixel. Ich weiß nicht ob sich das auf die maximale Anzeigegröße bezieht oder wirklich auf Width und Height bei glViewport. Die größe kannst du wie folgt abfragen.
Delphi-Quelltext
1: 2: 3: 4:
| var VP: array[0..1] of Integer; begin glGetIntegerv(GL_MAX_VIEWPORT_DIMS, @VP); |
Xion - Fr 12.09.08 14:53
wow, danke, ich hab momentan nicht so viel Zeit.
Also ich habs nach methode 2 schonmal probiert, hab sie jetzt auch einigermaßen zum laufen bekommen (bin am notebook fast verzweifelt, scheinbar kann es nicht mehr als 2x2 Bildschirme berechnen). Leider ist es nicht ganz einwandfrei, Size=2 funktioniert prima, Size=3 ist unten etwas abgeschnitten und Size=4 ist verzerrt oO
Hier mal der Code:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32:
| procedure TOpenGLWorks.SnapShot(Redraw: TTickProc; GameTimer: TGameTimer); var ATarget: Graphics.TBitmap; X,Y,W,H: integer; Size: integer; begin Size:=3; W:=VPanel.ClientWidth; H:=VPanel.ClientHeight;
glViewport( 0, 0, W*Size, H*Size ); Redraw(0);
ATarget:=Graphics.TBitmap.Create; ATarget.PixelFormat := pfDevice; ATarget.Width := W*Size; ATarget.Height := H*Size; GameTimer.Enabled:=False; for X:= 0 to (Size-1) do for Y:= 0 to (Size-1) do begin glViewport( -X*W, -Y*H, W*Size, H*Size ); Redraw(0); glViewport( -X*W, -Y*H, W*Size, H*Size ); Redraw(0); BitBlt(ATarget.Canvas.Handle, X*W, ( (Size-1)-Y)*H, W,H, DC, 0, 0, SRCCOPY); end; ATarget.SaveToFile('test.bmp');
glViewport( 0, 0, W, H ); Redraw(0); GameTimer.Enabled:=True; end; |
Zudem verstehe ich nicht, warum ich 2x die Szene rendern muss, um ein anständiges Bild zu bekommen (sonst kommt jeder Block doppelt)
Xion - Sa 13.09.08 12:54
das hier ist durch den obigen Code entstanden...das Ergebnis ist mir unerklärlich (das rausgeschnittene Eck).
Originalgröße: 4428x3328
Anmerkung: er scheint irgendwo nicht lange genug zu warten, bis er es als Screenshot ausliest. Ich denke mal das ist die differenz zwischen dem fertigen rendern und dem tatsächlichen anzeigen auf dem Bildschirm. Ich habs mal mit glReadPixels versucht, funktioniert aber leider so nicht:
Delphi-Quelltext
1: 2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15: 16: 17: 18: 19: 20: 21: 22: 23: 24: 25: 26: 27: 28: 29: 30: 31: 32: 33: 34: 35: 36: 37: 38: 39: 40: 41: 42: 43: 44: 45: 46: 47: 48:
| procedure TOpenGLWorks.SnapShot(Redraw: TTickProc; GameTimer: TGameTimer; Size: integer); var ATarget, SubTarget: Graphics.TBitmap; X,Y,W,H: integer; DestRect,TargetRect: TRect; begin
W:=VPanel.ClientWidth; H:=VPanel.ClientHeight;
glViewport( 0, 0, W*Size, H*Size ); Redraw(0);
DestRect.Left:=0; DestRect.Top:=0; DestRect.Right:=W; DestRect.Bottom:=H;
ATarget:=Graphics.TBitmap.Create; ATarget.PixelFormat := pfDevice; ATarget.Width := W*Size; ATarget.Height := H*Size;
SubTarget:=Graphics.TBitmap.Create; SubTarget.PixelFormat := pfDevice; SubTarget.Width := W; SubTarget.Height := H;
GameTimer.Enabled:=False; for X:= 0 to (Size-1) do for Y:= 0 to (Size-1) do begin glViewport( -X*W, -Y*H, W*Size, H*Size ); Redraw(0); glReadPixels( X*W, Y*H, W, H, GL_RGB, GL_BITMAP, @SubTarget );
TargetRect.Left:=X*W; TargetRect.Top:=Y*H; TargetRect.Right:=W+X*W; TargetRect.Bottom:=H+Y*H;
ATarget.Canvas.CopyRect(TargetRect,SubTarget.Canvas,DestRect); end; ATarget.SaveToFile('test.bmp');
glViewport( 0, 0, W, H ); Redraw(0); GameTimer.Enabled:=True; end; |
Entwickler-Ecke.de based on phpBB
Copyright 2002 - 2011 by Tino Teuber, Copyright 2011 - 2025 by Christian Stelzmann Alle Rechte vorbehalten.
Alle Beiträge stammen von dritten Personen und dürfen geltendes Recht nicht verletzen.
Entwickler-Ecke und die zugehörigen Webseiten distanzieren sich ausdrücklich von Fremdinhalten jeglicher Art!