OpenGL(二十三) 各向異性紋理過濾
阿新 • • 發佈:2019-02-19
如果使用一般的紋理過濾,當觀察方向跟模型表面不是相互垂直的的情況下,會出現紋理資訊的丟失,表現為影象看上去比較模糊,如下圖所示,遠處場景的細節資訊很差:
針對這種情況,可以採用同向異性過濾的方式處理紋理,在過濾紋理的時候,考慮到觀察角度不同,使紋理本身沿著模型表面傾斜的方向進行延伸。
使用如下語句查詢當前系統支援的最大同向異性過濾的數值,數值越大,表示沿著最大變化方向所取樣的紋理單元越多,顯示效果就越好:
GLfloat max_TexAni; //查詢允許的各向異性數量 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_TexAni);
以“ GL_TEXTURE_MAX_ANISOTROPY_EXT ”為引數呼叫“ glTexParameterf ”函式,並設定同向異性過濾的數值,就可以使設定生效。以下是經過同向異性過濾設定的顯示效果:
遠處紋理的細節增強了很多。以下是工程程式碼:
#define WindowWidth 400 #define WindowHeight 400 #define WindowTitle "OpenGL Mip紋理貼圖&&同向異性過濾" #include <Windows.h> #include "GL/glew.h" #include <GL/freeglut.h> #include <stdio.h> #include <stdlib.h> //定義紋理物件編號 GLuint texGround; GLuint texWall; GLuint texSky; #define BMP_Header_Length 54 //影象資料在記憶體塊中的偏移量 static GLfloat angle = 0.0f; //旋轉角度 static GLfloat zPosition = 10; // 函式power_of_two用於判斷一個整數是不是2的整數次冪 int power_of_two(int n) { if (n <= 0) return 0; return (n & (n - 1)) == 0; } /* 函式load_texture 28.* 讀取一個BMP檔案作為紋理 29.* 如果失敗,返回0,如果成功,返回紋理編號 30.*/ GLuint load_texture(const char* file_name) { GLint width, height, total_bytes; GLubyte* pixels = 0; GLuint last_texture_ID = 0, texture_ID = 0; // 開啟檔案,如果失敗,返回 FILE* pFile = fopen(file_name, "rb"); if (pFile == 0) return 0; // 讀取檔案中圖象的寬度和高度 fseek(pFile, 0x0012, SEEK_SET); fread(&width, 4, 1, pFile); fread(&height, 4, 1, pFile); fseek(pFile, BMP_Header_Length, SEEK_SET); // 計算每行畫素所佔位元組數,並根據此資料計算總畫素位元組數 { GLint line_bytes = width * 3; while (line_bytes % 4 != 0) ++line_bytes; total_bytes = line_bytes * height; } // 根據總畫素位元組數分配記憶體 pixels = (GLubyte*)malloc(total_bytes); if (pixels == 0) { fclose(pFile); return 0; } // 讀取畫素資料 if (fread(pixels, total_bytes, 1, pFile) <= 0) { free(pixels); fclose(pFile); return 0; } // 對就舊版本的相容,如果圖象的寬度和高度不是的整數次方,則需要進行縮放 // 若影象寬高超過了OpenGL規定的最大值,也縮放 { GLint max; glGetIntegerv(GL_MAX_TEXTURE_SIZE, &max); if (!power_of_two(width) || !power_of_two(height) || width > max || height > max) { const GLint new_width = 256; const GLint new_height = 256; // 規定縮放後新的大小為邊長的正方形 GLint new_line_bytes, new_total_bytes; GLubyte* new_pixels = 0; // 計算每行需要的位元組數和總位元組數 new_line_bytes = new_width * 3; while (new_line_bytes % 4 != 0) ++new_line_bytes; new_total_bytes = new_line_bytes * new_height; // 分配記憶體 new_pixels = (GLubyte*)malloc(new_total_bytes); if (new_pixels == 0) { free(pixels); fclose(pFile); return 0; } // 進行畫素縮放 gluScaleImage(GL_RGB, width, height, GL_UNSIGNED_BYTE, pixels, new_width, new_height, GL_UNSIGNED_BYTE, new_pixels); // 釋放原來的畫素資料,把pixels指向新的畫素資料,並重新設定width和height free(pixels); pixels = new_pixels; width = new_width; height = new_height; } } // 分配一個新的紋理編號 glGenTextures(1, &texture_ID); if (texture_ID == 0) { free(pixels); fclose(pFile); return 0; } // 繫結新的紋理,載入紋理並設定紋理引數 // 在繫結前,先獲得原來繫結的紋理編號,以便在最後進行恢復 GLint lastTextureID = last_texture_ID; glGetIntegerv(GL_TEXTURE_BINDING_2D, &lastTextureID); glBindTexture(GL_TEXTURE_2D, texture_ID); /*glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 130. glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); */ GLfloat max_TexAni; //查詢允許的各向異性數量 glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &max_TexAni); glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, max_TexAni); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_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); glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); /* glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, 140. GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); */ gluBuild2DMipmaps(GL_TEXTURE_2D, 3, width, height, GL_BGR_EXT, GL_UNSIGNED_BYTE, pixels); glBindTexture(GL_TEXTURE_2D, lastTextureID); //恢復之前的紋理繫結 free(pixels); return texture_ID; } void Display(void) { // 清除螢幕 glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // 設定視角 glMatrixMode(GL_PROJECTION); glLoadIdentity(); gluPerspective(65, 1, 1, 100); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); gluLookAt(0, 0, zPosition, 0, 0, 0, 0, 1, 0); glRotatef(angle, 0.0f, 1.0f, 0.0f); //旋轉 // 繪製左側牆壁以及紋理 glBindTexture(GL_TEXTURE_2D, texWall); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, -5.0f, 100.0f); glTexCoord2f(30.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -100.0f); glTexCoord2f(30.0f, 2.0f); glVertex3f(-5.0f, 5.0f, -100.0f); glTexCoord2f(0.0f, 2.0f); glVertex3f(-5.0f, 5.0f, 100.0f); glEnd(); //繪製右側牆 glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(5.0f, -5.0f, 100.0f); glTexCoord2f(30.0f, 0.0f); glVertex3f(5.0f, -5.0f, -100.0f); glTexCoord2f(30.0f, 2.0f); glVertex3f(5.0f, 5.0f, -100.0f); glTexCoord2f(0.0f, 2.0f); glVertex3f(5.0f, 5.0f, 100.0f); glEnd(); //繪製地板 glBindTexture(GL_TEXTURE_2D, texGround); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, -5.0f, 100.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(5.0f, -5.0f, 100.0f); glTexCoord2f(25.0f, 1.0f); glVertex3f(5.0f, -5.0f, -100.0f); glTexCoord2f(25.0f, 0.0f); glVertex3f(-5.0f, -5.0f, -100.0f); glEnd(); //繪製頂層 glBindTexture(GL_TEXTURE_2D, texSky); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-5.0f, 5.0f, 100.0f); glTexCoord2f(0.0f, 3.0f); glVertex3f(5.0f, 5.0f, 100.0f); glTexCoord2f(35.0f, 3.0f); glVertex3f(5.0f, 5.0f, -100.0f); glTexCoord2f(35.0f, 0.0f); glVertex3f(-5.0f, 5.0f, -100.0f); glEnd(); glutSwapBuffers(); } void SpecialKey(GLint key, GLint x, GLint y) { if (key == GLUT_KEY_UP) { zPosition += 1.0f; } if (key == GLUT_KEY_DOWN) { zPosition -= 1.0f; } if (key == GLUT_KEY_LEFT) { angle += 0.5f; } if (key == GLUT_KEY_RIGHT) { angle -= 0.5f; } glutPostRedisplay(); } int main(int argc, char* argv[]) { // GLUT初始化 glutInit(&argc, argv); glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA); glutInitWindowPosition(100, 100); glutInitWindowSize(WindowWidth, WindowHeight); glutCreateWindow(WindowTitle); glEnable(GL_DEPTH_TEST); glEnable(GL_TEXTURE_2D); // 啟用紋理 texGround = load_texture("ground.bmp"); //載入紋理 texWall = load_texture("wall.bmp"); texSky = load_texture("sky.bmp"); glutDisplayFunc(&Display); //回撥函式 glutSpecialFunc(&SpecialKey); glutMainLoop(); //迴圈呼叫 return 0; }