還有兩種紋理生成模式未介紹,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; }