[OpenGL] Problem mit Texturen
-
Hallo
Nach langer Zeit wollte ich nochmal etwas mit OpenGL machen... und bin beim Laden von Texturen auf ein Problem gestoßen. Zum Verwalten von Texturen habe ich die folgende Klasse geschrieben:
class Texture { private: Texture (const GLuint nTextureID) : m_nTextureID (nTextureID) { } public: ~Texture () { glDeleteTextures (1, &m_nTextureID); } public: static Texture* loadFromBMP (const std::string& sFile); GLuint getID () const { return m_nTextureID; } void setActive () const { glBindTexture (GL_TEXTURE_2D, m_nTextureID); } private: const GLuint m_nTextureID; };
Die Funktion zum Laden der Textur ist wie folgt definiert:
Texture* Texture::loadFromBMP (const std::string& sFile) { std::ifstream fIn (sFile.c_str (), std::ios::in | std::ios::binary); if (fIn.good ()) { try { // read header unsigned short nFormat; fIn.read (reinterpret_cast <char*> (&nFormat), 2); if (nFormat != 19778) { std::cerr << "(EE) Failed to read: " << sFile << std::endl; std::cerr << "(EE) Not a Windows Bitmap." << std::endl; std::cerr << std::endl; fIn.close (); return NULL; } fIn.seekg (18); signed int nWidth, nHeight; fIn.read (reinterpret_cast <char*> (&nWidth), 4); fIn.read (reinterpret_cast <char*> (&nHeight), 4); // compute body size const unsigned int nBodySize = 3 * nWidth * nHeight; // read planes unsigned short nPlanes; fIn.read (reinterpret_cast <char*> (&nPlanes), 2); // read bits per pixel unsigned short nBPP; fIn.read (reinterpret_cast <char*> (&nBPP), 2); // ensure format is supported if (nPlanes != 1 || nBPP != 24) { std::cerr << "(EE) Failed to read: " << sFile << std::endl; std::cerr << "(EE) Unsupported format: " << nBPP << " bpp, " << nPlanes << " plane(s)" << std::endl; std::cerr << "(EE) Only bitmaps with 24 color depth and 1 plane are supported." << std::endl; std::cerr << std::endl; fIn.close (); return NULL; } // seek past the rest of the header fIn.seekg (54); // read body data char* const pcData = new char [nBodySize]; fIn.read (pcData, nBodySize); // we are done reading the file fIn.close (); // reverse byte order (convert 'BGR' to 'RGB') for (unsigned int j = 0; j < nBodySize ; j += 3) { pcData [j] ^= pcData [j + 2]; pcData [j + 2] ^= pcData [j]; } // now we need to create the texture GLuint nTextureID; glGenTextures (1, &nTextureID); glBindTexture (GL_TEXTURE_2D, nTextureID); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri (GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // 2D texture, level of detail 0 (normal), 3 components (red, green, blue), x size, y size, // border 0 (normal), rgb color data, unsigned byte data, and finally the data itself glTexImage2D (GL_TEXTURE_2D, 0, 3, nWidth, nHeight, 0, GL_RGB, GL_UNSIGNED_BYTE, pcData); delete [] pcData; // finish return new Texture (nTextureID); } catch (...) { fIn.close (); std::cerr << "(EE) Failed to read: " << sFile << std::endl; std::cerr << "(EE) Unknown error." << std::endl; std::cerr << std::endl; return NULL; } } else { fIn.close (); std::cerr << "(EE) Failed to read: " << sFile << std::endl; std::cerr << "(EE) Either file not found or not allowed to open for reading." << std::endl; std::cerr << std::endl; return NULL; } }
Nun rufe ich bei der Initialisierung des Programms auf:
static Texture* s_aTextures []; // Member einer Klasse C // [...] Texture* C::s_aTextures [] = { NULL, NULL, NULL, NULL, NULL, NULL }; // [...] s_aTextures [0] = Texture::loadFromBMP ("test1.bmp"); s_aTextures [1] = Texture::loadFromBMP ("test2.bmp"); s_aTextures [2] = Texture::loadFromBMP ("test3.bmp"); s_aTextures [3] = Texture::loadFromBMP ("test4.bmp"); s_aTextures [4] = Texture::loadFromBMP ("test5.bmp"); s_aTextures [5] = Texture::loadFromBMP ("test6.bmp");
Beim Vor-Rendern der Szene in eine Display-List rufe ich dann auf unterschiedlichen Elementen des Arrays auf:
s_aTextures [2] -> setActive ();
Nun das Problem: Alles, was ich rendere, wird mit derselben Textur belegt, obwohl ich unterschiedliche Texturen einstelle. Um genau zu sein, wird alles mit der Textur überzogen, die ich als letztes erstelle (lade) - Nicht missverstehen, es wird nicht immer die Textur benutzt, auf der ich zuletzt setActive aufrufe. Aber ich finde den Fehler einfach nich. Die lokale Variable nTextureID in Texture::loadFromBMP hat auch einen korrekten Wert, nämlich bei jedem Aufruf einen anderen. Jemand eine Idee?
Danke im Voraus
-
Hast Du schonmal mit glGetError() gearbeitet? Könnte ja zum Beispiel beim binden der Textur was schief gehen.
Mr Train
-
Wie alt is deine GraKa/dein Treiber? Sind alle Texturen von der Weite/Höhe 2^n?
Du könntest auch nach dem Nutzen der Textur immer erstmal wieder auf die 0 binden, sodass keine Textur gewählt ist, einfach um evtl der Fehlerquelle näher zu kommen
-
Machst du das binden ausserhalb von glBegin(GL_...)?
-
Hi,
meiner Meinung nach liegt es hier dran:
(Irgendwie habe ich das Gefühl das da was mit den Zeigern nicht stimmt.
Weil du zwar mit Zeigern hantierst aber nirgends ein new für ein Objekt der Klasse zu sehen ist)static Texture* s_aTextures []; // Member einer Klasse C // [...] Texture* C::s_aTextures [] = { NULL, NULL, NULL, NULL, NULL, NULL }; // [...] s_aTextures [0] = Texture::loadFromBMP ("test1.bmp"); s_aTextures [1] = Texture::loadFromBMP ("test2.bmp"); s_aTextures [2] = Texture::loadFromBMP ("test3.bmp"); s_aTextures [3] = Texture::loadFromBMP ("test4.bmp"); s_aTextures [4] = Texture::loadFromBMP ("test5.bmp"); s_aTextures [5] = Texture::loadFromBMP ("test6.bmp");
Schmeiß mal alles was mit Zeigern ist raus:
Überall
class Texture { private: GLuint textur; public: ~Texture () { } public: ladenTest(const std::string& sFile); { textur=loadFromBMP ( sFile); } GluInt loadFromBMP (const std::string& sFile); GLuint getID () const { return textur; } void setActive () const { glBindTexture (GL_TEXTURE_2D,textur); } };
Texture test[6]; test[0].ladenTest ("test1.bmp"); test[1].ladenTest ("test2.bmp"); test[2].ladenTest ("test3.bmp"); test[3].ladenTest ("test4.bmp"); test[4].ladenTest("test5.bmp"); test[5].ladenTest("test6.bmp");
Dann beim Rendern:
test[0].setActive; //je nachdem welche du brauchst
Hinweis: Gegebenenfalls "Texture::" hinzufügen wo es der Compiler vermißt.
Fall es doch am OpenGL Teil liegt: Hier eine schnell zusammenkopierte funktionierende Klasse:
Textur.h#include <windows.h> // Header File For Windows #include <gl\glu.h> // Header File For The GLu32 Library #include <TCHAR.h> #include <math.h> // Header File For The Math Library (Used In BuildTexture) #include <fstream> using namespace std; #define GL_CLAMP_TO_EDGE 0x812F #pragma once class Textur { private: GLuint textur; bool geladen; int glTexParameterfv_param; bool LoadBitmap(wchar_t *filename); bool LoadTGA(wchar_t *filename); public: Textur (); Textur (GLuint glTexParameterfv_param_); bool LoadGLTextures(wchar_t *filename); bool get_geladen(); int get_texture(); };
Textur.cpp:
#include "Textur.h" Textur::Textur ():geladen(false),glTexParameterfv_param(GL_CLAMP_TO_EDGE){} Textur::Textur (GLuint glTexParameterfv_param_):geladen(false),glTexParameterfv_param(glTexParameterfv_param_) {} bool Textur::LoadGLTextures(wchar_t *filename) // Load Bitmaps And Convert To Textures { if (LoadBitmap(filename)) // versucht das Bild im angegeben Pfad als *.bmp zu laden { geladen=true; return true; } if (LoadTGA(filename)) // versucht as Bild im angegeben Pfad als *.tga zu laden { geladen=true; return true; } return false; } bool Textur::LoadBitmap(wchar_t *filename) // Load Bitmaps And Convert To Textures { HBITMAP hBMP; // Handle Of The Bitmap BITMAP BMP; // Bitmap Structure glGenTextures(1, &textur); // Create The Texture hBMP=(HBITMAP)LoadImage(GetModuleHandle(NULL), filename, IMAGE_BITMAP, 0, 0, LR_CREATEDIBSECTION | LR_LOADFROMFILE ); if (!hBMP) // Does The Bitmap Exist? { return false; // If Not Return False } GetObject(hBMP, sizeof(BMP), &BMP); // Get The Object // hBMP: Handle To Graphics Object // sizeof(BMP): Size Of Buffer For Object Information // &BMP: Buffer For Object Information glPixelStorei(GL_UNPACK_ALIGNMENT, 4); // Pixel Storage Mode (Word Alignment / 4 Bytes) glBindTexture(GL_TEXTURE_2D, textur); glTexImage2D(GL_TEXTURE_2D, 0, 3, BMP.bmWidth, BMP.bmHeight, 0, GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, glTexParameterfv_param ); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, glTexParameterfv_param ); //Mipmapping verhindert ein Rauschen bei entfernten Texturen glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_LINEAR ); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); gluBuild2DMipmaps( GL_TEXTURE_2D, 3, BMP.bmWidth, BMP.bmHeight ,GL_BGR_EXT, GL_UNSIGNED_BYTE, BMP.bmBits); DeleteObject(hBMP); // Delete The Object return true; } bool Textur::LoadTGA(wchar_t *filename) // Loads A TGA File Into Memory { GLubyte *imageData; // Image Data (Up To 32 Bits) GLuint bpp; // Image Color Depth In Bits Per Pixel. GLuint width; // Image Width GLuint height; // Image Height GLubyte TGAheader[12]={0,0,2,0,0,0,0,0,0,0,0,0}; // Uncompressed TGA Header GLubyte TGAcompare[12]; // Used To Compare TGA Header GLubyte header[6]; // First 6 Useful Bytes From The Header GLuint bytesPerPixel; // Holds Number Of Bytes Per Pixel Used In The TGA File GLuint imageSize; // Used To Store The Image Size When Setting Aside Ram GLuint temp; // Temporary Variable GLuint type=GL_RGBA; // Set The Default GL Mode To RBGA (32 BPP) FILE *file; _wfopen_s(&file,filename, L"rb"); // Open The TGA File if( file==NULL || // Does File Even Exist? fread(TGAcompare,1,sizeof(TGAcompare),file)!=sizeof(TGAcompare) || // Are There 12 Bytes To Read? memcmp(TGAheader,TGAcompare,sizeof(TGAheader))!=0 || // Does The Header Match What We Want? fread(header,1,sizeof(header),file)!=sizeof(header)) // If So Read Next 6 Header Bytes { if (file == NULL) // Did The File Even Exist? *Added Jim Strong* return false; // Return False else // Otherwise { fclose(file); // If Anything Failed, Close The File return false; // Return False } } width = header[1] * 256 + header[0]; // Determine The TGA Width (highbyte*256+lowbyte) height = header[3] * 256 + header[2]; // Determine The TGA Height (highbyte*256+lowbyte) if( width <=0 || // Is The Width Less Than Or Equal To Zero height <=0 || // Is The Height Less Than Or Equal To Zero (header[4]!=24 && header[4]!=32)) // Is The TGA 24 or 32 Bit? { fclose(file); // If Anything Failed, Close The File return false; // Return False } bpp = header[4]; // Grab The TGA's Bits Per Pixel (24 or 32) bytesPerPixel = bpp/8; // Divide By 8 To Get The Bytes Per Pixel imageSize = width*height*bytesPerPixel; // Calculate The Memory Required For The TGA Data imageData=(GLubyte *)malloc(imageSize); // Reserve Memory To Hold The TGA Data if( imageData==NULL || // Does The Storage Memory Exist? fread(imageData, 1, imageSize, file)!=imageSize) // Does The Image Size Match The Memory Reserved? { if(imageData!=NULL) // Was Image Data Loaded free(imageData); // If So, Release The Image Data fclose(file); // Close The File return false; // Return False } for(GLuint i=0; i<int(imageSize); i+=bytesPerPixel) // Loop Through The Image Data { // Swaps The 1st And 3rd Bytes ('R'ed and 'B'lue) temp=imageData[i]; // Temporarily Store The Value At Image Data 'i' imageData[i] = imageData[i + 2]; // Set The 1st Byte To The Value Of The 3rd Byte imageData[i + 2] = temp; // Set The 3rd Byte To The Value In 'temp' (1st Byte Value) } fclose (file); // Close The File // Build A Texture From The Data glGenTextures(1, &textur); // Generate OpenGL texture IDs glBindTexture(GL_TEXTURE_2D, textur); // Bind Our Texture glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); // Linear Filtered glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // Linear Filtered if (bpp==24) // Was The TGA 24 Bits { type=GL_RGB; // If So Set The 'type' To GL_RGB } glTexImage2D(GL_TEXTURE_2D, 0, type, width, height, 0, type, GL_UNSIGNED_BYTE,imageData ); return true; // Texture Building Went Ok, Return True } bool Textur::get_geladen() { return geladen; } int Textur::get_texture() { if (geladen) { return textur; } return NULL; }
Quellen von LoadBitmap(...) und LoadTGA(...)
http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=06
http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=24
-
Sein new steht so da:
return new Texture (nTextureID);
in Zeile 77.