[OpenGL]2D Texture Mapping

Reference:Nehe's OpenGL - Lesson 17

目前工作僅需要將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裡面了。

No comments:

Post a Comment

Build docker image from multiple build contexts

Build docker image from multiple build contexts ...