OpenGL纹理贴图渲染主要包含三步:
(1)在绘制前,加载OpenGL纹理资源
a. 读取bmp宽高值和像素矩阵
b. 调用glGenTextures获取纹理对象ID
c. 调用glBindTextures绑定纹理对象ID,使得后续OpenGL指令使用该ID的纹理
d. 调用glTexImage2D生成纹理
e. 设置一些OpenGL处理纹理的参数
BOOL LoadGLTextures() // 载入位图并转换成纹理{ int Status=FALSE; // 状态指示器 int w = 0; int h = 0; unsigned char* pImage=NULL; if (ReadBMP("Debug/Data/tree.bmp", pImage, w, h)) { Status=TRUE; // 将 Status 设为 TRUE glGenTextures(1, &texture[0]); // 创建纹理 // 使用来自位图数据生成 的典型纹理 glBindTexture(GL_TEXTURE_2D, texture[0]); // 生成纹理 glTexImage2D(GL_TEXTURE_2D, 0, 3, w, h, 0, GL_RGB, GL_UNSIGNED_BYTE, pImage/*TextureImage[0]->data*/); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR); // 线形滤波 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR); // 线形滤波 } if (pImage) free(pImage); return Status; // 返回 Status}
(2)启用纹理映射
glEnable(GL_TEXTURE_2D); // 启用纹理映射
(3)场景绘制
a. 绑定一个纹理对象ID,使得后续OpenGL指令使用该ID的纹理
b. 设置顶点纹理坐标
int DrawGLScene(GLvoid){ glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清除屏幕和深度缓存 glLoadIdentity(); // 重置当前的模型观察矩阵 glBindTexture(GL_TEXTURE_2D, texture[0]); // 选择纹理 glBegin(GL_QUADS); // 前面 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的左上 // 后面 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的左下 // 顶面 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 // 底面 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 // 右面 glTexCoord2f(1.0f, 0.0f); glVertex3f( 1.0f, -1.0f, -1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f( 1.0f, 1.0f, -1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f( 1.0f, 1.0f, 1.0f); // 纹理和四边形的左上 glTexCoord2f(0.0f, 0.0f); glVertex3f( 1.0f, -1.0f, 1.0f); // 纹理和四边形的左下 // 左面 glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); // 纹理和四边形的左下 glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); // 纹理和四边形的右下 glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); // 纹理和四边形的右上 glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); // 纹理和四边形的左上 glEnd(); return TRUE;}
绘制的结果如下图(立方体):
++++
读取BMP像素要注意的是():
(1) 贴图的长宽应相等,且为2的n次方[n>0&&n<10]
(2) 去除BMP文件中每行末尾要求的4字节对齐的无效像素(保持数据紧密)
(3) 将每个像素按RGB顺序存放,而BMP为BGR存放;因此,需要交换R和B的值
代码如下(以24位BMP为例):
bool ReadBMP(CHAR *Filename, unsigned char*& pImage, int& w, int& h){ FILE* pBmpFile = fopen(Filename, "rb"); if (!pBmpFile) return false; BITMAPFILEHEADER filehdr; BITMAPINFOHEADER infohdr; fread(&filehdr, 1, sizeof(filehdr), pBmpFile); fread(&infohdr, 1, sizeof(infohdr), pBmpFile); unsigned tmpImaLength = filehdr.bfSize - filehdr.bfOffBits; unsigned char* pTmpIma = new unsigned char[tmpImaLength]; fseek(pBmpFile, filehdr.bfOffBits, SEEK_SET); fread(pTmpIma, 1, tmpImaLength, pBmpFile); w = infohdr.biWidth; h = infohdr.biHeight; unsigned __int64 imageLength = (unsigned __int64)3*w*h; pImage = new unsigned char[imageLength]; unsigned idx = 0; unsigned bmpIdx = 0; for (long i=0; i0) bmpIdx += (4-lineLeftByte); } delete [] pTmpIma; fclose(pBmpFile); return true;}
为了避免这些麻烦,可以使用OpenGL提供的auxDIBImageLoadA来读BMP图片。
AUX_RGBImageRec *TextureImage[1]; // 创建纹理的存储空间memset(TextureImage,0,sizeof(void *)*1);TextureImage[0] = auxDIBImageLoadA(Filename);
生成纹理之后,记得要把TexureImage[0]->data和TexureImage[0]的堆内存free掉!