NeHe OpenGL教程第二十二课,DancingWind翻译
From Nehe SDK
![]() |
第22课 |
|
这一课由Jens Schneider所写,它基本上是由第6课改写而来的,在这一课里,你将学习:
最后这份代码超过了1200行,大部分我们在前面的教程中遇到过了。我不会解释每一行代码,只在重要的地方做些提示,好了,让我们开始吧。 |
#include <string.h> // 字符串处理函数
| MAX_EMBOSS常量定义了突起的最大值 |
#define MAX_EMBOSS (GLfloat)0.01f // 定义了突起的最大值
| 好了,现在我们准备使用GL_ARB_multitexture这个扩展,它非常简单。
大部分图形卡不止一个纹理单元,为了利用这个功能,你必须检查GL_ARB_multitexture是否被支持,它可以使你同时把2个或多个不同的纹理映射到OpenGL图元上。开起来这个功能好像没有太大的作用,但当你使用多个纹理时,如果能同时把这些纹理值混合,而不使用费时的乘法运算,你将会得到很高的速度提高。 现在回到我们的代码,__ARB_ENABLE用来设置是否使用ARB扩展。如果你想看你的OpenGL扩展,只要把#define EXT_INFO前的注释去掉就行了。接着,我们在运行检查我们的扩展,以保证我们的程序可以在不同的系统上运行。所以我们需要一些内存保存扩展名的字符串,他们是下面两行。接着我们用一个变量multitextureSupported来标志当前系统是否能使用multitexture扩展,并用maxTexelUnits记录运行系统的纹理单元,这个值最少是1。 |
#define __ARB_ENABLE true // 使用它设置是否使用ARB扩展 // #define EXT_INFO // 把注释去掉,可以在启动时查看你的扩展 #define MAX_EXTENSION_SPACE 10240 // 保存扩展字符 #define MAX_EXTENSION_LENGTH 256 // 每个扩展字符串最大的长度 bool multitextureSupported=false; // 标志是否支持多重渲染 bool useMultitexture=true; // 如果支持,是否使用它 GLint maxTexelUnits=1; // 纹理处理单元的个数
| 下面的函数定义用来使用OpenGL的扩展函数,你可以把PFN-who-ever-reads-this看成是预先定义的函数类型,因为我们不清楚是否能得到这些函数的实体,所以先把他们都设置为NULL。glMultiTexCoordifARB函数是glTexCoordif函数的扩展,它门的功能相似,其中i为纹理坐标的维数,f为数据的类型。最后两个函数用来激活纹理处理单元,可以使用特定的纹理单元来邦定纹理。 顺便说一句,ARB是"Architectural Review Board"的缩写,用来定义这个组织提出的对OpenGL的扩展,并不强制OpenGL的实现必须包含这个功能,但他们希望这个功能得到广泛的支持。当前,只有multitexture被加入到ARB中,这从另一个方面支持multitexture的扩展将大大的提高渲染速度。 |
PFNGLMULTITEXCOORD1FARBPROC glMultiTexCoord1fARB = NULL; PFNGLMULTITEXCOORD2FARBPROC glMultiTexCoord2fARB = NULL; PFNGLMULTITEXCOORD3FARBPROC glMultiTexCoord3fARB = NULL; PFNGLMULTITEXCOORD4FARBPROC glMultiTexCoord4fARB = NULL; PFNGLACTIVETEXTUREARBPROC glActiveTextureARB = NULL; PFNGLCLIENTACTIVETEXTUREARBPROC glClientActiveTextureARB= NULL;
下面我们来定义一些全局变量:
|
GLuint filter=1; // 定义过滤器类型 GLuint texture[3]; // 保存三个纹理 GLuint bump[3]; //保存三个凹凸纹理 GLuint invbump[3]; // 保存三个反转了的凹凸纹理 GLuint glLogo; // glLogo保存标志 GLuint multiLogo; // multiLogo保存多重纹理标志 GLfloat LightAmbient[] = { 0.2f, 0.2f, 0.2f}; // 环境光 GLfloat LightDiffuse[] = { 1.0f, 1.0f, 1.0f}; // 漫射光 GLfloat LightPosition[] = { 0.0f, 0.0f, 2.0f}; // 光源位置 GLfloat Gray[] = { 0.5f, 0.5f, 0.5f, 1.0f};
| 下面一块代码用来保存立方体的纹理和坐标,每5个数字描述一个顶点,包含2D的纹理坐标和3D的顶点坐标。 |
// 立方体的纹理和坐标 GLfloat data[]= { // 前面 0.0f, 0.0f, -1.0f, -1.0f, +1.0f, 1.0f, 0.0f, +1.0f, -1.0f, +1.0f, 1.0f, 1.0f, +1.0f, +1.0f, +1.0f, 0.0f, 1.0f, -1.0f, +1.0f, +1.0f, // 背面 1.0f, 0.0f, -1.0f, -1.0f, -1.0f, 1.0f, 1.0f, -1.0f, +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, -1.0f, 0.0f, 0.0f, +1.0f, -1.0f, -1.0f, // 上面 0.0f, 1.0f, -1.0f, +1.0f, -1.0f, 0.0f, 0.0f, -1.0f, +1.0f, +1.0f, 1.0f, 0.0f, +1.0f, +1.0f, +1.0f, 1.0f, 1.0f, +1.0f, +1.0f, -1.0f, // 下面 1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 0.0f, 1.0f, +1.0f, -1.0f, -1.0f, 0.0f, 0.0f, +1.0f, -1.0f, +1.0f, 1.0f, 0.0f, -1.0f, -1.0f, +1.0f, // 右面 1.0f, 0.0f, +1.0f, -1.0f, -1.0f, 1.0f, 1.0f, +1.0f, +1.0f, -1.0f, 0.0f, 1.0f, +1.0f, +1.0f, +1.0f, 0.0f, 0.0f, +1.0f, -1.0f, +1.0f, // 左面 0.0f, 0.0f, -1.0f, -1.0f, -1.0f, 1.0f, 0.0f, -1.0f, -1.0f, +1.0f, 1.0f, 1.0f, -1.0f, +1.0f, +1.0f, 0.0f, 1.0f, -1.0f, +1.0f, -1.0f };
| 下一部分代码,用来这运行时确定是否支持多重纹理的扩展。
首先,我们假定一个字符串包含了所有的扩展名,各个扩展名之间用'\n'分开。我们所要做的就是在其中查找是否有我们需要的扩展。如果成功找到则返回TRUE,否则返回FALSE。 |
bool isInString(char *string, const char *search) {
int pos=0;
int maxpos=strlen(search)-1;
int len=strlen(string);
char *other;
for (int i=0; i<len; i++) {
if ((i==0) || ((i>1) && string[i-1]=='\n')) { // 新的扩展名开始与这里
other=&string[i];
pos=0; // 开始新的比较
while (string[i]!='\n') { // 比较整个扩展名
if (string[i]==search[pos]) pos++; // 下一个字符
if ((pos>maxpos) && string[i+1]=='\n') return true; // 如果整个扩展名相同则成功返回
i++;
}
}
}
return false; // 没找到
}
| 现在我们需要先取得扩展名字符串,并把它转换为以'\n'分割的字符串,接着调用以上定义的函数看看是否包含我们需要的扩展。如果定义了__ARB_ENABLE则使用多重纹理扩展,接下来我们检查是否支持GL_EXT_texture_env_combine扩展,这个扩展提供各个纹理单元复杂的交互方式,利用它可以完成复杂的混合方程。如果所有的扩展都被支持,我们首先取得纹理单元的个数,把它保存到变量maxTexelUnits中,接着通过函数wglGetProcAdress把各个函数定义连接到各自的实体上,这样在后面的程序中就可以使用这些函数了。 |
bool initMultitexture(void) {
char *extensions;
extensions=strdup((char *) glGetString(GL_EXTENSIONS)); // 返回扩展名字符串
int len=strlen(extensions);
for (int i=0; i<len; i++) // 使用'\n'分割各个扩展名
if (extensions[i]==' ') extensions[i]='\n';
#ifdef EXT_INFO
MessageBox(hWnd,extensions,"supported GL extensions",MB_OK | MB_ICONINFORMATION);
#endif
if (isInString(extensions,"GL_ARB_multitexture") // 是否支持多重纹理扩展?
&& __ARB_ENABLE // 是否使用多重纹理扩展?
&& isInString(extensions,"GL_EXT_texture_env_combine")) // 是否支持纹理环境混合
{
glGetIntegerv(GL_MAX_TEXTURE_UNITS_ARB,&maxTexelUnits);
glMultiTexCoord1fARB = (PFNGLMULTITEXCOORD1FARBPROC) wglGetProcAddress("glMultiTexCoord1fARB");
glMultiTexCoord2fARB = (PFNGLMULTITEXCOORD2FARBPROC) wglGetProcAddress("glMultiTexCoord2fARB");
glMultiTexCoord3fARB = (PFNGLMULTITEXCOORD3FARBPROC) wglGetProcAddress("glMultiTexCoord3fARB");
glMultiTexCoord4fARB = (PFNGLMULTITEXCOORD4FARBPROC) wglGetProcAddress("glMultiTexCoord4fARB");
glActiveTextureARB = (PFNGLACTIVETEXTUREARBPROC) wglGetProcAddress("glActiveTextureARB");
glClientActiveTextureARB= (PFNGLCLIENTACTIVETEXTUREARBPROC) wglGetProcAddress("glClientActiveTextureARB");
#ifdef EXT_INFO
MessageBox(hWnd,"The GL_ARB_multitexture 扩展被使用.","支持多重纹理",MB_OK | MB_ICONINFORMATION);
#endif
return true;
}
useMultitexture=false; // 如果不支持多重纹理则返回false
return false;
}
| 初始化灯光 |
void initLights(void) {
glLightfv(GL_LIGHT1, GL_AMBIENT, LightAmbient);
glLightfv(GL_LIGHT1, GL_DIFFUSE, LightDiffuse);
glLightfv(GL_LIGHT1, GL_POSITION, LightPosition);
glEnable(GL_LIGHT1);
}
| 下面我们加载许多纹理,这和前面的教程很像 |
int LoadGLTextures() { // 载入*.bmp图像,并转换为纹理
bool status=true;
AUX_RGBImageRec *Image=NULL;
char *alpha=NULL;
// 加载基础纹理
if (Image=auxDIBImageLoad("Data/Base.bmp")) {
glGenTextures(3, texture); // 创建3个纹理
// 创建使用临近过滤器过滤得纹理
glBindTexture(GL_TEXTURE_2D, texture[0]);
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, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);
// 创建使用线形过滤器过滤得纹理
glBindTexture(GL_TEXTURE_2D, texture[1]);
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, GL_RGB8, Image->sizeX, Image->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, Image->data);
// 创建使用线形Mipmap过滤器过滤得纹理
glBindTexture(GL_TEXTURE_2D, texture[2]);
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, GL_RGB8, Image->sizeX, Image->sizeY, GL_RGB, GL_UNSIGNED_BYTE, Image->data);
}
else status=false;
if (Image) { // 如果图像句柄存在,则释放图像回收资源
if (Image->data) delete Image->data;
delete Image;
Image=NULL;
}
| 现在我们加载凹凸映射纹理。这个纹理必须使用50%的亮度(原因我们在后面介绍),我们使用glPixelTransferf函数完成这个功能。 另一个限制是我们不希望纹理重复贴图,只希望它粘贴一次,从纹理坐标(0,0)-(1,1),所有大于它的纹理坐标都被映射到边缘,为了完成这个功能,我们使用glTexParameteri函数。 |
// 载入凹凸贴图 if (Image=auxDIBImageLoad("Data/Bump.bmp")) { glPixelTransferf(GL_RED_SCALE,0.5f); // 把颜色值变为原来的50% glPixelTransferf(GL_GREEN_SCALE,0.5f); glPixelTransferf(GL_BLUE_SCALE,0.5f); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_S,GL_CLAMP); //不使用重复贴图 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_WRAP_T,GL_CLAMP); glGenTextures(3, bump); //创建凹凸贴图纹理 // 创建使用临近过滤器过滤得纹理 >…<
// 创建使用线形过滤器过滤得纹理 >…<
// 创建使用线形Mipmap过滤器过滤得纹理 >…<
| 反转凹凸贴图数据,创建三个反转的凹凸贴图纹理 |
for (int i=0; i<3*Image->sizeX*Image->sizeY; i++) // 反转凹凸贴图数据 Image->data[i]=255-Image->data[i]; glGenTextures(3, invbump); // 创建三个反转了凹凸贴图
// 创建使用临近过滤器过滤得纹理 >…<
// 创建使用线形过滤器过滤得纹理 >…<
// 创建使用线形Mipmap过滤器过滤得纹理 >…< } else status=false; if (Image) { // 如果图像存在,则删除 if (Image->data) delete Image->data; delete Image; Image=NULL; }
| 载入标志图像,图像是把颜色和alpha通道存为两张不同的bmp位图的,所以在处理的时候需要注意以下各个分量的位置。 |
if (Image=auxDIBImageLoad("Data/OpenGL_ALPHA.bmp")) {
alpha=new char[4*Image->sizeX*Image->sizeY];
for (int a=0; a<Image->sizeX*Image->sizeY; a++)
alpha[4*a+3]=Image->data[a*3];
if (!(Image=auxDIBImageLoad("Data/OpenGL.bmp"))) status=false;
for (a=0; a<Image->sizeX*Image->sizeY; a++) {
alpha[4*a]=Image->data[a*3];
alpha[4*a+1]=Image->data[a*3+1];
alpha[4*a+2]=Image->data[a*3+2];
}
glGenTextures(1, &glLogo); // 创建标志纹理
// 使用线形过滤器
glBindTexture(GL_TEXTURE_2D, glLogo);
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, GL_RGBA8, Image->sizeX, Image->sizeY, 0, GL_RGBA, GL_UNSIGNED_BYTE, alpha);
delete alpha;
}
else status=false;
if (Image) { // 如果图像存在,则删除
if (Image->data) delete Image->data;
delete Image;
Image=NULL;
}
// 载入扩展标志纹理
if (Image=auxDIBImageLoad("Data/multi_on_alpha.bmp")) {
alpha=new char[4*Image->sizeX*Image->sizeY];
>...<
glGenTextures(1, &multiLogo);
>...<
delete alpha;
}
else status=false;
if (Image) { // 如果图像存在,则删除
if (Image->data) delete Image->data;
delete Image;
Image=NULL;
}
return status;
}
| 下面是窗口大小变化函数,没有任何改变。
接下来是绘制一个立方体的函数,它使用常规的方法绘制。 |
void doCube (void) {
int i;
glBegin(GL_QUADS);
// 前面
glNormal3f( 0.0f, 0.0f, +1.0f);
for (i=0; i<4; i++) {
glTexCoord2f(data[5*i],data[5*i+1]);
glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
}
// 后面
glNormal3f( 0.0f, 0.0f,-1.0f);
for (i=4; i<8; i++) {
glTexCoord2f(data[5*i],data[5*i+1]);
glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
}
// 上面
glNormal3f( 0.0f, 1.0f, 0.0f);
for (i=8; i<12; i++) {
glTexCoord2f(data[5*i],data[5*i+1]);
glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
}
// 下面
glNormal3f( 0.0f,-1.0f, 0.0f);
for (i=12; i<16; i++) {
glTexCoord2f(data[5*i],data[5*i+1]);
glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
}
// 右面
glNormal3f( 1.0f, 0.0f, 0.0f);
for (i=16; i<20; i++) {
glTexCoord2f(data[5*i],data[5*i+1]);
glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
}
// 左面
glNormal3f(-1.0f, 0.0f, 0.0f);
for (i=20; i<24; i++) {
glTexCoord2f(data[5*i],data[5*i+1]);
glVertex3f(data[5*i+2],data[5*i+3],data[5*i+4]);
}
glEnd();
}
| 现在到了OpenGL的初始化函数,它和前面的教程基本相同,只是添加了以下代码: |
multitextureSupported=initMultitexture(); initLights();
| 这里我们完成了95%的工作,下面我们来解释上面提到的原理。 开始理论讲解(凹凸映射) 凹凸贴图 凹凸贴图 真实的凹凸贴图是逐像素计算的
凹凸贴图是在效果和精度之间的一个折中
漫射光的计算 C = (L*N)*Dl*Dm
近似的漫射因子 L*N 纹理图代表高度场
偏移量m的导出 偏移量m的近似导出
计算凹凸量
我们还没有完成所有的任务,还必须做以下内容:
保存纹理!
把灯光方向向量变换到一个笛卡尔坐标系中
使用法向坐标系中的向量作为偏移量
计算向量,纹理坐标
虽然我们做了一些改动,使得这个程序的实现与TNT的实现不一样,但它能工作与各种不同的显卡上。在这里我们将学到两三件事,凹凸映射在大多数显卡上是一个多通道算法(在TNT系列,可以使用一个2纹理通道实现),现在你应该能想到多重纹理的好处了吧。我们将使用一个三通道非多重纹理的算法实现,这个算法可以被改写为使用一个2纹理通道实现的算法。 现在必须告诉你,我们将要做一些矩阵和向量的乘法,但那没有什么可担心的,所有的矩阵和向量都使用齐次坐标。 |
// 计算向量v=v*M(左乘)
void VMatMult(GLfloat *M, GLfloat *v) {
GLfloat res[3];
res[0]=M[ 0]*v[0]+M[ 1]*v[1]+M[ 2]*v[2]+M[ 3]*v[3];
res[1]=M[ 4]*v[0]+M[ 5]*v[1]+M[ 6]*v[2]+M[ 7]*v[3];
res[2]=M[ 8]*v[0]+M[ 9]*v[1]+M[10]*v[2]+M[11]*v[3];
v[0]=res[0];
v[1]=res[1];
v[2]=res[2];
v[3]=M[15];
}
开始理论讲解(凹凸映射) 开始,让我们看看它的算法
但这个实现在计算漫射光的方法和我们是一样的,我们不能存储漫射因子,所以我们不能使用Michael I. Gold介绍的方法,因为我们想让它在任何显卡上运行而不仅仅是TNT系列。为什么不光照计算留到最后呢?这在简单的几何体绘制上是可行的,如果你需要渲染几千个具有凹凸贴图的三角形,你会感到绘制的速度不够快,到那时你需要改变这种渲染过程,寻找其它的方法。 在我们的实现里,它看起来和上面的实现差不多,除了投影部分,我们将使用我们自己的近似。
这个示意图显示了我们坐标系统,你可以通过相减相邻的坐标来获得s,t向量,但必须保证他们构成右手系和归一化。 结束理论讲解(凹凸映射) 下面让我们看看如何生成偏移量,首先创建一个函数创建凹凸映射: |
// 设置纹理偏移,都为单位长度 // n : 表面的法向量 // c : 当前的顶点纹理坐标,返回纹理坐标的偏移量 // l : 灯光的位置 // s : s方向 // t : t方向 void SetUpBumps(GLfloat *n, GLfloat *c, GLfloat *l, GLfloat *s, GLfloat *t) { GLfloat v[3]; // 灯光方向 GLfloat lenQ; // 灯光方向向量的长度,使用它来单位化 // 计算灯光方向 v[0]=l[0]-c[0]; v[1]=l[1]-c[1]; v[2]=l[2]-c[2]; lenQ=(GLfloat) sqrt(v[0]*v[0]+v[1]*v[1]+v[2]*v[2]); v[0]/=lenQ; v[1]/=lenQ; v[2]/=lenQ; // 把方向向量投影到s,t方向 c[0]=(s[0]*v[0]+s[1]*v[1]+s[2]*v[2])*MAX_EMBOSS; c[1]=(t[0]*v[0]+t[1]*v[1]+t[2]*v[2])*MAX_EMBOSS;
}
| 那看起来复杂么,但为了理解这个效果理论是必须的。(我在写这篇教程的时候也学习了它)。
我在程序运行的时候,总喜欢在屏幕上显示标志,现在我们有了两个,使用doLogo函数创建它。 下面的函数显示两个标志:一个OpenGL的标志,一个多重纹理的标志,如果可以使用多重纹理,则标志使用alpha混合,并看起来半透明。为了让它在屏幕的边沿显示我们使用混合并禁用光照和深度测试。
|
void doLogo(void) {
// 必须最后在调用这个函数,以公告板的形式显示两个标志
glDepthFunc(GL_ALWAYS);
glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_BLEND);
glDisable(GL_LIGHTING);
glLoadIdentity();
glBindTexture(GL_TEXTURE_2D,glLogo);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f); glVertex3f(0.23f, -0.4f,-1.0f);
glTexCoord2f(1.0f,0.0f); glVertex3f(0.53f, -0.4f,-1.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(0.53f, -0.25f,-1.0f);
glTexCoord2f(0.0f,1.0f); glVertex3f(0.23f, -0.25f,-1.0f);
glEnd();
if (useMultitexture) {
glBindTexture(GL_TEXTURE_2D,multiLogo);
glBegin(GL_QUADS);
glTexCoord2f(0.0f,0.0f); glVertex3f(-0.53f, -0.25f,-1.0f);
glTexCoord2f(1.0f,0.0f); glVertex3f(-0.33f, -0.25f,-1.0f);
glTexCoord2f(1.0f,1.0f); glVertex3f(-0.33f, -0.15f,-1.0f);
glTexCoord2f(0.0f,1.0f); glVertex3f(-0.53f, -0.15f,-1.0f);
glEnd();
}
glDepthFunc(GL_LEQUAL);
}
| 现在到了绘制凹凸贴图的函数了,我们先来看看不使用多重映射的方法,它通过三个通道实现。在第一步,我们先取得模型变换矩阵的逆矩阵! |
bool doMesh1TexelUnits(void) {
GLfloat c[4]={0.0f,0.0f,0.0f,1.0f}; // 保存当前的顶点
GLfloat n[4]={0.0f,0.0f,0.0f,1.0f}; // 保存法线
GLfloat s[4]={0.0f,0.0f,0.0f,1.0f}; // s纹理坐标方向
GLfloat t[4]={0.0f,0.0f,0.0f,1.0f}; // t纹理坐标方向
GLfloat l[4]; // 保存灯光方向
GLfloat Minv[16]; // 保存模型变换矩阵的逆
int i;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清空背景颜色和深度缓存
// 创建模型变换矩阵的逆
glLoadIdentity();
glRotatef(-yrot,0.0f,1.0f,0.0f);
glRotatef(-xrot,1.0f,0.0f,0.0f);
glTranslatef(0.0f,0.0f,-z);
glGetFloatv(GL_MODELVIEW_MATRIX,Minv);
glLoadIdentity();
glTranslatef(0.0f,0.0f,z);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
// 设置灯光的位置
l[0]=LightPosition[0];
l[1]=LightPosition[1];
l[2]=LightPosition[2];
l[3]=1.0f;
VMatMult(Minv,l);
通道1:
|
glBindTexture(GL_TEXTURE_2D, bump[filter]); glDisable(GL_BLEND); glDisable(GL_LIGHTING); doCube();
通道2:
|
glBindTexture(GL_TEXTURE_2D,invbump[filter]); glBlendFunc(GL_ONE,GL_ONE); glDepthFunc(GL_LEQUAL); glEnable(GL_BLEND); glBegin(GL_QUADS); // 前面 n[0]=0.0f; n[1]=0.0f; n[2]=1.0f; s[0]=1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=0; i<4; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); // 设置纹理坐标为偏移后的纹理坐标 glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 后面 n[0]=0.0f; n[1]=0.0f; n[2]=-1.0f; s[0]=-1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=4; i<8; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 上面 n[0]=0.0f; n[1]=1.0f; n[2]=0.0f; s[0]=1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=0.0f; t[2]=-1.0f; for (i=8; i<12; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 下面 n[0]=0.0f; n[1]=-1.0f; n[2]=0.0f; s[0]=-1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=0.0f; t[2]=-1.0f; for (i=12; i<16; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 右面 n[0]=1.0f; n[1]=0.0f; n[2]=0.0f; s[0]=0.0f; s[1]=0.0f; s[2]=-1.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=16; i<20; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 左面 n[0]=-1.0f; n[1]=0.0f; n[2]=0.0f; s[0]=0.0f; s[1]=0.0f; s[2]=1.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=20; i<24; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glTexCoord2f(data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } glEnd();
通道3:
|
if (!emboss) {
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D,texture[filter]);
glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
glEnable(GL_LIGHTING);
doCube();
}
最后的通道:
|
xrot+=xspeed; yrot+=yspeed; if (xrot>360.0f) xrot-=360.0f; if (xrot<0.0f) xrot+=360.0f; if (yrot>360.0f) yrot-=360.0f; if (yrot<0.0f) yrot+=360.0f; //绘制标志 doLogo(); return true; // 成功返回 }
| 这个函数将在多重纹理功能的支持下载两个通道中完成凹凸贴图的绘制,我们支持两个纹理单元,与一个纹理单元不同的是,我们给一个顶点设置两个纹理坐标。 |
bool doMesh2TexelUnits(void) {
GLfloat c[4]={0.0f,0.0f,0.0f,1.0f}; // 保存当前的顶点
GLfloat n[4]={0.0f,0.0f,0.0f,1.0f}; // 保存法线
GLfloat s[4]={0.0f,0.0f,0.0f,1.0f}; // s纹理坐标方向
GLfloat t[4]={0.0f,0.0f,0.0f,1.0f}; // t纹理坐标方向
GLfloat l[4]; // 保存灯光方向
GLfloat Minv[16]; // 保存模型变换矩阵的逆
int i;
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 清空背景颜色和深度缓存
// 创建模型变换矩阵的逆
glLoadIdentity();
glRotatef(-yrot,0.0f,1.0f,0.0f);
glRotatef(-xrot,1.0f,0.0f,0.0f);
glTranslatef(0.0f,0.0f,-z);
glGetFloatv(GL_MODELVIEW_MATRIX,Minv);
glLoadIdentity();
glTranslatef(0.0f,0.0f,z);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
// 设置灯光的位置
l[0]=LightPosition[0];
l[1]=LightPosition[1];
l[2]=LightPosition[2];
l[3]=1.0f;
VMatMult(Minv,l);
通道1:
|
// 纹理单元 #0 glActiveTextureARB(GL_TEXTURE0_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, bump[filter]); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_REPLACE); // 纹理单元 #1 glActiveTextureARB(GL_TEXTURE1_ARB); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, invbump[filter]); glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_COMBINE_EXT); glTexEnvf (GL_TEXTURE_ENV, GL_COMBINE_RGB_EXT, GL_ADD); // 禁用混合和光照 glDisable(GL_BLEND); glDisable(GL_LIGHTING);
| 现在按面一个一个的渲染立方体,和doMesh1TexelUnits函数中所作的操作差不多,只是用glMultiTexCoor2fARB替换glTexCoord2f,在这个函数中,你必须把纹理坐标发向不同的纹理处理单元,可用的参数值为GL_TEXTUREi_ARB0到GL_TEXTUREi_ARB31。 |
glBegin(GL_QUADS); // 前面 n[0]=0.0f; n[1]=0.0f; n[2]=1.0f; s[0]=1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=0; i<4; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]); glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 后面 n[0]=0.0f; n[1]=0.0f; n[2]=-1.0f; s[0]=-1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=4; i<8; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]); glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 上面 n[0]=0.0f; n[1]=1.0f; n[2]=0.0f; s[0]=1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=0.0f; t[2]=-1.0f; for (i=8; i<12; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]); glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 下面 n[0]=0.0f; n[1]=-1.0f; n[2]=0.0f; s[0]=-1.0f; s[1]=0.0f; s[2]=0.0f; t[0]=0.0f; t[1]=0.0f; t[2]=-1.0f; for (i=12; i<16; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]); glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 右面 n[0]=1.0f; n[1]=0.0f; n[2]=0.0f; s[0]=0.0f; s[1]=0.0f; s[2]=-1.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=16; i<20; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]); glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } // 左面 n[0]=-1.0f; n[1]=0.0f; n[2]=0.0f; s[0]=0.0f; s[1]=0.0f; s[2]=1.0f; t[0]=0.0f; t[1]=1.0f; t[2]=0.0f; for (i=20; i<24; i++) { c[0]=data[5*i+2]; c[1]=data[5*i+3]; c[2]=data[5*i+4]; SetUpBumps(n,c,l,s,t); glMultiTexCoord2fARB(GL_TEXTURE0_ARB,data[5*i], data[5*i+1]); glMultiTexCoord2fARB(GL_TEXTURE1_ARB,data[5*i]+c[0], data[5*i+1]+c[1]); glVertex3f(data[5*i+2], data[5*i+3], data[5*i+4]); } glEnd();
通道2:
|
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
if (!emboss) {
glTexEnvf (GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
glBindTexture(GL_TEXTURE_2D,texture[filter]);
glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
glEnable(GL_BLEND);
glEnable(GL_LIGHTING);
doCube();
}
最后的通道:
|
xrot+=xspeed; yrot+=yspeed; if (xrot>360.0f) xrot-=360.0f; if (xrot<0.0f) xrot+=360.0f; if (yrot>360.0f) yrot-=360.0f; if (yrot<0.0f) yrot+=360.0f; doLogo(); return true; }
| 最后绘制一个无凹凸贴图的立方体,用来观察两者之间的效果 |
bool doMeshNoBumps(void) {
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glLoadIdentity();
glTranslatef(0.0f,0.0f,z);
glRotatef(xrot,1.0f,0.0f,0.0f);
glRotatef(yrot,0.0f,1.0f,0.0f);
if (useMultitexture) {
glActiveTextureARB(GL_TEXTURE1_ARB);
glDisable(GL_TEXTURE_2D);
glActiveTextureARB(GL_TEXTURE0_ARB);
}
glDisable(GL_BLEND);
glBindTexture(GL_TEXTURE_2D,texture[filter]);
glBlendFunc(GL_DST_COLOR,GL_SRC_COLOR);
glEnable(GL_LIGHTING);
doCube();
xrot+=xspeed;
yrot+=yspeed;
if (xrot>360.0f) xrot-=360.0f;
if (xrot<0.0f) xrot+=360.0f;
if (yrot>360.0f) yrot-=360.0f;
if (yrot<0.0f) yrot+=360.0f;
doLogo();
return true;
}
| 所有的绘制函数都已经完成,接下来只要在绘制函数中调用即可 |
bool DrawGLScene(GLvoid)
{
if (bumps) {
if (useMultitexture && maxTexelUnits>1)
return doMesh2TexelUnits();
else return doMesh1TexelUnits(); }
else return doMeshNoBumps();
}
| 删除OpenGL窗口 |
GLvoid KillGLWindow(GLvoid)
| 创建OpenGL窗口 |
BOOL CreateGLWindow(char* title, int width, int height, int bits, bool fullscreenflag)
| Windows循环 |
LRESULT CALLBACK WndProc( HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
在Windows主函数中加入一些控制键 :
|
if (keys['E'])
{
keys['E']=false;
emboss=!emboss;
}
if (keys['M'])
{
keys['M']=false;
useMultitexture=((!useMultitexture) && multitextureSupported);
}
if (keys['B'])
{
keys['B']=false;
bumps=!bumps;
}
if (keys['F'])
{
keys['F']=false;
filter++;
filter%=3;
}
if (keys[VK_PRIOR])
{
z-=0.02f;
}
if (keys[VK_NEXT])
{
z+=0.02f;
}
if (keys[VK_UP])
{
xspeed-=0.01f;
}
if (keys[VK_DOWN])
{
xspeed+=0.01f;
}
if (keys[VK_RIGHT])
{
yspeed+=0.01f;
}
if (keys[VK_LEFT])
{
yspeed-=0.01f;
}
现在你应该可以熟练的使用凹凸贴图了,如果你想让你的具有凹凸贴图的程序跑起来更快,你应该注意以下几点:
如果你有任何纹理,请联系我或访问我的网站http://www.glhint.de 我必须感谢以下的人:
| < 第21课 | 第23课 > |


