OpenGL(三) 載入貼圖
阿新 • • 發佈:2018-12-30
有了模型還需要貼圖。 載入貼圖 的流程大體分為兩部分,首先是圖片的解碼,其次是使用UV座標與模型對應。本文主要從底層原理和第三方庫兩個方面來介紹 載入貼圖 。
解碼
下面分別介紹硬編碼實現和SOIL庫兩種方式。
硬編碼實現
因為載入不同的型別圖片偏移值不一樣,載入圖片之前要確定圖片型別。另一方面,對於DXT這種壓縮圖片,也需要在壓縮圖的基礎上進行取樣,而不是將其還原回未壓縮的圖元。
static unsigned char* DecodeBMPData(unsigned char* imageData,int&width,int& heigh) { //decode bmp int pixelDataOffset =*((int*)(imageData+10)); width = *((int*)(imageData +18)); heigh = *((int*)(imageData +22)); unsigned char* pixelData = (imageData+pixelDataOffset); for(int i = 0;i<width*heigh*3;i+=3) { //bgr->rgb unsigned char temp = pixelData[i+2]; pixelData[i+2] = pixelData[i]; pixelData[i] = temp; } return pixelData; } const unsigned long FORMATE_DXT1 = 0x31545844l; //DXT1-> 1 T X D static unsigned char* DecodeDXT1Data(unsigned char* imageData,int&width,int& height,int& pixelSize) { height = *(unsigned long*)(imageData+sizeof(unsigned long)*3); width = *(unsigned long*)(imageData+sizeof(unsigned long)*4); pixelSize = *(unsigned long*)(imageData+sizeof(unsigned long)*5); unsigned long compressFormate; compressFormate = *(unsigned long*)(imageData+sizeof(unsigned long)*21); switch (compressFormate) { case FORMATE_DXT1: printf("DXT1\n"); break; default: break; } unsigned char* pixelData = new unsigned char[pixelSize]; memcpy(pixelData,(imageData+sizeof(unsigned long)*32),pixelSize); return pixelData; } GLuint CreateTextureFromFile(const char* imagePath) { unsigned char* imageData =(unsigned char*) LoadFileContent(imagePath); int width = 0; int heigh = 0; //decode bmp unsigned char* pixelData =nullptr; int pixelDataSize = 0; GLenum srcForamte = GL_RGB; if ((*(unsigned short*)imageData) == 0x4D42) { pixelData = DecodeBMPData(imageData,width,heigh); } else if (memcmp(imageData,"DDS ",4)==0) { pixelData = DecodeDXT1Data(imageData,width,heigh,pixelDataSize); srcForamte = GL_COMPRESSED_RGBA_S3TC_DXT1_EXT; } if (pixelData == nullptr) { printf("cannot decode %s \n",imagePath); delete imageData; return 0; } GLuint texture; glGenTextures(1,&texture); glBindTexture(GL_TEXTURE_2D,texture); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); if(srcForamte == GL_RGB){ glTexImage2D(GL_TEXTURE_2D,0,GL_RGB,width,heigh,0,GL_RGB,GL_UNSIGNED_BYTE,pixelData); } else if (srcForamte == GL_COMPRESSED_RGBA_S3TC_DXT1_EXT) { glCompressedTexImage2D(GL_TEXTURE_2D,0,srcForamte,width,heigh,0,pixelDataSize,pixelData); } glBindBuffer(GL_TEXTURE_2D,0); delete imageData; return texture; }
可以注意到,通過glGenTextures
生成buffer,通過glTexParameteri
設定圖片的顯示引數,取樣引數等。最後通過glTexImage2D
生成圖元。
第三方庫實現
載入貼圖 還可以使用SOIL庫,SOIL是簡易OpenGL影象庫(Simple OpenGL Image Library)的縮寫,它支援大多數流行的影象格式,使用起來也很簡單,你可以從它的主頁下載。如果使用SOIL庫載入,程式碼封裝如下:
GLuint CreateTextureFromFile(const char* imagePath) { GLuint texture; glGenTextures(1, &texture); glBindTexture(GL_TEXTURE_2D, texture); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); int width, height; unsigned char* image = SOIL_load_image(imagePath, &width, &height, 0, SOIL_LOAD_RGB); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, image); glGenerateMipmap(GL_TEXTURE_2D); SOIL_free_image_data(image); glBindTexture(GL_TEXTURE_2D, 0); return texture; }
使用貼圖
通過glActiveTexture
可以開啟圖元位置。
TextureLocation = glGetUniformLocation(s_program,"U_MainTexture");
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D,mainTexture);
glUniform1i(TextureLocation,0);
在OpenGL中圖元可以直接被指定,而無需glUniform
為其賦值,它可以從0到16,分別傳入顯示卡中,預設0對應第一張圖元的位置。當然也可以通過使用glUniform1i
,給紋理取樣器分配一個位置值,這樣可以實現在一個片段著色器中設定多個 載入貼圖 。
預設情況下GL_TEXTURE0是被啟用的。因此在單圖元的情況下,可以只寫如下程式碼進行渲染。
glBindTexture(GL_TEXTURE_2D,mainTexture);
shader繪製
在編寫shader時,需要在vs中加入texcoord,並傳遞給fs。fs方面要加入sampler2D來接收紋理。
//vs
attribute vec3 pos;
attribute vec2 texcoord;
attribute vec3 normal;
uniform mat4 M;
uniform mat4 V;
uniform mat4 P;
varying vec2 V_Texcoord;
void main()
{
V_Texcoord = texcoord;
gl_Position=P*V*M*vec4(pos,1.0);
}
//fs
uniform sampler2D U_MainTexture;
varying vec2 V_Texcoord;
void main()
{
gl_FragColor= texture2D(U_MainTexture,V_Texcoord);
}
總結
通過以上程式碼,可以 載入貼圖 並將其繪製出來。建議將其封裝成介面或類,因為這部分程式碼很底層,通常不會更改。
關注我的微信公眾號,獲取更多優質內容