OpenGL教程(3):加载贴图
发表于2017-11-25
有了模型还需要贴图,加载贴图的流程大体分为两部分,首先是图片的解码,其次是使用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); }
总结
通过以上代码,可以 加载贴图 并将其绘制出来。建议将其封装成接口或类,因为这部分代码很底层,通常不会更改。