Du bist nicht angemeldet.

Stilllegung des Forums
Das Forum wurde am 05.06.2023 nach über 20 Jahren stillgelegt (weitere Informationen und ein kleiner Rückblick).
Registrierungen, Anmeldungen und Postings sind nicht mehr möglich. Öffentliche Inhalte sind weiterhin zugänglich.
Das Team von spieleprogrammierer.de bedankt sich bei der Community für die vielen schönen Jahre.
Wenn du eine deutschsprachige Spieleentwickler-Community suchst, schau doch mal im Discord und auf ZFX vorbei!

Werbeanzeige

1

21.11.2007, 23:33

PNG's schnell und einfach laden

Heute möchte ich euch einmal zeigen, wie man sehr leicht PNG-Bilddateien in seinem Spiel verwenden kann. Als Sprache wird C++ verwendet, mit dem geladenen Bild kann man dann machen, was man will (OpenGL oder DX Textur und so weiter).

Warum PNG? Nun PNG ist ein weit verbreitetes Format, um Rastergrafiken (solche die aus einzelnen Pixel bestehen) komprimiert abspeichern zu können. Im Gegensatz zum ebenfalls weit verbreiteten JPEG-Formats ist PNG aber absolut verlustfrei. Außerdem kann es einen Alphawert speichern, was bei 2D Spielen und auch in vielen Situationen bei 3D Spielens sehr wichtig ist.

Zum Laden von PNG-Dateien gibt es eine Referenz Bibliothek, names libpng. Diese ist allerdings ein ziemlich Klotz und will erst einmal verstanden werden. In diesem Tutorial benutze ich aber nicht libpng, sondern picopng.

PicoPNG ist eine sehr kompakte Bibliothek. Obwohl Bibliothek eigentlich das falsche Wort ist, es handelt sich nur um eine einzige C++ Funktion. Das schöne an ihr ist, das keine externen Abhängigkeiten wie libpng oder zlib (PNG benutzt zlib zur Kompression) benötigt werden. Man hat in einer einzigen Funktion alles was man benötigt! Daher ist diese „Bibliothek“ auch so enorm klein, der gesamte Quelltext hat nur 33 kb (kompiliert dementsprechend noch kleiner).
Netterweise steht das ganze unter einer sehr liberalen OpenSource Lizenz, man kann damit so gut wie alles machen (benutzen, verbreiten, verändern, verkaufen, usw.).

Diese eine „magische“ Funktion heißt decodePNG. Sie erwartet einen Zeiger auf die PNG-Daten und liefert ein std::vector mit dem geladenen 32 Bit Bild. Daher müssen wir selber erst die Bilddatei in unseren Speicher laden und diesen dann decodePNG übergeben. Das hat den Vorteil, dass man nicht nur echte Dateien laden kann, sondern die Bilder auch aus einem Archiv oder aus einer Ressource oder direkt aus dem Internet geladen speichern kann. Fangen wir also an:

Als erstes laden wir PicoPNG runter. Zu finden ist das ganze unter:
http://members.gamedev.net/lode/projects/LodePNG/

Nun fügen wir unserem Projekt die picopng.cpp unserem Projekt hinzu. Die Mainfunktion, die sich ganz am Ende der Datei befindet, wird gelöscht oder auskommentiert, sie dient nur als Beispiel. Zusätzlich sollte man noch eine picopng.h anlegen, in der man den Prototypen der decodePNG schreibt.
Nun benutzen wir decodePNG um eine OpenGL Textur zu erstellen:

C-/C++-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
GLuint LoadPNG(std::string Filename)
{
    std::ifstream File;
    File.open(Filename.c_str(), std::ios::in | std::ios::binary);

    File.seekg(0, std::ios::end);//zum ende der datei springen

    int FileLength=File.tellg();
    File.seekg(0, std::ios::beg);

    std::vector<unsigned char> Buffer, Image;
    Buffer.resize(FileLength);

    File.read((char*)(&Buffer[0]), FileLength);

    File.close();

    unsigned long XSize=0, YSize=0;

    decodePNG(Image, XSize, YSize, &Buffer[0], (unsigned long)Buffer.size());

    //jetzt die Textur erstellen


    GLuint NewTexture;
    glGenTextures(1, &NewTexture);
    glBindTexture(GL_TEXTURE_2D, NewTexture);
    glTexImage2D(GL_TEXTURE_2D, 0, 4, XSize, YSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, &Image[0]);

    return NewTexture;
}


Und das wars auch schon :D
Wie man sieht, wird keinerlei Fehlerkontrolle vorgenommen, das sollte natürlich nicht so bleiben. So und jetzt noch ein paar Erläuterungen, was dieser Code den so macht:
Zuerst wird die Bilddatei geöffnet, und zwar im Binarymode. Das ist wichtig, da es sonst zu kleinen, aber schwerwiegenden Fehlern kommen kann. Dann springen wir zum Ende der Datei (seekg) hohlen uns die Adresse des Lesezeigers (tellg) und speichern diese. Den genau das ist ja unsere Dateigröße. Anschließend springen wir wieder zurück zum Anfang. Nun werden 2 Buffer angelegt, einen für die Rohdaten, und einen für das decodierte Bild. Wir lesen also die gesamte Datei (mit read) in den Buffer, und schließen sie anschließend wieder.
Die decodePNG erwartet 3 Referenzen, nämlich auf den Vector für das fertige Bild, und auf 2 Variablen, in denen später die Bildgröße stehen wird. Das Format ist beim laden immer RGBA, also muss uns das decodePNG nicht mehr mitteilen. Wir erstellen also die 2 Variablen um die Größe zu speichern, und rufen decodePNG auf. Der vierte Parameter ist der Zeiger auf die Rohdaten, der fünfte die Größe.
Jetzt haben wir also die fertige PNG Datei in unserem Speicher und können damit machen, was wir wollen. Ich habe hier mal eine einfache OpenGL Textur erstellt, aber mit wenig mehr Aufwand ließe sich auch eine D3D Textur oder ein Ddraw Surface erzeugen. Daher gehe ich hier auch nicht näher auf die erstellung der OpenGL Textur ein, sämtliche Funktionen sind ja im Internet ausreichend dokumentiert.

So und nun viel Spaß beim Laden von PNG Dateien!

PS: Ich weiß selber, dass es keine tolle Leistung ist, ein Tutorial über eine einzige Funktion zu schreiben. Ich hoffe lediglich ein paar Leuten zeigen zu können, wie schnell und einfach mal PNG-Dateien laden kann, ohne gleich Tonnen an fremden Code mitschleppen zu müssen.
Lieber dumm fragen, als dumm bleiben!

2

02.05.2008, 15:58

Hallo

Irgendwie funktioniert das nicht bei mir.
Um die datei zu laden, benutz ich die funktion, die bei picopng mit dabei ist.
Und decodePNG is sowieso klar. Bis dahin scheint auch alles glad zu laufen.
Wenn Ich aus den daten dann allerdings eine Texture in OpenGL mache und sie dann über ein Quadrat lege, bleibt der der Bildschirm weiß. Die Texture erstell ich genauso wie im Tutorial.

kann mir da jemand helfen?

3

02.05.2008, 16:32

Hast du Texturen aktiviert? Wurde die Textur korrekt geladen?
Lieber dumm fragen, als dumm bleiben!

4

02.05.2008, 16:41

Zitat von »"Jonathan_Klein"«

Hast du Texturen aktiviert? Wurde die Textur korrekt geladen?


Jo, Textren sind Aktiviert. Wenn ich zur laufzeit die Variablen überprüfe, wird mir auch gültige Werte angezeigt, also das bild wird als korrekt geladen.

Ich denke, das hier irgendwas falsch läuft:

C-/C++-Quelltext

1
2
3
4
GLuint NewTexture;
glGenTextures(1, &NewTexture);
glBindTexture(GL_TEXTURE_2D, NewTexture);
glTexImage2D(GL_TEXTURE_2D, 0, 4, XSize, YSize, 0, GL_RGBA, GL_UNSIGNED_BYTE, &Image[0]);


die größe der Texture ist 256x256

David Scherfgen

Administrator

Beiträge: 10 382

Wohnort: Hildesheim

Beruf: Wissenschaftlicher Mitarbeiter

  • Private Nachricht senden

5

02.05.2008, 17:05

Mach noch Folgendes (nach deinem Code):

C-/C++-Quelltext

1
2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

6

02.05.2008, 17:09

Zitat von »"David Scherfgen"«

Mach noch Folgendes:

C-/C++-Quelltext

1
2
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);


Es klappt. Vielen Dank!! ^_^
War schon total verzweifelt...

7

25.08.2009, 13:07

Hallo Freunde,

echt saubere Sache dieser Loader, leider steht das Bild auf dem Kopf, wenn ich es als Texture lade.

Vielleicht weiß ja jemand zufällig warum :badgrin:

Hier ein Teil vom Code:

gluOrtho2D(0, 512, 0, 512);
.............

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

glBindTexture(GL_TEXTURE_2D, texture);
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);glVertex2f(200,200);
glTexCoord2f(1.0f, 0.0f);glVertex2f(328, 200);
glTexCoord2f(1.0f, 1.0f);glVertex2f(328, 328);
glTexCoord2f(0.0f, 1.0f);glVertex2f(200, 328);
glEnd();

Vielen Dank schonmal :-)

xardias

Community-Fossil

Beiträge: 2 731

Wohnort: Santa Clara, CA

Beruf: Software Engineer

  • Private Nachricht senden

8

25.08.2009, 13:53

Mal die Texturkoordinaten vertauscht?

also:

C-/C++-Quelltext

1
2
3
4
glTexCoord2f(0.0f, 1.0f);glVertex2f(200,200); 
glTexCoord2f(1.0f, 1.0f);glVertex2f(328, 200); 
glTexCoord2f(1.0f, 0.0f);glVertex2f(328, 328); 
glTexCoord2f(0.0f, 0.0f);glVertex2f(200, 328); 

Viktor

Alter Hase

Beiträge: 533

Wohnort: Ludwigshafen

Beruf: Student

  • Private Nachricht senden

9

25.08.2009, 13:54

pngs werden doch über-kopf gespeichert oder zumindest kann man das glaube ich beim speichern angeben. les doch einfach rückwärts aus :D
oder dreh die textur, bevor du sie in deine variable speicherst, da wird es sicher eine funktion in ogl geben...
oder machs so wie oben...mal wieder nur ne sekunden zu spät...

10

25.08.2009, 14:07

Ich bin beeindruckt wie schnell ich hier eine Antwort bekam. Vielen Dank.

Werds dann so machen, hatte schon gedacht ich hätte einen Fehler gemacht. :-)

Werbeanzeige