OpenGL是如何把紋理元素的顏色和幾何圖元的顏色結合起來的?是經過紋理環境的模式來控制。設置紋理環境模式的函數以下: 函數
void glTexEnvi(GLenum target, GLenum pname, GLint param); oop
void glTexEnvf(GLenum target, GLenum pname, GLfloat param); 測試
void glTexEnviv(GLenum target, GLenum pname, GLint *param); spa
void glTexEnvfv(GLenum target, GLenum pname, GLfloat *param); .net
這個函數有許多的選項,一些高級的選項會在下一節進行介紹。在前面的金字塔例子中,咱們在把紋理映射到幾何圖元以前,設置環境模式爲GL_MODULATE。 設計
GL_MODULATE模式是把紋理元素的顏色乘以幾何圖元(進行光照計算以後)的顏色。經過這種模式,咱們能夠用彩色的幾何圖形與紋理結合來產生不一樣的效果。 3d
咱們還能夠用GL_REPLACE模式簡單地覆蓋掉紋理下面的結合圖形的顏色。這樣片斷的顏色值將直接採用紋理的顏色。這樣就消除了幾何圖形對紋理的影響。若是紋理有alpha通道,咱們還能夠開啓混合效果,來建立透明的紋理。 code
還有一種貼紙模式參數爲GL_DECAL。當紋理沒有alpah通道的時候,其效果和GL_REPLACE是同樣的。若是有alpah通道,那麼將會把紋理元素的顏色和紋理下面片斷的顏色進行alpha混合。 orm
紋理還可使用GL_BLEND環境模式,來與一個常量值進行混合。在設置這個模式的時候,必須設置紋理環境的顏色。 blog
GLfloat fColor[4] = {1.0f, 0.0f, 0.0f, 0.0f};
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_BLEND);
glTexEnvfv(GL_TEXTURE_ENV, GL_TEXTURE_ENV_COLOR, fColor);
還有一種GL_ADD模式,簡單地把紋理元素的顏色與下面的片斷顏色進行相加,超過1.0的調整爲1.0. 這樣會使得不少地方看起來是白色的或接近白色。
在紋理映射的過程當中,有許多參數會影響渲染的方式和效果。能夠經過下面的四個函數來設置紋理參數:
void glTexParameterf(GLenum target, GLenum pname, GLfloat param);
void glTexParameteri(GLenum target, GLenum pname, GLint param);
void glTexParameterfv(GLenum target, GLenum pname, GLfloat *param);
void glTexParameteriv(GLenum target, GLenum pname, GLint *param);
target參數能夠是GL_TEXTURE_1D,GL_TEXTURE_2D,GL_TEXTURE_3D.第二個參數pname是告訴OpenGL哪一個參數被設置。最後一個參數是紋理的參數值。
不像像素圖渲染到顏色緩衝區同樣,在紋理映射過程當中,紋理元素與屏幕上的像素幾乎不是一一對應的。通常狀況下,在把紋理映射到幾何圖形的表面上時,咱們須要對其進行拉伸或收縮。
根據一個紋理貼圖的拉伸或收縮來計算顏色片斷的過程稱爲紋理過濾。OpenGL中有放大過濾器和縮小過濾器。這兩種過濾器的pname參數分別爲GL_TEXTURE_MAG_FILTER,GL_TEXTURE_MIN_FILTER。目前爲止,咱們使用GL_NEAREST(最鄰近) 和 GL_LINEAR(線性)兩種過濾方式。注意:你須要爲GL_TEXTURE_MIN_FILTER選擇其中一種,由於在默認的過濾器不能用於Mipmap。
最鄰近過濾方法是一種最簡單、最快速的過濾方式。無論紋理座標落入哪一個紋理單元,這個紋理單元的顏色就做爲這個片斷的紋理顏色。最鄰近過濾的方式雖然快,但會走樣特別是在紋理被拉伸到特別大的時候有大片的斑駁狀像素。設置最鄰近的方法:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
線性過濾比最鄰近過濾作更多的工做,但它的效果更好。並且在現代的硬件下,這種開銷能夠忽略不計。線性過濾會取周圍的紋理單元進行加權平均獲得當前紋理單元的顏色。線性過濾的特徵是當紋理被放大時,會產生「模糊」的效果。設置線性過濾:
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
在紋理映射中,紋理座標的範圍爲[0.0,1.0]。當紋理座標超出這個值時,OpenGL會根據你設置的環繞模式來處理這種狀況。咱們能夠分別爲每一個紋理座標軸(s,t,r)設置一個環繞模式,經過glTexParameteri 第二個參數爲GL_TEXTURE_WRAP_S,GL_TEXTURE_WRAP_T或者GL_TEXTURE_WRAP_R.環繞的模式有GL_REPEAT,GL_CLAMP,GL_CLAMP_TO_EDGE, GL_WRAP_TO_BORDER.
GL_REPEAT環繞模式只是簡單的重複紋理。例如:1.1出的紋理單元與0.1處的紋理單元是相同的。在平鋪式的紋理應用到大型幾何圖形的時候,很是有用。一個設計良好的無縫小型紋理緊挨着平鋪到大型幾何圖形上看起來像是無縫的大型紋理。
GL_CLAMP環繞模式是超過1.0的截取爲1.0。
若是環繞紋理的意義是是否對紋理進行重複那麼使用GL_REPEAT和GL_CLAMP兩種模式就足夠了。然而紋理的環繞模式,會影響到如何在紋理貼圖邊緣進行紋理過濾。對於GL_NEAREST過濾方式來說,紋理的環繞模式對它並無什麼影響,由於紋理座標老是對應到紋理貼圖上的某一個單元。但對GL_LINEAR模式來講,紋理的環繞模式會影響它對紋理邊緣的處理效果。由於它是對周圍的紋理元素做一個平均值。
對於GL_CLAMP截取型的環繞模式,還提供了一些選項來處理紋理邊緣。GL_CLAMP_TO_EDGE環繞模式簡單地忽略邊緣的紋理採樣,不把它們包括在平均值中。GL_CLAMP_TO_BORDER環繞模式在紋理座標在0.0到1.0範圍以外時只使用邊界紋理單元。邊界紋理單元是做爲圍繞基本圖像的額外的行和列,與基本紋理圖像一塊兒加載的。
下面引用:http://blog.csdn.net/lixiang996/article/details/6859575 的文章片斷來解釋
下圖顯示不一樣參數的效果:
將1張2*2的紋理(分別爲紅,綠,藍,黑),貼到一個方塊左下角。
方塊四個點的紋理座標依次指定爲(0,0), (2,0), (2,2), (0,2)。
根據上述的規則,座標將進行截取,截取到相應的範圍。
咱們繪製暗白色線將紋理座標爲1的地方在圖中標註出來:
注:border顏色默認爲黑色
濾鏡:GL_NEAREST 纏繞模式:GL_CLAMP 或 GL_CLAMP_TO_EDGE
濾鏡:GL_NEAREST 纏繞模式:GL_CLAMP_TO_BORDER。
濾鏡:GL_LINEAR 纏繞模式:GL_CLAMP。
濾鏡:GL_NEAREST 纏繞模式:GL_CLAMP_TO_EDGE。
濾鏡:GL_NEAREST 纏繞模式:GL_CLAMP_TO_BORDER。
Toon-shading(也叫單元格着色)使用一維貼圖做爲一個查找顏色的查找表格去找到其中一個固定的顏色(使用GL_NEAREST)來填充幾何圖形。
經過幾何圖形的表面法線與指向光源的向量來計算光源照射到模型表面的強度。表面法線與指向光源向量的點擊取值範圍爲0.0到1.0。以這個值做爲頂點紋理座標,給這個頂點找到對應的紋理元素。下面是一個使用一維紋理來製造卡通效果的例子:
#include "gltools.h" #include "math3d.h" #include <math.h> //指向光線的向量 M3DVector3f vLightDir = {-1.0f, 1.0f, 1.0f}; void SetupRC() { //一維紋理,逐漸加深的紅色 GLbyte toonMap[4][3] = {{32, 0, 0}, {64, 0, 0}, {128, 0, 0}, {192, 0, 0}}; glClearColor(0.0f, 0.0f, 0.5f, 1.0f); //開啓隱藏面裁剪和深度測試 glCullFace(GL_BACK); glFrontFace(GL_CCW); glEnable(GL_CULL_FACE); glEnable(GL_DEPTH_TEST); //設置紋理參數和紋理環境 glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_1D, GL_TEXTURE_WRAP_S, GL_CLAMP); glTexEnvf(GL_TEXTURE_1D, GL_TEXTURE_ENV_MODE, GL_DECAL); //加載紋理 glPixelStorei(GL_UNPACK_ALIGNMENT, 1); glTexImage1D(GL_TEXTURE_1D, 0, GL_RGB, 4, 0, GL_RGB, GL_UNSIGNED_BYTE, toonMap); glEnable(GL_TEXTURE_1D); } //畫甜甜圈 void DrawDoughnut(GLfloat majorRadius, GLfloat minorRadius, GLint numMajor, GLint numMinor, M3DVector3f vLightDir) { M3DMatrix44f mInvertLight; M3DMatrix44f mModelView; M3DVector3f vNewLight; M3DVector3f vNormal; glGetFloatv(GL_MODELVIEW_MATRIX, mModelView); double majorStep = 2.0 * M3D_PI / numMajor; double minorStep = 2.0 * M3D_PI / numMinor; //把光變換到物體座標空間中,即乘以反轉的模型視圖矩陣 m3dInvertMatrix44(mInvertLight, mModelView); m3dTransformVector3(vNewLight, vLightDir, mInvertLight); vNewLight[0] -= mInvertLight[12]; vNewLight[1] -= mInvertLight[13]; vNewLight[2] -= mInvertLight[14]; m3dNormalizeVector(vNewLight); for (int i = 0; i < numMajor; ++i) { double a0 = i * majorStep; double a1 = a0 + majorStep; GLfloat x0 = (GLfloat)cos(a0); GLfloat y0 = (GLfloat)sin(a0); GLfloat x1 = (GLfloat)cos(a1); GLfloat y1 = (GLfloat)sin(a1); glBegin(GL_TRIANGLE_STRIP); for (int j = 0; j <= numMinor; ++j) { double s = j * minorStep; GLfloat c = (GLfloat)cos(s); GLfloat r = minorRadius * c + majorRadius; GLfloat z = minorRadius * (GLfloat)sin(s); //第一個點 vNormal[0] = x0*c; vNormal[1] = y0*c; vNormal[2] = z/minorRadius; m3dNormalizeVector(vNormal); //設置紋理座標爲光的強度 //數學中向量點積就是來判斷兩個向量的夾角的大小 glTexCoord1f(m3dDotProduct(vNewLight, vNormal)); glVertex3f(x0*r, y0*r, z); //第二個點 vNormal[0] = x1*c; vNormal[1] = y1*c; vNormal[2] = z/minorRadius; m3dNormalizeVector(vNormal); //設置紋理座標爲光的強度 glTexCoord1f(m3dDotProduct(vNewLight, vNormal)); glVertex3f(x1*r, y1*r, z); } glEnd(); } } void RenderScene() { static GLfloat yRot = 0.0f; glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glPushMatrix(); glTranslatef(0.0f, 0.0f, -2.5f); glRotatef(yRot, 0.0f, 1.0f, 0.0f); DrawDoughnut(0.35f, 0.15f, 50, 25, vLightDir); glPopMatrix(); glutSwapBuffers(); yRot += 0.5f; } void TimerFunc(int value) { glutPostRedisplay(); glutTimerFunc(50, TimerFunc, 1); } void ChangeSize(GLsizei w, GLsizei h) { if (h == 0) h = 1; GLfloat fAspect = (GLfloat)w/(GLfloat)h; glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(35.5, fAspect, 1.0, 50.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); glutPostRedisplay(); } int main(int args, char *argv[]) { glutInit(&args, argv); glutInitWindowSize(800, 600); glutInitDisplayMode(GL_DOUBLE | GL_RGB | GL_DEPTH); glutCreateWindow("CARTOON"); SetupRC(); glutDisplayFunc(RenderScene); glutReshapeFunc(ChangeSize); glutTimerFunc(50, TimerFunc, 1); glutMainLoop(); return 0; }