编程中一个好的天空贴图会给玩家带来舒适的感觉,还有那对周围物体的反射光映射到球、等物体上或许是一个特别愉悦的事件,然而这在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;
}
实验结果截图: