NeHe OpenGL第二十三课:球面映射
球面映射:
这一个将教会你如何把环境纹理包裹在你的3D模型上,让它看起来象反射了周围的场景一样。
球体环境映射是一个创建快速金属反射效果的方法,但它并不像真实世界里那么精确!我们从18课的代码开始来创建这个教程,教你如何创建这种效果。
在我们开始之间,看一下红宝书中的介绍。它定义球体环境映射为一幅位于无限远的图像,把它映射到球面上。
在Photoshop中创建一幅球体环境映射图。
首先,你需要一幅球体环境映射图,用来把它映射到球体上。在Photoshop中打开一幅图并选择所有的像素,创建它的一个复制。
接着,我们把图像变为2的幂次方大小,一般为128x128或256x256。
最后使用扭曲(distort)滤镜,并应用球体效果。然后把它保存为*.bmp文件。
我们并没有添加任何全局变量,只是把纹理组的大小变为6,以保存6幅纹理。
GLuint texture[6]; // 保存6幅纹理
下面我们要做的就是载入这些纹理
int LoadGLTextures()
{
int Status=FALSE;
AUX_RGBImageRec *TextureImage[2]; // 创建纹理的保存空间
memset(TextureImage,0,sizeof(void *)*2); // 清空为0
// 载入*.bmp图像
if ((TextureImage[0]=LoadBMP("Data/BG.bmp")) && // 背景图
(TextureImage[1]=LoadBMP("Data/Reflect.bmp"))) // 反射图(球形纹理图)
{
Status=TRUE;
glGenTextures(6, &texture[0]); // 创建6个纹理
for (int loop=0; loop<=1; loop++)
{
// 创建临近点过滤纹理图
glBindTexture(GL_TEXTURE_2D, texture[loop]); // 创建纹理0和1
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, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
// 创建线形过滤纹理图
glBindTexture(GL_TEXTURE_2D, texture[loop+2]); // 创建纹理2,3
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
// 创建线形Mipmap纹理图
glBindTexture(GL_TEXTURE_2D, texture[loop+4]); // 创建纹理4,5
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR_MIPMAP_NEAREST);
gluBuild2DMipmaps(GL_TEXTURE_2D, 3, TextureImage[loop]->sizeX, TextureImage[loop]->sizeY,
GL_RGB, GL_UNSIGNED_BYTE, TextureImage[loop]->data);
}
for (loop=0; loop<=1; loop++)
{
if (TextureImage[loop]) // 如果图像存在则清除
{
if (TextureImage[loop]->data)
{
free(TextureImage[loop]->data);
}
free(TextureImage[loop]);
}
}
}
return Status;
}
我们对立方体的绘制代码做了一些小的改动,把法线的范围从[-1,1]缩放到[-0.5,0.5]。如果法向量太大的话,会产生一些块状效果,影响视觉效果。
GLvoid glDrawCube()
{
glBegin(GL_QUADS);
// 前面
glNormal3f( 0.0f, 0.0f, 0.5f);
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);
// 背面
glNormal3f( 0.0f, 0.0f,-0.5f);
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);
// 上面
glNormal3f( 0.0f, 0.5f, 0.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);
// 下面
glNormal3f( 0.0f,-0.5f, 0.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);
// 右面
glNormal3f( 0.5f, 0.0f, 0.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);
// 左面
glNormal3f(-0.5f, 0.0f, 0.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();
}
在初始化OpenGL中,我们添加一些新的函数来使用球体纹理映射。
下面的代码让OpenGL自动为我们计算使用球体映射时,顶点的纹理坐标。
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // 设置s方向的纹理自动生成
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); // 设置t方向的纹理自动生成
我们几乎完成了所有的工作!接下来要做的就是就是绘制渲染,我删除了一些二次几何体,因为它们的视觉效果并不好。当然我们需要OpenGL为这些几何体自动生成坐标,接着选择球体映射纹理并绘制几何体。最后把
OpenGL状态设置正常模式。
int DrawGLScene(GLvoid)
{
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity(); // 重置视口
glTranslatef(0.0f,0.0f,z);
glEnable(GL_TEXTURE_GEN_S); // 自动生成s方向纹理坐标
glEnable(GL_TEXTURE_GEN_T); // 自动生成t方向纹理坐标
glBindTexture(GL_TEXTURE_2D, texture[filter+(filter+1)]); // 绑定纹理
glPushMatrix();
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
switch(object)
{
case 0:
glDrawCube();
break;
case 1:
glTranslatef(0.0f,0.0f,-1.5f); // 创建圆柱
gluCylinder(quadratic,1.0f,1.0f,3.0f,32,32);
break;
case 2:
gluSphere(quadratic,1.3f,32,32); // 创建球
break;
case 3:
glTranslatef(0.0f,0.0f,-1.5f); // 创建圆锥
gluCylinder(quadratic,1.0f,0.0f,3.0f,32,32);
break;
};
glPopMatrix();
glDisable(GL_TEXTURE_GEN_S); // 禁止自动生成纹理坐标
glDisable(GL_TEXTURE_GEN_T);
xrot+=xspeed;
yrot+=yspeed;
return TRUE; // 成功返回
}
最后我们使用空格来切换各个不同的几何体
if (keys[' '] && !sp)
{
sp=TRUE;
object++;
if(object>3)
object=0;
}
原文及其个版本源代码下载:
http://nehe.gamedev.net/data/lessons/lesson.asp?lesson=23