编程中一个好的天空贴图会给玩家带来舒适的感觉,还有那对周围物体的反射光映射到球、等物体上或许是一个特别愉悦的事件,然而这在opengl里实现又不太难,
请看下面的代码:
#include <GL\glew.h> #include <GL\GLAUX.H> #define GLUT_DISABLE_ATEXIT_HACK #include <gl\glut.h> #include <stdio.h> #include <stdlib.h> // 摄像机参数 static float cameraTheta = 0, cameraPhi = 0; static int mouseX = 0, mouseY = 0, mouseDownX = 0, mouseDownY = 0; //天空渲染模式 static int cubeMapAvailable = 0; //纹理 static UINT textureObjectCubeMap; static UINT textureObjects2d[6]; //接收反射光物体参数 static int rotateObject = 1; static int objectAngle = 0; //渲染函数 static void drawSkybox(void); static void drawSkyboxCubeEnvironment(void); //初始化函数 static BOOL initSkybox(void); static void shutdownSkybox(void); //加载bmp图片 //swap_bytes用于翻转倒* BMP文件 BOOL loadImage(GLenum target, const char *fileName); #define SWAP_BYTES(b1,b2) (b1)=(b1)^(b2);(b2)=(b1)^(b2);(b1)=(b1)^(b2); static void onTimer(int value); static void onDisplay(void); static void onMouseMoveButtonDown(int x, int y); static void onMouseDown(int b, int s, int x, int y); static void onSpecialKey(int key, int x, int y); static void onKey(unsigned char key, int x, int y); //用立方体环境映射绘制天空盒 Skybox的立方体绘制两三角扇形 //以立方体的相对角为中心 void drawSkyboxCubeEnvironment(void) { drawSkybox();//绘制天空盒 //设置对象矩阵 glMatrixMode(GL_MODELVIEW); glPushMatrix(); glLoadIdentity(); glTranslatef(0, 0, -8); glRotatef(cameraPhi, 1, 0, 0); glRotatef(cameraTheta, 0, 1, 0); //旋转 glRotatef((float)objectAngle, 1, 0, 0); glRotatef((float)2 * objectAngle, 0, 1, 0); //旋转纹理矩阵匹配摄像机矩阵 //mode 指定哪一个矩阵堆栈是下一个矩阵操作的目标, //可选值: GL_MODELVIEW、GL_PROJECTION、GL_TEXTURE. glMatrixMode(GL_TEXTURE); glPushMatrix(); glLoadIdentity(); glRotatef(-cameraTheta, 0, 1, 0); glRotatef(-cameraPhi, 1, 0, 0); //绘制映射物体 if (cubeMapAvailable) { glDisable(GL_TEXTURE_2D); glEnable(GL_DEPTH_TEST);//开启深度测试 glEnable(GL_TEXTURE_CUBE_MAP_ARB);//开启映射 glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, textureObjectCubeMap);//绑定映射纹理 //绘制反光物体 //建立映射坐标生成 glTexGenf(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glTexGenf(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glTexGenf(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP_ARB); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glutSolidSphere(3, 16, 16); //禁用反射映射 glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glDisable(GL_DEPTH_TEST);//关闭深度测试 } glPopMatrix(); //恢复模型矩阵 glMatrixMode(GL_MODELVIEW); glPopMatrix();//恢复矩阵 } //采用六面和六2D纹理绘制天空盒 void drawSkybox(void) { //禁用映射 glDisable(GL_TEXTURE_CUBE_MAP_ARB); glEnable(GL_TEXTURE_2D); float skyHei = 20; //天空的高度 float skyLen = 40; //天空的长度 float skyWid = 40; //天空的宽度 //后面 glBindTexture(GL_TEXTURE_2D, textureObjects2d[0]); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(1, 0); glVertex3f(skyLen, skyHei, skyWid); glTexCoord2f(1, 1); glVertex3f(skyLen, -skyHei, skyWid); glTexCoord2f(0, 1); glVertex3f(-skyLen, -skyHei, skyWid); glTexCoord2f(0, 0); glVertex3f(-skyLen, skyHei, skyWid); glEnd(); //前面 glBindTexture(GL_TEXTURE_2D, textureObjects2d[1]); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(1, 0); glVertex3f(-skyLen, skyHei, -skyWid); glTexCoord2f(1, 1); glVertex3f(-skyLen, -skyHei, -skyWid); glTexCoord2f(0, 1); glVertex3f(skyLen, -skyHei, -skyWid); glTexCoord2f(0, 0); glVertex3f(skyLen, skyHei, -skyWid); glEnd(); //右面 glBindTexture(GL_TEXTURE_2D, textureObjects2d[2]); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(1, 0); glVertex3f(skyLen, skyHei, -skyWid); glTexCoord2f(1, 1); glVertex3f(skyLen, -skyHei, -skyWid); glTexCoord2f(0, 1); glVertex3f(skyLen, -skyHei, skyWid); glTexCoord2f(0, 0); glVertex3f(skyLen, skyHei, skyWid); glEnd(); //左面 glBindTexture(GL_TEXTURE_2D, textureObjects2d[3]); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(1, 0); glVertex3f(-skyLen, skyHei, skyWid); glTexCoord2f(1, 1); glVertex3f(-skyLen, -skyHei, skyWid); glTexCoord2f(0, 1); glVertex3f(-skyLen, -skyHei, -skyWid); glTexCoord2f(0, 0); glVertex3f(-skyLen, skyHei, -skyWid); glEnd(); //顶面 glBindTexture(GL_TEXTURE_2D, textureObjects2d[4]); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 1); glVertex3f(-skyLen, skyHei, skyWid); glTexCoord2f(0, 0); glVertex3f(-skyLen, skyHei, -skyWid); glTexCoord2f(1, 0); glVertex3f(skyLen, skyHei, -skyWid); glTexCoord2f(1, 1); glVertex3f(skyLen, skyHei, skyWid); glEnd(); //底面 glBindTexture(GL_TEXTURE_2D, textureObjects2d[5]); glBegin(GL_TRIANGLE_FAN); glTexCoord2f(0, 1); glVertex3f(-skyLen, -skyHei, -skyWid); glTexCoord2f(0, 0); glVertex3f(-skyLen, -skyHei, skyWid); glTexCoord2f(1, 0); glVertex3f(skyLen, -skyHei, skyWid); glTexCoord2f(1, 1); glVertex3f(skyLen, -skyHei, -skyWid); glEnd(); glDisable(GL_TEXTURE_2D);//关闭纹理 } void onDisplay(void) { float w = (float)glutGet(GLUT_WINDOW_WIDTH), h = (float)glutGet(GLUT_WINDOW_HEIGHT); glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(100.0, w / h, 1, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glRotatef(cameraPhi, 1, 0, 0); glRotatef(cameraTheta, 0, 1, 0); glClear(GL_DEPTH_BUFFER_BIT); drawSkyboxCubeEnvironment(); glutSwapBuffers(); glutPostRedisplay(); } //定时器 void onTimer(int value) { if (rotateObject) { objectAngle = (objectAngle + 1) % 360; } glutTimerFunc(10, onTimer, 0); } //鼠标事件 void onMouseMoveButtonDown(int x, int y) { cameraTheta += (float)(x - mouseDownX); cameraPhi += (float)(y - mouseDownY); cameraPhi = max(-90, min(cameraPhi, 90)); mouseDownX = x; mouseDownY = y; } void onMouseDown(int b, int s, int x, int y) { if (s == GLUT_DOWN) { mouseDownX = x; mouseDownY = y; glutMotionFunc(onMouseMoveButtonDown); } else { glutMotionFunc(0); } } void onSpecialKey(int key, int x, int y) { switch (key) { case GLUT_KEY_RIGHT: cameraTheta = cameraTheta + 5; while (cameraTheta > 360) cameraTheta -= 360; break; case GLUT_KEY_LEFT: cameraTheta = cameraTheta - 5; while (cameraTheta < 0) cameraTheta += 360; break; case GLUT_KEY_UP: cameraPhi = cameraPhi + 5; if (cameraPhi > 90) cameraPhi = 90; break; case GLUT_KEY_DOWN: cameraPhi = cameraPhi - 5; if (cameraPhi < -90) cameraPhi = -90; break; }; } void onKey(unsigned char key, int x, int y) { switch (key) { case VK_ESCAPE:exit(0); break; case 'r': case 'R': rotateObject = !rotateObject; break; }; } //加载bmp图片 BOOL loadImage(GLenum target, const char *fileName) { int i, topRow, bottomRow; AUX_RGBImageRec *image = auxDIBImageLoad(fileName); if (image != 0) { topRow = 0; bottomRow = 3 * image->sizeX*(image->sizeY - 1); while (topRow < bottomRow) { for (i = 0; i<3 * image->sizeX; i++) { SWAP_BYTES(image->data[bottomRow + i], image->data[topRow + i]); } bottomRow -= 3 * image->sizeX; topRow += 3 * image->sizeX; } glTexImage2D(target, 0, 3, image->sizeX, image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, image->data); free(image->data); free(image); return TRUE; } else { return FALSE; } } //初始化 BOOL initSkybox(void) { int i; int clampMode; BOOL succeeded = GL_TRUE; char *imageFiles[] = { "data/image1.bmp", "data/image2.bmp", "data/image3.bmp", "data/image4.bmp", "data/image5.bmp", "data/image6.bmp" }; glGenTextures(6, textureObjects2d); glGenTextures(1, &textureObjectCubeMap); //单位化 glEnable(GL_NORMALIZE); //如果我们能用gl_clamp_to_edge //判定是否支持特定的OpenGL扩展 //extension是指定要测试的OpenGL扩展的名称 if (glutExtensionSupported("GL_EXT_texture_edge_clamp")) { clampMode = GL_CLAMP_TO_EDGE;//去除接缝间的空隙边框是否处理?(不处理) } else { clampMode = GL_CLAMP; } //生成 2d textures for (i = 0; i<6; i++) { glBindTexture(GL_TEXTURE_2D, textureObjects2d[i]); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, clampMode); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, clampMode);//去除接缝间的空隙边框是否处理?(不处理) succeeded = succeeded && loadImage(GL_TEXTURE_2D, imageFiles[i]); } //生成多维数据集映射,如果支持 if (glutExtensionSupported("GL_ARB_texture_cube_map")) { cubeMapAvailable = 1; glBindTexture(GL_TEXTURE_CUBE_MAP_ARB, textureObjectCubeMap); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP_ARB, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); succeeded = succeeded && loadImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Z_ARB, imageFiles[0]); succeeded = succeeded && loadImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Z_ARB, imageFiles[1]); succeeded = succeeded && loadImage(GL_TEXTURE_CUBE_MAP_POSITIVE_X_ARB, imageFiles[2]); succeeded = succeeded && loadImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_X_ARB, imageFiles[3]); succeeded = succeeded && loadImage(GL_TEXTURE_CUBE_MAP_POSITIVE_Y_ARB, imageFiles[4]); succeeded = succeeded && loadImage(GL_TEXTURE_CUBE_MAP_NEGATIVE_Y_ARB, imageFiles[5]); } else { cubeMapAvailable = 0; } return succeeded; } // 删除内存 void shutdownSkybox(void) { glDeleteTextures(6, textureObjects2d); glDeleteTextures(1, &textureObjectCubeMap); } int main(int argc, char **argv) { glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB | GLUT_DEPTH); glutCreateWindow("天空盒及照在物体上的反射光"); glutReshapeWindow(1366, 768); glutDisplayFunc(onDisplay); glutMouseFunc(onMouseDown); glutSpecialFunc(onSpecialKey); glutKeyboardFunc(onKey); glutTimerFunc(10, onTimer, 0); if (!initSkybox()) { shutdownSkybox(); return 0; } glutMainLoop(); shutdownSkybox(); return 0; }
实验结果截图: