标签:
在二维游戏中,我们几乎绕不开精灵绘制这一过程,除了直接在opengl读入图像并绘制外,我们更常使用纹理来完成这一过程,把纹理贴到在xy平面上的面片,做出二维游戏的效果。
这样我们可以很方便的使用opengl提供给我们的一些方法来执行精灵的变换,而不是使用大量的贴图来手工完成变换过程;同时,还可以通过调节深度信息来确定物体的遮挡关系,而不用花心思考虑绘制的先后顺序,因为我们知道,在二维世界里,谁遮挡谁是和绘制顺序相关的。
但是,我们也发现,我们的精灵不总是四四方方的,所以纹理加载进来后会有背景色,我们希望去除这个背景色,一个非常好的想法就是使用带透明(alpha)通道的图片,可是非常遗憾的是,opengl不能直接支持这种格式,所以这个方法不可行。
还有一种比较常见的想法是这样的:我们使用一种颜色代表背景色,在opengl中开启alpha测试,把纹理图片设置为rgba格式,读取到这种颜色后,我们把alpha手动设为0,也就是全透明;否则设为1,也就是不透明。我们可以意识到:这个背景色的选取是有要求的,它不能与图片不透明部分rgb相同,否则它们会被错误识别为透明的。
以下是我最近正在写的一个基于opengl的小游戏,这里使用了透明纹理,其中火箭是网上暂时找的图片,用于测试效果,下面的飞船图片是自己做的。由于火箭没有进行特殊处理,所以我们发现它的中间部分也有被镂空的,这就是本次代码存在的一些缺陷,它需要我们对图片进行特殊的处理才能使用。
上图的代码会在完成后放出,为了演示透明纹理效果,我又单独给出了一个测试代码:
原图(图1来自网络):
使用透明纹理后效果:
在这个代码中,背景色是通过参数指定的。
需要注意的是,在绘制的时候,我们尽可能先绘制一般纹理的面片,然后统一绘制透明纹理的面片,在绘制透明纹理时,需要把深度测试设置为只读模式,最后再修改回原来的状态,防止在进行处理的时候破坏了深度信息。
test.h
#pragma once #define GLUT_DISABLE_ATEXIT_HACK #include "GL/GLUT.H" void loadTex(int i, char *filename, GLuint* texture);//一般纹理 void loadTex(int i, char *filename, GLuint* texture,unsigned char* backgroundColor);//透明纹理
#define _CRT_SECURE_NO_WARNINGS #include<stdio.h> #include<windows.h> #include"test.h" #define BITMAP_ID 0x4D42 //读纹理图片 static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader) { FILE *filePtr; // 文件指针 BITMAPFILEHEADER bitmapFileHeader; // bitmap文件头 unsigned char *bitmapImage; // bitmap图像数据 int imageIdx = 0; // 图像位置索引 unsigned char tempRGB; // 交换变量 // 以“二进制+读”模式打开文件filename filePtr = fopen(filename, "rb"); if (filePtr == NULL) { printf("file not open\n"); return NULL; } // 读入bitmap文件图 fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr); // 验证是否为bitmap文件 if (bitmapFileHeader.bfType != BITMAP_ID) { fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n"); return NULL; } // 读入bitmap信息头 fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); // 将文件指针移至bitmap数据 fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); // 为装载图像数据创建足够的内存 bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage]; // 验证内存是否创建成功 if (!bitmapImage) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } // 读入bitmap图像数据 fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr); // 确认读入成功 if (bitmapImage == NULL) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } //由于bitmap中保存的格式是BGR,下面交换R和B的值,得到RGB格式 for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) { tempRGB = bitmapImage[imageIdx]; bitmapImage[imageIdx] = bitmapImage[imageIdx + 2]; bitmapImage[imageIdx + 2] = tempRGB; } // 关闭bitmap图像文件 fclose(filePtr); return bitmapImage; } //读纹理图片 static unsigned char *LoadBitmapFile(char *filename, BITMAPINFOHEADER *bitmapInfoHeader, unsigned char* backgroundColor) { FILE *filePtr; // 文件指针 BITMAPFILEHEADER bitmapFileHeader; // bitmap文件头 unsigned char *bitmapImage; // bitmap图像数据 int imageIdx = 0; // 图像位置索引 // 以“二进制+读”模式打开文件filename filePtr = fopen(filename, "rb"); if (filePtr == NULL) { printf("file not open\n"); return NULL; } // 读入bitmap文件图 fread(&bitmapFileHeader, sizeof(BITMAPFILEHEADER), 1, filePtr); // 验证是否为bitmap文件 if (bitmapFileHeader.bfType != BITMAP_ID) { fprintf(stderr, "Error in LoadBitmapFile: the file is not a bitmap file\n"); return NULL; } // 读入bitmap信息头 fread(bitmapInfoHeader, sizeof(BITMAPINFOHEADER), 1, filePtr); // 将文件指针移至bitmap数据 fseek(filePtr, bitmapFileHeader.bfOffBits, SEEK_SET); // 为装载图像数据创建足够的内存 bitmapImage = new unsigned char[bitmapInfoHeader->biSizeImage]; // 验证内存是否创建成功 if (!bitmapImage) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } // 读入bitmap图像数据 fread(bitmapImage, 1, bitmapInfoHeader->biSizeImage, filePtr); // 确认读入成功 if (bitmapImage == NULL) { fprintf(stderr, "Error in LoadBitmapFile: memory error\n"); return NULL; } unsigned char* bitmapData; // 纹理数据 bitmapData = new unsigned char[bitmapInfoHeader->biSizeImage / 3 * 4]; int count = 0; //添加alpha通道 for (imageIdx = 0; imageIdx < bitmapInfoHeader->biSizeImage; imageIdx += 3) { bitmapData[count] = bitmapImage[imageIdx + 2]; bitmapData[count + 1] = bitmapImage[imageIdx + 1]; bitmapData[count + 2] = bitmapImage[imageIdx]; if (bitmapData[count] == backgroundColor[0] && bitmapData[count + 1] == backgroundColor[1] && bitmapData[count + 2] == backgroundColor[2]){ bitmapData[count + 3] = 0; } else bitmapData[count + 3] = 255; count += 4; } // 关闭bitmap图像文件 fclose(filePtr); return bitmapData; } //加载纹理的函数 void loadTex(int i, char *filename, GLuint* texture) { BITMAPINFOHEADER bitmapInfoHeader; // bitmap信息头 unsigned char* bitmapData; // 纹理数据 bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader); glBindTexture(GL_TEXTURE_2D, texture[i]); // 指定当前纹理的放大/缩小过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, //mipmap层次(通常为,表示最上层) GL_RGB, //我们希望该纹理有红、绿、蓝数据 bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2 bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2 0, //边框(0=无边框, 1=有边框) GL_RGB, //bitmap数据的格式 GL_UNSIGNED_BYTE, //每个颜色数据的类型 bitmapData); //bitmap数据指针 } //加载纹理的函数 void loadTex(int i, char *filename, GLuint* texture, unsigned char* backgroundColor) { BITMAPINFOHEADER bitmapInfoHeader; // bitmap信息头 unsigned char* bitmapData; // 纹理数据 bitmapData = LoadBitmapFile(filename, &bitmapInfoHeader,backgroundColor); glBindTexture(GL_TEXTURE_2D, texture[i]); // 指定当前纹理的放大/缩小过滤方式 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexImage2D(GL_TEXTURE_2D, 0, //mipmap层次(通常为,表示最上层) GL_RGBA, //我们希望该纹理有红、绿、蓝、alpha数据 bitmapInfoHeader.biWidth, //纹理宽带,必须是n,若有边框+2 bitmapInfoHeader.biHeight, //纹理高度,必须是n,若有边框+2 0, //边框(0=无边框, 1=有边框) GL_RGBA, //bitmap数据的格式 GL_UNSIGNED_BYTE, //每个颜色数据的类型 bitmapData); //bitmap数据指针 }
#define _CRT_SECURE_NO_WARNINGS #include <stdio.h> #include <string.h> #include<time.h> #include <stdlib.h> #include"test.h" #include <math.h> /* for cos(), sin(), and sqrt() */ GLuint texture[2]; //视区 float whRatio; int wHeight = 0; int wWidth = 0; //视点 float center[] = { 0, 0, 0 }; float eye[] = { 0, 0, 5 }; void drawRect(GLuint texture) { glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, texture); //选择纹理texture[status] const GLfloat x1 = -0.5, x2 = 0.5; const GLfloat y1 = -0.5, y2 = 0.5; const GLfloat point[4][2] = { { x1,y1 },{ x2,y1 },{ x2,y2 },{ x1,y2 } }; int dir[4][2] = { { 0,0 },{ 1,0 },{ 1,1 },{ 0,1 } }; glBegin(GL_QUADS); for (int i = 0; i < 4; i++) { glTexCoord2iv(dir[i]); glVertex2fv(point[i]); } glEnd(); glDisable(GL_TEXTURE_2D); } void drawScene() { glPushMatrix(); glScalef(8, 6, 1); drawRect(texture[0]); glPopMatrix(); glDepthMask(GL_FALSE); glPushMatrix(); glTranslatef(0.0f,-1.0f, 1.0f); glScalef(1.8, 2, 0); drawRect(texture[1]); glPopMatrix(); glDepthMask(GL_TRUE); } void updateView(int height, int width) { glViewport(0, 0, width, height); glMatrixMode(GL_PROJECTION);//设置矩阵模式为投影 glLoadIdentity(); //初始化矩阵为单位矩阵 whRatio = (GLfloat)width / (GLfloat)height; //设置显示比例 glOrtho(-3, 3, -3, 3, -100, 100); //正投影 glMatrixMode(GL_MODELVIEW); //设置矩阵模式为模型 } void reshape(int width, int height) { if (height == 0) //如果高度为0 { height = 1; //让高度为1(避免出现分母为0的现象) } wHeight = height; wWidth = width; updateView(wHeight, wWidth); //更新视角 } void idle() { glutPostRedisplay(); } void init() { srand(unsigned(time(NULL))); glEnable(GL_DEPTH_TEST);//开启深度测试 glEnable(GL_ALPHA_TEST); glAlphaFunc(GL_GREATER, 0.5); glEnable(GL_LIGHTING); //开启光照模式 glClearColor(1.0f, 1.0f, 1.0f, 1.0f); unsigned char color[] = { 255,255,255 }; glGenTextures(2, texture); loadTex(0, "1.bmp", texture); loadTex(1, "2.bmp", texture,color); } void redraw() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);//清除颜色和深度缓存 glMatrixMode(GL_MODELVIEW); glLoadIdentity(); //初始化矩阵为单位矩阵 gluLookAt(eye[0], eye[1], eye[2], center[0], center[1], center[2], 0, 1, 0); // 场景(0,0,0)的视点中心 (0,5,50),Y轴向上 glPolygonMode(GL_FRONT, GL_FILL); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); // 启用光照计算 glEnable(GL_LIGHTING); // 指定环境光强度(RGBA) GLfloat ambientLight[] = { 2.0f, 2.0f, 2.0f, 1.0f }; // 设置光照模型,将ambientLight所指定的RGBA强度值应用到环境光 glLightModelfv(GL_LIGHT_MODEL_AMBIENT, ambientLight); // 启用颜色追踪 glEnable(GL_COLOR_MATERIAL); // 设置多边形正面的环境光和散射光材料属性,追踪glColor glColorMaterial(GL_FRONT, GL_AMBIENT_AND_DIFFUSE); drawScene();//绘制场景 glutSwapBuffers();//交换缓冲区 } int main(int argc, char *argv[]) { glutInit(&argc, argv);//对glut的初始化 glutInitDisplayMode(GLUT_RGBA | GLUT_DEPTH | GLUT_DOUBLE); //初始化显示模式:RGB颜色模型,深度测试,双缓冲 glutInitWindowSize(800, 600);//设置窗口大小 int windowHandle = glutCreateWindow("Simple GLUT App");//设置窗口标题 glutDisplayFunc(redraw); //注册绘制回调函数 glutReshapeFunc(reshape); //注册重绘回调函数 glutIdleFunc(idle);//注册全局回调函数:空闲时调用 init(); glutMainLoop(); // glut事件处理循环 return 0; }
标签:
原文地址:http://blog.csdn.net/zju_fish1996/article/details/51876197