通常狀況下,咱們設置紋理的環境爲GL_MODULATE模式,在這種狀況下,受到光照的幾何圖形會和紋理的顏色進行結合。正常狀況下,OpenGL進行光照計算,並根據標準的光照模型進行單個片斷的顏色計算。而後,再把片斷的顏色乘以紋理的顏色,等到結合後的顏色。可是這樣的話會削弱圖形的光照效果。由於通過光照計算事後的片斷的顏色值最大值是1.0(即最亮的顏色),任何值乘以小於1.0的值,一定小於其自己(即不可能比原來更亮)。(if y <= 1.0 then x * y <= x. x y是正數)。 html
沒有應用紋理以前: 算法
應用紋理以後光照效果被削弱了: 數組
要解決這個問題,咱們能夠在紋理映射以後再應用鏡面光高亮的效果(經過加而不是乘的方式)。這個技巧成爲輔助鏡面光顏色。經過設置光照的模型來達到此目的,函數調用以下: 網絡
glLightModeli(GL_LIGHT_MODEL_COLOR_CONTROL, GL_SEPARATE_SPECULAR_COLOR); 函數
加了這一行以後的效果以下: oop
要切回正常狀態,指定光照模型爲GL_SINGLE_COLOR便可,函數調用以下: 性能
glLightModeli(GL_LIGHT_COLOR_CONTROL, GL_COLOR_SINGLE); 學習
使用沒有開啓光照,咱們能夠手動來設置輔助顏色,經過glSecondarycolor函數調用設置輔助顏色和經過glEnable(GL_COLOR_SUM);來開啓。手動設置的輔助顏色只有在沒有開啓光照的狀況下有做用。 優化
各向異性過濾並不是OpenGL核心API的一部分,但其做爲擴展被普遍用於提高紋理過濾操做的質量。在先前學習的兩個基本的過濾器最鄰近過濾(GL_NEAREST)和線性過濾(GL_LINEAR)。OpenGL使用紋理座標計算獲得紋理將映射到幾何圖形的哪個片斷上。而後經過對該位置周圍的紋理元素以GL_NEAREST過濾或GL_LINEAR過濾方式進行採樣。 ui
當咱們的視角是垂直於該幾何圖形的時候,這樣的方式沒有問題。然而當咱們的視角與幾何圖形造成一個斜角的時候,以常規的方式對周邊紋理進行採樣會丟失一些紋理的信息,它看起來變模糊了。更真實和精確的採樣是,沿着平面傾斜的方向,拉長紋理的採樣。以下的第二個圖:
咱們能夠把各向異性過濾應用去基本的和mipmap方式的紋理過濾模式上。在使用以前咱們須要檢查各向異性過濾擴展是否被支持,使用glTools函數裏的函數:
if(gltIsExtSupported(「GL_EXT_texture_filter_anisotropic」))
若是擴展是被支持的,咱們能夠查到支持各向異性過濾的最大值。經過調用glGetFloatv參數爲GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT。
GLfloat fLargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest);
值越大各向異性過濾的粒度越大,若是值爲1.0就表明普通的紋理過濾。各向異性過濾會帶來必定的開銷。現代的顯卡都已經支持各向異性過濾,並且作了優化。最後咱們經過glTexParameterf函數來設置各向異性過濾的最大值,以下:
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest);
非開啓各向異性過濾的通道效果圖,能夠看到遠處的磚塊較模糊:
開啓各向異性過濾以後的效果圖:
使用紋理的缺陷是紋理須要大量的內存來存儲和處理。在早期咱們會把紋理壓縮成JPG的格式,而後在加載以前(調用glTexImage以前)對其進行解壓。這樣僅僅是節省了磁盤的空間以及加快了在網絡上傳輸紋理的速度,但並無減小對顯存(加載到顯存中仍是原格式那麼大)。
在OpenGL1.3後,OpenGL原生支持了紋理壓縮的特性。在更低的版本中,經過擴展來支持,你能夠經過GL_ARB_texture_compression來檢查是否支持這個擴展。OpenGL對紋理的壓縮不只僅是加載壓縮的紋理,並且在顯卡內存中也是保存着壓縮的紋理。這能夠減小加載紋理時使用的內存以及提高處理紋理的性能(減小了移動紋理和切換紋理的時間,由於要操做的內存空間變小了)。
你能夠經過下表的一個常量做爲glTexImage函數中internalFormat參數的值,來達到壓縮紋理的目的。當紋理沒法被壓縮時,將使用對應的基本內部格式。
壓縮格式 | 基本內部格式 |
GL_COMPRESSED_ALPHA | GL_ALPHA |
GL_COMPRESSED_LUMINANCE | GL_LUMINANCE |
GL_COMPRESSED_LUMINANCE_ALPHA | GL_LUMINANCE_ALPHA |
GL_COMPRESSED_RGB | GL_RGB |
GL_COMPRESSED_RGBA | GL_RGBA |
GL_COMPRESSED_INTENSITY | GL_INTENSITY |
在這種方式下,加載壓縮的圖像會多耗一點時間,但卻提高了處理紋理內存的速度。但你使用這種方式壓縮了紋理以後,你能夠經過glGetTexLevelParameteriv參數爲GL_TEXTURE_COMPRESSED來檢查紋理是否壓縮成功。
GLint compFlag;
glGetTexLevelParameteriv(GL_TEXTURE_2D, 0, GL_TEXTURE_COMPRESSED, &compFlag);
此函數接受的參數以下表:
參數 | 返回值 |
GL_TEXTURE_COMPRESSED | 返回1表明壓縮成功,0表明失敗 |
GL_TEXTURE_COMPRESSED_IMAGE_SIZE | 返回壓縮後紋理的大小(字節爲單位) |
GL_TEXTURE_INTERNAL_FORMAT | 使用的壓縮格式 |
GL_NUM_COMPRESSED_TEXTURE_FORMATS | 支持的壓縮格式的數量 |
GL_COMPRESSED_TEXTURE_FORMATS | 返回一個保存每個被支持的壓縮格式的數組常量 |
GL_TEXTURE_COMPRESSION_HINT | 紋理壓縮的提示值 |
咱們還能夠經過glHint函數來告訴OpenGL咱們要用的是最快的壓縮算法仍是最高質量的壓縮算法。經過使用GL_NUM_COMPRESSED_TEXTURE_FORMATS和GL_COMPRESSED_TEXTURE_FORMATS來得到被支持的壓縮格式的列表。幾乎全部的OpenGl實現都支持GL_EXT_texture_compression_s3tc紋理壓縮格式,若是這個擴展被支持那下面表格的全部格式都是支持的(僅適用於2維紋理)
格式 | 描述 |
GL_COMPRESSED_RGB_S3TC_DXT1 | RGB數據被壓縮。alpha爲1.0 |
GL_COMPRESSED_RGBA_S3TC_DXT1 | RGB數據被壓縮。alpha值爲1.0或0.0 |
GL_COMPRESSED_RGBA_S3TC_DXT3 | RGB數據被壓縮。alpha值用4位存儲 |
GL_COMPRESSED_RGBA_S3TC_DXT5 | RGB數據被壓縮。alpha爲一些8位值的加權平均值 |
在前面咱們已經介紹了,如何壓縮紋理數據。而後咱們能夠經過glGetCompressedTexImage(與glGetTexImage獲取未壓縮數據同樣)來獲取被壓縮的數據,並把它存到硬盤上。在隨後的加載中,直接加載已經壓縮過的紋理數據會更快。此技術徹底依賴於硬件的實現。
加載已經預先壓縮過的紋理數據,能夠調用下面的函數:
void glCompressedTexImage1D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLint border, GLsizei imageSize, void *data);
void glCompressedTexImage2D(GLenum target, GLint level, GLenum internalFormat, GLsizei width, GLsizei height, GLint border, GLsizei imageSize, void *data);
void glCompressedTexImage3D(GLenum target, GLint level, GLenum internalFormat, GLint width, GLint height, GLint depth, GLint border, GLint imageSize, void *data);
這個方法與glTexImage幾乎是同樣的,不同的是其internalFormat必須是壓縮的格式。若是實現支持GL_EXT_texture_compression_s3tc擴展,那麼其參數值就能夠是上面的表格列出的值。固然也有glCompressedTexSubImage函數來更新部分已加載的壓縮過的紋理數據,就像glTexSubImage同樣。
紋理壓縮時很是流行的特性。更小的紋理意味着更快的加載速度,更快地在網上傳輸,更快地拷貝到顯卡中,能夠加載更多的紋理。下面作了個簡單的實驗:
不壓縮和壓縮後的圖片大小的對比,壓縮前是196kb左右,壓縮後只有32kb了:
GLint flag;在前面咱們學習過使用紋理座標來把紋理映射到幾何圖形上。在球體和平滑的平面上手動指定紋理座標是簡單的。可是遇到了複雜的表面,咱們要爲其指定紋理座標就有寫困難了。OpenGL提供了自動生成紋理座標的特性來解決這個問題。
經過glEnable來開啓S,T,R和Q的紋理座標自動生成:
glEnable(GL_TEXTURE_GEN_S);
glEnable(GL_TEXTURE_GEN_T);
glEnable(GL_TEXTURE_GEN_R);
glEnable(GL_TEXTURE_GEN_Q);
當自動生成紋理座標的功能被開啓,那麼glTexCoord的函數調用將被忽略。OpenGL爲自動爲每個頂點計算紋理座標。咱們能夠經過相應的glDisable來關閉紋理座標的自動生成。
咱們能夠經過下面的兩個函數來設置自動生成紋理座標的方法:
void glTexGenf(GLenum coord, GLenum pname, GLfloat param);
void glTexGenfv(GLenum coord, GLenum pname, GLfloat *param);
第一個參數指定了紋理座標軸,能夠是GL_S,GL_T,GL_R或GL_Q。第二個參數必須是GL_TEXTURE_SPHERE,GL_OBJECT_PLANE或GL_EYE_PLANE.最後一個參數設置紋理生成的方法或模式。glTexGen也有相應的GLint和GLdouble模式。
下面是TEXGEN示例:
#include "gltools.h" #include <stdio.h> #define ENV 0 #define STRIPES 1 #define TEXTURENUM 2 const char* texFileName[] = {"..\\Environment.tga","..\\stripes.tga"}; static GLuint textureName[TEXTURENUM]; static GLfloat yRot = 0.0f; static GLfloat zPos = -2.0f; static GLint iRenderMode = 3; void ProcessMenu(int value) { //投影平面 GLfloat zPlane[] = {0.0f, 0.0f, 1.0f, 0.0f}; //渲染模式 iRenderMode = value; switch(value) { case 1: //物體線性 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR); glTexGenfv(GL_S, GL_OBJECT_PLANE, zPlane); glTexGenfv(GL_T, GL_OBJECT_PLANE, zPlane); break; case 2: //視覺線性 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR); glTexGenfv(GL_S, GL_EYE_PLANE, zPlane); glTexGenfv(GL_T, GL_EYE_PLANE, zPlane); break; case 3: default: //球體貼圖 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); } glutPostRedisplay(); } void SetupRC() { glClearColor(0.0f, 0.0f, 0.0f, 1.0f); glEnable(GL_DEPTH_TEST); GLint iWidth, iHeight, iComponents; GLenum eFormat; //設置紋理環境 glTexEnvi(GL_TEXTURE_2D, GL_TEXTURE_ENV, GL_REPLACE); //生成紋理名稱 glGenTextures(TEXTURENUM, textureName); for (int i = 0; i < TEXTURENUM; ++i) { //加載紋理圖像 void *pImage = gltLoadTGA(texFileName[i], &iWidth, &iHeight, &iComponents, &eFormat); if (pImage) { //綁定紋理 glBindTexture(GL_TEXTURE_2D, textureName[i]); //構建mimap gluBuild2DMipmaps(GL_TEXTURE_2D, iComponents, iWidth, iHeight, eFormat, GL_UNSIGNED_BYTE, pImage); //設置紋理參數 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_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } free(pImage); } if (gltIsExtSupported("GL_EXT_texture_filter_anisotropic")) { GLfloat fLargest; glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &fLargest); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, fLargest); } glEnable(GL_TEXTURE_2D); //設置爲球體貼圖 glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP); //開啓S、T座標的紋理座標生成 glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); } void ShutdownRC() { glDeleteTextures(TEXTURENUM, textureName); } void RenderScene() { glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glMatrixMode(GL_PROJECTION); //背景圖,使用正交投影 glPushMatrix(); glLoadIdentity(); gluOrtho2D(0.0, 1.0, 0.0, 1.0); glDepthMask(GL_FALSE); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glBindTexture(GL_TEXTURE_2D, textureName[ENV]); //關閉紋理座標的生成 glDisable(GL_TEXTURE_GEN_S); glDisable(GL_TEXTURE_GEN_T); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex2f(0.0f, 0.0f); glTexCoord2f(1.0f, 0.0f); glVertex2f(1.0f, 0.0f); glTexCoord2f(1.0f, 1.0f); glVertex2f(1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex2f(0.0f, 1.0f); glEnd(); //還原投影矩陣 glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); glEnable(GL_TEXTURE_GEN_S); glEnable(GL_TEXTURE_GEN_T); glDepthMask(GL_TRUE); if (iRenderMode != 3) { glBindTexture(GL_TEXTURE_2D, textureName[STRIPES]); } glPushMatrix(); glTranslatef(0.0f, 0.0f, zPos); glRotatef(yRot, 0.0f, 1.0f, 0.0f); gltDrawTorus(0.35, 0.15, 61, 37); glPopMatrix(); glutSwapBuffers(); } void ChangeSize(GLsizei w, GLsizei h) { if (h == 1) h = 0; glViewport(0, 0, w, h); GLfloat aspect = (GLfloat)w/(GLfloat)h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(35.5, aspect, 1.0, 150.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glutPostRedisplay(); } void SpecialKey(int value, int x, int y) { if (value == GLUT_KEY_LEFT) { yRot += 0.5f; } if (value == GLUT_KEY_RIGHT) { yRot -= 0.5f; } if (value == GLUT_KEY_UP) { zPos += 0.5f; } if (value == GLUT_KEY_DOWN) { zPos -= 0.5f; } if (yRot > 365.5f) { yRot = 0.0f; } glutPostRedisplay(); } int main(int arg, char **argv) { glutInit(&arg, argv); glutInitDisplayMode(GL_RGB | GL_DOUBLE | GL_DEPTH); glutInitWindowSize(800, 600); glutCreateWindow("TEXGEN"); glutReshapeFunc(ChangeSize); glutDisplayFunc(RenderScene); glutSpecialFunc(SpecialKey); glutCreateMenu(ProcessMenu); glutAddMenuEntry("Object Linear", 1); glutAddMenuEntry("Eye linear", 2); glutAddMenuEntry("sphere map", 3); glutAttachMenu(GLUT_RIGHT_BUTTON); SetupRC(); glutMainLoop(); ShutdownRC(); return 0; }
當設置紋理生成的模式爲GL_OBJECT_LINEAR的時候,紋理座標生成使用的公式以下:
coord = P1*X + P2*Y + P3*Z + P4*W
其中X,Y,Z,W是被映射物體的頂點座標值,P1-P4是平面方程的係數。紋理座標是今後平面透視投影到幾何圖形上的。例如,爲了從平面Z=0上投影紋理座標S和T咱們可使用下面的代碼:
//投影平面
GLfloat zPlane[] = {0.0f, 0.0f, 1.0f, 0.0f};......//物體線性
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_OBJECT_LINEAR);glTexGenfv(GL_S, GL_OBJECT_PLANE, zPlane);glTexGenfv(GL_T, GL_OBJECT_PLANE, zPlane);
注意每一個座標均可以用不一樣的平面方程來生成紋理座標,咱們這裏把S和T座標的平面方程設置成同樣的。在這裏使用了物體線性的模式,無論你怎麼調整這個圓環,紋理老是固定在幾何圖元上的。效果以下:
當選擇視覺線性模式是,紋理座標的生成方程與物體線性模式是類似的。不一樣的是如今的X,Y,Z和W值表明着視點的紋理(照相機或眼睛的位置)。平面方程的那些係數也要反轉過來。事實上如今全部東西都用視覺座標來表示了。代碼以下:
//投影平面
GLfloat zPlane[] = {0.0f, 0.0f, 1.0f, 0.0f};......//視覺線性
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_EYE_LINEAR);glTexGenfv(GL_S, GL_EYE_PLANE, zPlane);glTexGenfv(GL_T, GL_EYE_PLANE, zPlane);
效果以下,紋理會隨着你視角的旋轉而改變了:
當紋理生成模式設置爲GL_SPHERE_MAP的時候,OpenGL生成座標的方式是物體呈現着當前紋理的倒影。想象一下魚眼睛的效果。示例中設置球體映射模式的代碼以下:
glTexGeni(GL_S, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
glTexGeni(GL_T, GL_TEXTURE_GEN_MODE, GL_SPHERE_MAP);
效果以下:
爲了得到更爲逼真的效果,使用立方體映射。但球體映射仍是有必定用途的,由於它只要求1個紋理開銷較小,而立方體映射則要6個紋理,若是你不須要真正的反射,球體映射能夠知足你的要求了。