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);
}
总结
通过以上代码,可以 加载贴图 并将其绘制出来。建议将其封装成接口或类,因为这部分代码很底层,通常不会更改。
