还有两种纹理生成模式未介绍,GL_REFLECTION_MAP和GL_NORMAL_MAP,这两种模式须要用到新的纹理目标:立方体贴图。一个立方体贴图被当作一个纹理来看待,它由六个正方形的2D图像(必须是正方形)来组成立方体的六个面。下图展现了cubemap示例的立方体的六个面:html
这六个面分别是-X,+X,-Y,+Y,-Z,+Z.而后咱们使用GL_REFLECTION_MAP的模式来生成纹理,可以制造一个真实的表面的倒影。git
立方体贴图有六个新的值做为参数传给glTexImage2D:github
GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X,数组
GL_TEXTURE_CUBE_MAP_POSITIVE_Y,GL_TEXTURE_CUBE_MAP_NEGATIVE_Y,函数
GL_TEXTURE_CUBE_MAP_POSITIVE_Z,GL_TEXTURE_CUBE_MAP_NEGATIVE_Z.oop
例如咱们要加载正X方向的贴图,以下所示:ui
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X, 0, GL_RGBA, iWidth, iHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE, pImage);spa
在cubemap示例中,咱们把立方体的六个图像的路径保存到一个数组中,而后循环加载这六个面的纹理贴图:.net
// 纹理文件目录 static const char * szCubeFile[] = { " ..\\pos_x.tga " , " ..\\neg_x.tga " , " ..\\pos_y.tga " , " ..\\neg_y.tga " , " ..\\pos_z.tga " , " ..\\neg_z.tga " }; // 立方体纹理 static GLenum cube[] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z}; ..... ..... GLint iWidth, iHeight, iComponents; GLenum eFormat; // 设置纹理环境 glTexEnvi(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_ENV, GL_REPLACE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); for ( int i = 0 ; i < 6 ; ++ i) { glPixelStorei(GL_UNPACK_ALIGNMENT, 1); void* pImage = gltLoadTGA(szCubeFile[i], & iWidth, & iHeight, & iComponents, & eFormat); if(pImage) { glTexImage2D(cube[i], 0 , iComponents, iWidth, iHeight, 0 , eFormat, GL_UNSIGNED_BYTE, pImage); free(pImage); } }
而后咱们须要启用立方体贴图:glEnable(GL_TEXTURE_CUBE_MAP);若是GL_TEXTURE_CUBE_MAP和GL_TEXTURE_2D同时开启的话,GL_TEXTURE_CUBE_MAP优先起做用。PS:glTexParameter同时对这立方体的六张图像起做用,这六张图像在一个立方体纹理中。3d
立方体贴图的纹理坐标与通常的3D纹理坐标不一样,S,T和R纹理坐标表明着一个从立方体贴图中心出发的有符号向量,这个向量会与立方体的六个面中的一个相交,而后对相交的纹理单元进行采样。
立方体贴图经常使用于建立一个对周围景象进行反射的物体。下面的代码建立一个天空盒,一个有效的天空盒包含了六个面,从盒子的中心出发朝六个方向看去能够看到六张图像。天空盒是有六个正方形构成的,每一个面使用glTexCoord3f来手工设置纹理坐标。
glPushMatrix(); //手动指定纹理坐标 glBegin(GL_QUADS); //-x glTexCoord3f(-1.0f, 1.0f, -1.0f); glVertex3f(-fExtent, fExtent, -fExtent); glTexCoord3f(-1.0f, 1.0f, 1.0f); glVertex3f(-fExtent, fExtent, fExtent); glTexCoord3f(-1.0f, -1.0f, 1.0f); glVertex3f(-fExtent, -fExtent, fExtent); glTexCoord3f(-1.0f, -1.0f, -1.0f); glVertex3f(-fExtent, -fExtent, -fExtent); //+x glTexCoord3f(1.0f, -1.0f, -1.0f); glVertex3f(fExtent, -fExtent, -fExtent); glTexCoord3f(1.0f, -1.0f, 1.0f); glVertex3f(fExtent, -fExtent, fExtent); glTexCoord3f(1.0f, 1.0f, 1.0f); glVertex3f(fExtent, fExtent, fExtent); glTexCoord3f(1.0f, 1.0f, -1.0f); glVertex3f(fExtent, fExtent, -fExtent); //+y glTexCoord3f(-1.0f, 1.0f, -1.0f); glVertex3f(-fExtent, fExtent, -fExtent); glTexCoord3f(-1.0f, 1.0f, 1.0f); glVertex3f(-fExtent, fExtent, fExtent); glTexCoord3f(1.0f, 1.0f, 1.0f); glVertex3f(fExtent, fExtent, fExtent); glTexCoord3f(1.0f, 1.0f, -1.0f); glVertex3f(fExtent, fExtent, -fExtent); //-y glTexCoord3f(1.0f, -1.0f, -1.0f); glVertex3f(fExtent, -fExtent, -fExtent); glTexCoord3f(1.0f, -1.0f, 1.0f); glVertex3f(fExtent, -fExtent, fExtent); glTexCoord3f(-1.0f, -1.0f, 1.0f); glVertex3f(-fExtent, -fExtent, fExtent); glTexCoord3f(-1.0f, -1.0f, -1.0f); glVertex3f(-fExtent, -fExtent, -fExtent); //-z glTexCoord3f(-1.0f, -1.0f, -1.0f); glVertex3f(-fExtent, -fExtent, -fExtent); glTexCoord3f(1.0f, -1.0f, -1.0f); glVertex3f(fExtent, -fExtent, -fExtent); glTexCoord3f(1.0f, 1.0f, -1.0f); glVertex3f(fExtent, fExtent, -fExtent); glTexCoord3f(-1.0f, 1.0f, -1.0f); glVertex3f(-fExtent, fExtent, -fExtent); //+z glTexCoord3f(-1.0f, 1.0f, 1.0f); glVertex3f(-fExtent, fExtent, fExtent); glTexCoord3f(1.0f, 1.0f, 1.0f); glVertex3f(fExtent, fExtent, fExtent); glTexCoord3f(1.0f, -1.0f, 1.0f); glVertex3f(fExtent, -fExtent, fExtent); glTexCoord3f(-1.0f, -1.0f, 1.0f); glVertex3f(-fExtent, -fExtent, fExtent); glEnd(); glPopMatrix();
为了手工设置纹理坐标,在画天空盒以前咱们须要关闭纹理坐标自动生成的功能。
glDisable(GL_TEXTURE_GEN_S);
glDisable(GL_TEXTURE_GEN_T);
glDisable(GL_TEXTURE_GEN_R);
DrawSkyBox();
而后画一个反射周围环境的球体,开启纹理坐标自动生成:
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP);
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
为了制造更加真实的反射效果,咱们须要考虑照相机的方向。咱们能够从照相机类中得到照相机的旋转矩阵,并进行反转,而后在应用立方体纹理贴图以前,先把它乘以纹理矩阵。没有旋转纹理坐标,立方体贴图将没法正确地反射周围的环境(反射的是固定的图像不随着照相机的旋转而变化)。由于gltDrawSphere函数并不影响模型视图矩阵,因此咱们可使矩阵模式为GL_TEXTURE(纹理模式)直到咱们画完球体后再恢复到原始状态。代码以下:
//绘制球体 glPushMatrix(); //切换到纹理矩阵 glMatrixMode(GL_TEXTURE); glPushMatrix(); M3DMatrix44f m,invert; //获取照相机的位置,并进行反转,造成正确的反射纹理 camra.GetCameraOrientation(m); m3dInvertMatrix44(invert,m); glMultMatrixf(invert); gltDrawSphere(0.75f, 41, 41); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopMatrix(); glutSwapBuffers();
效果以下(移动照相机,并旋转能够看到不一样的反射面)(code):
现代的OpenGL硬件实现都支持把多个纹理应用到几何图形上。咱们能够经过GL_MAX_TEXTURE_UNITS检查有多少个纹理单元是可用的:
GLint iUnits;
glGetIntergv(GL_MAX_TETURE_UNITS, &iUnits);
可用的纹理为从基础的纹理单元(GL_TEXTURE0)到最大的纹理单元(GL_TEXTUREn)(这里的n表明纹理单元的下标)。每一个纹理单元有本身的纹理环境状态(即每一个纹理单元均可经过glTexEnv设置本身的纹理环境),纹理坐标生成状态(glTexGen),纹理矩阵状态,纹理的启用状态和纹理过滤器等。
纹理结合的过程大体以下:
首先从图形中获得了片断的颜色值做为输入,而后和被应用到图元上的第一个纹理上对应的颜色值进行结合做为输出。把以前的输出做为输入,再与第二个纹理的颜色进行结合,如此循环到最后一个被启用的纹理单元为止。
默认状况下,第一个纹理单元是激活的纹理单元。除了glTexCoord以外的全部纹理命令,都只影响当前激活的纹理单元。咱们能够经过调用glActiveTexture参数为GL_TEXTUREn来激活相应的纹理单元(纹理的下标是从0开始)。例如:咱们激活第二个纹理,并启用2D纹理:
glActiveTexture(GL_TEXTURE1);
glEnable(GL_TEXTURE_2D);
相反地禁用则以下:
glDisable(GL_TEXTURE_2D);
glActiveTexutre(GL_TEXTURE0);
全部的纹理函数调用glTexParameter, glTexEnv,glTexGen,glTexImage和glBindTexture都只对当前激活的纹理单元有效。当图形被渲染时,被启用的纹理单元将被应用。
当咱们使用glTexCoord指定纹理坐标的时候,这个纹理坐标是针对GL_TEXTURE0设置的。若是咱们想为其余的纹理单元设置纹理坐标能够经过glMultiTexCoord来设置:
glMultiTexCoord1f(GLenum texUnit, GLfloat s);
glMultiTexCoord2f(Glenum texUnit, GLfloat s, GLfloat t);
glMultiTexCoord3f(GLenum texUnit, GLfloat s, GLfloat t, GLfloat r);
其中texUnit为GL_TEXTUREn. 也有相应的不一样类型的版本。固然咱们也能够用自动生成纹理坐标的方式。
在以前的CUBEMAP例子的基础上作一些更改,咱们把cubemap的纹理做为第二个纹理即GL_TEXTURE1。第一个纹理是有污点的纹理。而后,让立方体贴图纹理和这个污点纹理相乘,就能获得以下的效果:
完整代码示例以下:
#include "gltools.h"#include "math3d.h"#include "glframe.h"#define COLORMAP 0#define CUBEMAP 1#define TEXTURENUM 2//纹理对象GLuint textureObj[TEXTURENUM] = {0,0};//纹理路径数组static const char *szCubeFile[] = {"..\\pos_x.tga", "..\\neg_x.tga","..\\pos_y.tga", "..\\neg_y.tga","..\\pos_z.tga", "..\\neg_z.tga"};//立方体贴图static GLenum cube[] = {GL_TEXTURE_CUBE_MAP_POSITIVE_X, GL_TEXTURE_CUBE_MAP_NEGATIVE_X, GL_TEXTURE_CUBE_MAP_POSITIVE_Y, GL_TEXTURE_CUBE_MAP_NEGATIVE_Y, GL_TEXTURE_CUBE_MAP_POSITIVE_Z, GL_TEXTURE_CUBE_MAP_NEGATIVE_Z};static GLfloat fExtent = 10.0f;static GLFrame camra;void DrawSkyBox() { glPushMatrix(); glBegin(GL_QUADS); //-x glMultiTexCoord3f(GL_TEXTURE1, -1.0f, 1.0f, -1.0f); glVertex3f(-fExtent, fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, 1.0f, 1.0f); glVertex3f(-fExtent, fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, -1.0f, 1.0f); glVertex3f(-fExtent, -fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, -1.0f, -1.0f); glVertex3f(-fExtent, -fExtent, -fExtent); //+x glMultiTexCoord3f(GL_TEXTURE1, 1.0f, -1.0f, -1.0f); glVertex3f(fExtent, -fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, -1.0f, 1.0f); glVertex3f(fExtent, -fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, 1.0f, 1.0f); glVertex3f(fExtent, fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, 1.0f, -1.0f); glVertex3f(fExtent, fExtent, -fExtent); //+y glMultiTexCoord3f(GL_TEXTURE1, -1.0f, 1.0f, -1.0f); glVertex3f(-fExtent, fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, 1.0f, 1.0f); glVertex3f(-fExtent, fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, 1.0f, 1.0f); glVertex3f(fExtent, fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, 1.0f, -1.0f); glVertex3f(fExtent, fExtent, -fExtent); //-y glMultiTexCoord3f(GL_TEXTURE1, 1.0f, -1.0f, -1.0f); glVertex3f(fExtent, -fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, -1.0f, 1.0f); glVertex3f(fExtent, -fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, -1.0f, 1.0f); glVertex3f(-fExtent, -fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, -1.0f, -1.0f); glVertex3f(-fExtent, -fExtent, -fExtent); //-z glMultiTexCoord3f(GL_TEXTURE1, -1.0f, -1.0f, -1.0f); glVertex3f(-fExtent, -fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, -1.0f, -1.0f); glVertex3f(fExtent, -fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, 1.0f, -1.0f); glVertex3f(fExtent, fExtent, -fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, 1.0f, -1.0f); glVertex3f(-fExtent, fExtent, -fExtent); //+z glMultiTexCoord3f(GL_TEXTURE1, -1.0f, 1.0f, 1.0f); glVertex3f(-fExtent, fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, 1.0f, 1.0f); glVertex3f(fExtent, fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, 1.0f, -1.0f, 1.0f); glVertex3f(fExtent, -fExtent, fExtent); glMultiTexCoord3f(GL_TEXTURE1, -1.0f, -1.0f, 1.0f); glVertex3f(-fExtent, -fExtent, fExtent); glEnd(); glPopMatrix(); }void SetupRC() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); GLint iWidth, iHeight, iComponents; GLenum eFormat; glGenTextures(TEXTURENUM, textureObj); //设置立方体纹理对象状态 glBindTexture(GL_TEXTURE_CUBE_MAP, textureObj[CUBEMAP]); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_MAG_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_WRAP_R, GL_CLAMP_TO_EDGE); for (int i = 0; i < 6; ++i) { glTexParameteri(GL_TEXTURE_CUBE_MAP, GL_GENERATE_MIPMAP, GL_TRUE); glPixelStorei(GL_UNPACK_ALIGNMENT, 1);void *pImage = gltLoadTGA(szCubeFile[i], &iWidth, &iHeight, &iComponents, &eFormat);if (pImage) { glTexImage2D(cube[i], 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pImage); free(pImage); } } //设置污点纹理对象状态 glBindTexture(GL_TEXTURE_2D, textureObj[COLORMAP]); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); void *pImage = gltLoadTGA("..\\tarnish.tga", &iWidth, &iHeight, &iComponents, &eFormat); if (pImage) { glTexImage2D(GL_TEXTURE_2D, 0, iComponents, iWidth, iHeight, 0, eFormat, GL_UNSIGNED_BYTE, pImage); free(pImage); } //激活纹理单元0,并启用2D纹理,设置它的纹理和纹理环境, glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, textureObj[COLORMAP]); glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV, GL_DECAL); //激活纹理单元1,启用CUBEMAP,并设置它的纹理和纹理环境,纹理生成模式 glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_CUBE_MAP); glBindTexture(GL_TEXTURE_CUBE_MAP, textureObj[CUBEMAP]); glTexEnvi(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_ENV, GL_MODULATE); //设置自动生成纹理坐标的方式为投影 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); glTexGeni(GL_R, GL_TEXTURE_GEN_MODE, GL_REFLECTION_MAP); camra.MoveForward(-5.0f); }void RenderScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); camra.ApplyCameraTransform(); //先关闭纹理单元0的2D纹理 glActiveTexture(GL_TEXTURE0); glDisable(GL_TEXTURE_2D); //选择纹理单元1并启用立方体贴图 glActiveTexture(GL_TEXTURE1); glEnable(GL_TEXTURE_CUBE_MAP); //天空的纹理坐标手工设置 glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glDisable(GL_TEXTURE_GEN_R); glTexEnvi(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_ENV, GL_DECAL); DrawSkyBox(); //开启纹理坐标自动生成 glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glEnable(GL_TEXTURE_GEN_R); glTexEnvi(GL_TEXTURE_CUBE_MAP, GL_TEXTURE_ENV, GL_MODULATE); //绘制球体,激活纹理0和纹理1进行结合 glActiveTexture(GL_TEXTURE0); glEnable(GL_TEXTURE_2D); glPushMatrix(); //注意每一个纹理单元都有本身的纹理矩阵,这里咱们操做纹理1,立方体贴图 glActiveTexture(GL_TEXTURE1); glMatrixMode(GL_TEXTURE); glPushMatrix(); M3DMatrix44f m,invert; //获取照相机的位置,并进行反转,造成反射的纹理 camra.GetCameraOrientation(m); m3dInvertMatrix44(invert,m); glMultMatrixf(invert); gltDrawSphere(0.75f, 41, 41); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glPopMatrix(); glPopMatrix(); glutSwapBuffers(); }void ShutdownRC() { glDeleteTextures(TEXTURENUM, textureObj); }void ChangeSize(GLsizei w, GLsizei h) { if (h == 0) h = 1; glViewport(0, 0, w, h); GLfloat aspect = (GLfloat)w/(GLfloat)h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(35.0, aspect, 1.0, 100.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glutPostRedisplay(); }void SpecialKey(int value, int x, int y) { if (value == GLUT_KEY_LEFT) { camra.RotateLocalY(-0.1f); } if (value == GLUT_KEY_RIGHT) { camra.RotateLocalY(0.1f); } if (value == GLUT_KEY_UP) { camra.MoveForward(0.1f); } if (value == GLUT_KEY_DOWN) { camra.MoveForward(-0.1f); } glutPostRedisplay(); }int main(int args, char *argv[]) { glutInit(&args, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA | GLUT_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow("mutiltex"); glutDisplayFunc(RenderScene); glutReshapeFunc(ChangeSize); SetupRC(); glutSpecialFunc(SpecialKey); glutMainLoop(); ShutdownRC(); return 0; }