目前工作僅需要將BMP圖片顯示在GL Scene裡面,所以就僅擷取出貼圖片材質的部分。並附上原網頁的註解,已方便對照跟查詢。由於是原文貼上,所以有些用語會有有前後不接的狀況,所以想瞭解原始的意義,請至Hehe的網頁觀看。
0.在開始進行前,先建立存放Texture的全域變數。
....
GLuint m_glTextureList[DS_GL_MAX_TEXTURE];
....
1.建立讀取BMP檔的函式。由於此函式有用到auxDIBImageLoad,所以要在*.h引用gl\glaux.h。
CDSFileIO為自訂的類別,目的是要檢查檔案是否存在,不存在的話,讀了也沒有意義。原文有提到圖片大小的建議,目前因為要配合TreeList顯示的問題,所以是讀取16*16的大小的圖片,於Windows XP SP2測試是正常。
//----------- L.6 ----------------
//Now immediately after the above code, and before ReSizeGLScene(), we want to add the following section of code.
// The job of this code is to load in a bitmap file. If the file doesn't exist NULL is sent back meaning the texture couldn't be loaded.
// Before I start explaining the code there are a few VERY important things you need to know about the images you plan to use as textures.
// The image height and width MUST be a power of 2.
// The width and height must be at least 64 pixels, and for compatability reasons, shouldn't be more than 256 pixels.
// If the image you want to use is not 64, 128 or 256 pixels on the width or height, resize it in an art program.
// There are ways around this limitation, but for now we'll just stick to standard texture sizes.
//First thing we do is create a file handle. A handle is a value used to identify a resource so that our program can access it.
// We set the handle to NULL to start off.
//----------- L.6 ----------------
AUX_RGBImageRec *COpenGLDrawer::LoadBMP(const char *szFilePath)
{
if ( CDSFileIO::IsFileExisted(szFilePath) ) {
return auxDIBImageLoad(szFilePath); // Load The Bitmap And Return A Pointer
} //End of if ( CDSFileIO::IsFileExisted(szFilePath) )
return NULL; // If Load Failed Return NULL
}
2.建立設定Texture的函式。DS_GL_MAX_TEXTURE為自訂常數,用來定義讀取BMP的Texture buffer大小。所以就看需求定義即可。由於是非動態宣告buffer大小,所以要小心邊界的問題。
//The follwing code has also changed very little from the code used in previous tutorials.
// If you're not sure what each of the following lines do, go back and review.
//Note that TextureImage[ ] is going to hold 2 rgb image records.
// It's very important to double check code that deals with loading or storing our textures.
// One wrong number could result in a memory leak or crash!
// Load Bitmaps And Convert To Textures
int COpenGLDrawer::LoadGLTextures(std::vector<std::string> vstrFilePaths)
{
int Status = TRUE;
AUX_RGBImageRec *TextureImage[DS_GL_MAX_TEXTURE]; // Create Storage Space For The Textures
//The next line is the most important line to watch. If you were to replace the 2 with any other number, major problems will happen.
// Double check! This number should match the number you used when you set up TextureImages[ ].
//The two textures we're going to load are font.bmp (our font), and bumps.bmp.
// The second texture can be replaced with any texture you want.
// I wasn't feeling very creative, so the texture I decided to use may be a little drab.
/* memset(TextureImage,0,sizeof(void *)*2); // Set The Pointer To NULL */
memset(TextureImage,0,sizeof(void *)*DS_GL_MAX_TEXTURE); // Set The Pointer To NULL
for ( unsigned int i = 0 ; i < vstrFilePaths.size() ; i++ )
{
if ( !(TextureImage[i] = this->LoadBMP(vstrFilePaths[i].c_str())) ) //有一個讀不到就不繼續執行,不然就是要改塞白色。
{
Status = FALSE;
}
} //End of for ( unsigned int i = 0 ; i < vstrFilePaths.size() ; i++ )
if ( !Status ) {
return Status; };
//Another important line to double check.
// I can't begin to tell you how many emails I've received from people asking "why am I only seeing one texture,
// or why are my textures all white!?!".
// Usually this line is the problem.
// If you were to replace the 2 with a 1, only one texture would be created and the second texture would appear all white.
// If you replaced the 2 with a 3 you're program may crash!
//You should only have to call glGenTextures() once. After glGenTextures() you should generate all your textures.
// I've seen people put a glGenTextures() line before each texture they create.
// Usually they causes the new texture to overwrite any textures you've already created.
// It's a good idea to decide how many textures you need to build, call glGenTextures() once, and then build all the textures.
// It's not wise to put glGenTextures() inside a loop unless you have a reason to.
//--------- L.6 ------------
//Now that we've loaded the image data into TextureImage[0], we will build a texture using this data.
// The first line glGenTextures(1, &texture[0]) tells OpenGL we want to generate one texture name
// (increase the number if you load more than one texture).
// Remember at the very beginning of this tutorial we created room for one texture with the line GLuint texture[1].
// Although you'd think the first texture would be stored at &texture[1] instead of &texture[0], it's not.
// The first actual storage area is 0.
// If we wanted two textures we would use GLuint texture[2] and the second texture would be stored at texture[1].
//The second line glBindTexture(GL_TEXTURE_2D, texture[0]) tells OpenGL to bind the named texture texture[0] to a texture target.
// 2D textures have both height (on the Y axes) and width (on the X axes).
// The main function of glBindTexture is to assign a texture name to texture data.
// In this case we're telling OpenGL there is memory available at &texture[0].
// When we create the texture, it will be stored in the memory that &texture[0] references.
//--------- L.6 ------------
glGenTextures((GLsizei)DS_GL_MAX_TEXTURE, &this->m_glTextureList[0]); //Create n Texture
for ( unsigned int i = 0 ; i < this->m_vstrTextureFilePath.size() ; i++ ) // Loop Through All The Textures
{
//Build All The Textures
glBindTexture(GL_TEXTURE_2D, this->m_glTextureList[i]);
//----------- L.6 ---------------
//The next two lines tell OpenGL what type of filtering to use when the image is larger (GL_TEXTURE_MAG_FILTER) or
// stretched on the screen than the original texture, or when it's smaller (GL_TEXTURE_MIN_FILTER) on the screen than the actual texture.
// I usually use GL_LINEAR for both. This makes the texture look smooth way in the distance, and when it's up close to the screen.
// Using GL_LINEAR requires alot of work from the processor/video card, so if your system is slow, you might want to use GL_NEAREST.
// A texture that's filtered with GL_NEAREST will appear blocky when it's stretched. You can also try a combination of both.
// Make it filter things up close, but not things in the distance.
//----------- L.6 ---------------
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//----------- L.6 ---------------
//Next we create the actual texture. The following line tells OpenGL the texture will be a 2D texture (GL_TEXTURE_2D).
// Zero represents the images level of detail, this is usually left at zero. Three is the number of data components.
// Because the image is made up of red data, green data and blue data, there are three components.
// TextureImage[0]->sizeX is the width of the texture. If you know the width, you can put it here,
// but it's easier to let the computer figure it out for you.
// TextureImage[0]->sizey is the height of the texture. zero is the border.
// It's usually left at zero. GL_RGB tells OpenGL the image data we are using is made up of red, green and blue data in that order.
// GL_UNSIGNED_BYTE means the data that makes up the image is made up of unsigned bytes, and finally...
// TextureImage[0]->data tells OpenGL where to get the texture data from.
// In this case it points to the data stored in the TextureImage[0] record.
//----------- L.6 ---------------
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[i]->sizeX, TextureImage[i]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[i]->data);
} //End of for ( int i = 0 ; i < DS_GL_MAX_TEXTURE ; i++ )
/* } */
//The following lines of code check to see if the bitmap data we loaded to build our textures is using up ram.
// If it is, the ram is freed. Notice we check and free both rgb image records.
// If we used 3 different images to build our textures, we'd check and free 3 rgb image records.
//---------- L.6 ---------------
//Now we free up any ram that we may have used to store the bitmap data. We check to see if the bitmap data was stored in TextureImage[0].
// If it was we check to see if the data has been stored. If data was stored, we erase it.
// Then we free the image structure making sure any used memory is freed up.
//---------- L.6 ---------------
for ( int i = 0 ; i < this->m_vstrTextureFilePath.size() ; i++ )
{
if ( TextureImage[i] ) // If Texture Exists
{
if ( TextureImage[i]->data ) { //If Texture Image Exists
free(TextureImage[i]->data); }; //Free the Texture Image Memory
free(TextureImage[i]); //Free The Image Structure
} //End of if ( TextureImage[i] )
} //End of for ( int i = 0 ; i < DS_GL_MAX_TEXTURE ; i++ )
return Status; // Return The Status
}
3.上述兩個步驟完成後,則在GL做初始化的時候,載入並設定Texture。這邊所需的圖檔路徑皆在物件產生時就先指定好。
void COpenGLDrawer::glInitialize(void)
{
....
if ( !this->LoadGLTextures(this->m_vstrTextureFilePath) ) {
return; };
....
}
4.於GL Scene繪製方式如下,關鍵的部分為,要進行Texture Mapping時,要先開啟GL_TEXTURE。繪製完成後,可設定關閉,以釋放記憶體。
void CDSGLDrawer::glDrawScene(void)
{
//Show Texture
//The next line of code selects which texture we want to use.
// If there was more than one texture you wanted to use in your scene,
// you would select the texture using glBindTexture(GL_TEXTURE_2D, texture[number of texture to use]).
// If you wanted to change textures, you would bind to the new texture.
// One thing to note is that you can NOT bind a texture inside glBegin() and glEnd(), you have to do it before or after glBegin().
// Notice how we use glBindTextures to specify which texture to create and to select a specific texture.
glBindTexture(GL_TEXTURE_2D, this->m_glTextureList[0]); // Select Our Texture
//To properly map a texture onto a quad, you have to make sure the top right of the texture is mapped to the top right of the quad.
// The top left of the texture is mapped to the top left of the quad,
// the bottom right of the texture is mapped to the bottom right of the quad, and finally,
// the bottom left of the texture is mapped to the bottom left of the quad.
// If the corners of the texture do not match the same corners of the quad, the image may appear upside down, sideways, or not at all.
//The first value of glTexCoord2f is the X coordinate.
// 0.0f is the left side of the texture. 0.5f is the middle of the texture, and 1.0f is the right side of the texture.
// The second value of glTexCoord2f is the Y coordinate. 0.0f is the bottom of the texture.
// 0.5f is the middle of the texture, and 1.0f is the top of the texture.
//So now we know the top left coordinate of a texture is 0.0f on X and 1.0f on Y, and the top left vertex of a quad is -1.0f on X,
// and 1.0f on Y.
// Now all you have to do is match the other three texture coordinates up with the remaining three corners of the quad.
//Try playing around with the x and y values of glTexCoord2f.
// Changing 1.0f to 0.5f will only draw the left half of a texture from 0.0f (left) to 0.5f (middle of the texture).
// Changing 0.0f to 0.5f will only draw the right half of a texture from 0.5f (middle) to 1.0f (right).
//--------------- L.6 -------------------
//If everything went OK, and the texture was created, we enable 2D texture mapping.
// If you forget to enable texture mapping your object will usually appear solid white, which is definitely not good.
//--------------- L.6 -------------------
glEnable(GL_TEXTURE_2D); // Enable Texture Mapping
glPolygonMode(GL_FRONT, GL_FILL); //Set polygon rasterization mode
glColor3f(1.0f, 1.0f, 1.0f); //用來重設顏色,如果前面這個位置有被繪製圖形,就必須做此動作。反之則無所謂。
glBegin(GL_QUADS);
glTexCoord2f(0.0f, 0.0f);
glVertex2f(this->m_vcdsGrid[0].LB.x, this->m_vcdsGrid[0].LB.y); // Bottom Left Of The Texture and Quad
glTexCoord2f(1.0f, 0.0f);
glVertex2f(this->m_vcdsGrid[0].RB.x, this->m_vcdsGrid[0].RB.y); // Bottom Right Of The Texture and Quad
glTexCoord2f(1.0f, 1.0f);
glVertex2f(this->m_vcdsGrid[0].RT.x, this->m_vcdsGrid[0].RT.y); // Top Right Of The Texture and Quad
glTexCoord2f(0.0f, 1.0f);
glVertex2f(this->m_vcdsGrid[0].LT.x, this->m_vcdsGrid[0].LT.y); // Top Left Of The Texture and Quad
glEnd();
glDisable(GL_TEXTURE_2D); //Disable texture mapping
}
這樣就可以把讀進來的圖,貼到GL Scene裡面了。