圖形學_opengl紋理對映
學了半學期的圖形學,除了幾個用python或是matlab比較方便的實驗外,用的大多數是opengl,在這總結一下紋理貼圖實驗中opengl的用法。
1、編譯器連線靜態庫
有用到glaux.h的程式,在加入相應的.h、.lib檔案後,需要加入兩行程式碼強行連線靜態庫:
#pragma comment(lib, "glaux") #pragma comment(lib, "legacy_stdio_definitions")
另外關於glaux.h,我想吐槽的是在csdn賣下載的人是有多想賺錢?……這裡我把找到的glaux.h的下載連結貼出來,需要自取:
連結:https://pan.baidu.com/s/1-P44eWXlehmd9jPYuXNiHw 提取碼:hbi6
2、畫一個簡單立方體
最終目的是要把貼圖對映到一個立方體上,首先我們需要構建一個不存在任何紋理的立方體。首先構建繪製函式:
void Draw(void){ glClear(GL_COLOR_BUFFER_BIT); glLoadIdentity(); glTranslatef(0.0f, 0.0f, -5.0f); glBegin(GL_QUADS); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glVertex3f(1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glEnd(); glFlush(); }
首先使用大家都喜歡的(必須用的)清屏函式glClear將視窗清除成當前幀的顏色,不用的話滿屏漆黑。glLoadIdentity()被用於重置觀察模型矩陣(感覺這幾個函式挺固定搭配的?);glTranslatef()用於移動模型,三個引數分別對應x,y,z軸線模型的移動,這裡向負方向移動了5f。不挪 或者 向正方向挪,只會造成我們的視點根本無法觀察到模型,負方向5f的位置相對大小比較合適。
模板一樣的東西套完以後,可以注意到glBegin()和glEnd()之間有一萬個(不是)glVertex3f的堆疊,用於確定立方體的點。glBegin的引數QUADS表示接下來要繪製以四個點為一組的四邊形,其他引數的含義如下:
GL_POINTS:把每個頂點作為一個點進行處理,頂點n定義了點n,繪製N個點。
GL_LINES: 把每個頂點作為一個獨立的線段,頂點2n-1和2n之間定義了n條線段,繪製N/2條線段
GL_LINE_STRIP:繪製從第一個頂點到最後一個頂點依次相連的一組線段,第n和n+1個頂點定義了線段n,繪製n-1條線段。
GL_LINE_LOOP: 繪製從第一個頂點到最後一個頂點依次相連的一組線段,然後最後一個頂點和第一個頂點相連,第n和n+1個頂點定義了線段n,繪製n條線段。
GL_TRIANGLES: 把每個頂點作為一個獨立的三角形,頂點3n-2,3n-1和3n定義了第n個三角形,繪製了N/3個三角形。
GL_TRIANGLE_STPIP:繪製一組相連的三角形,對於奇數n,頂點n,n+1,和n+2定義了第n個三角形;對於偶數n,頂點n+1,n和n+2定義了第n個三角形,繪製N-2個三角 形。
GL_QUAD_STRIP:繪製一組相連的四邊形。每個四邊形是由一對頂點及其後給定的一對頂點共同確定的。頂點2n-1,2n,2n+2和2n+1定義了第n個四邊形,繪製了N/2-1個 四邊形。
GL_POLYGON:繪製了一個凸多邊形。頂點1到n定義了這個多邊形。
而glVertex3f()的三個函式對應x,y,z軸的座標,共4*6個glVertex3f()函式呼叫,對應立方體6個面的4個點。最後使用glFlush()清除快取。
再構建一個display函式用於展示,將繪製函式置於display函式中:
void display(void){ glClear(GL_COLOR_BUFFER_BIT); Draw(); glutSwapBuffers(); }
清屏函式還是熟悉的味道,只是多了glutSwapBuffers()用於交換緩衝區和顯示圖形。
接下來是一個實現繪圖之後轉換矩陣模式的函式,用於主函式中的回撥函式glutReshapeFunc()中:
void reshape(GLsizei w, GLsizei h){ glViewport(0, 0, w, h); glMatrixMode(GL_PROJECTION); gluPerspective(60, (GLfloat)w / h, 0, 100); glMatrixMode(GL_MODELVIEW); }
繪圖以後先將矩陣模式調為投影模式,在投影模式下使用gluPerspective調整投影模型(不同模式下能執行的操作不一樣),四個引數分別代表視野範圍(值越大,視野範圍越寬闊),裁剪面的寬w高h比(影響到視野的截面有多大),近裁剪面到眼睛的距離,遠裁剪面到眼睛的距離(都不能設定設定為負值)。設定完之後把矩陣設定為檢視模式。
最後是main()函式:
int main(int argc, char* argv[]){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowSize(600, 600); glutInitWindowPosition(100, 100); glutCreateWindow("大立方體?"); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
main函式的固定搭配glutInit(),之後設定顯示模式為RGB單快取區,分別設定視窗大小和位置之後,使用glutDisplayFunc()呼叫display(),glutReshapeFunc呼叫reshape,所得圖形是這樣:
呃…這不是個正方形嘛,其實只是我們的視點正對著立方體 而已。
接下來給它貼上紋理,首先加入三軸的旋轉變數與一個紋理陣列:
GLfloat xrot = 0; GLfloat yrot = 0; GLfloat zrot = 0; GLuint texture[1];
讀入貼圖檔案:
AUX_RGBImageRec* LoadBMP(const char Filename[7]){ FILE* File = NULL; if (!Filename){ return NULL; } File = fopen(Filename, "r"); if (File) { fclose(File); return auxDIBImageLoadA(Filename); } return NULL; }
聽說是固定搭配?
有了讀取點陣圖的函式,我們需要將它轉換成紋理:
int LoadGLTextures(GLuint* texture, const char bmp_file_name[7], int texture_id){ int re=1; AUX_RGBImageRec* TextureImage[1]; memset(TextureImage, 0, sizeof(void*) * 1); if (TextureImage[0] = LoadBMP(bmp_file_name)){ re = 0; glGenTextures(texture_id, texture); glBindTexture(GL_TEXTURE_2D, *texture); glTexImage2D(GL_TEXTURE_2D, 0, 3, TextureImage[0]->sizeX, TextureImage[0]->sizeY, 0, GL_RGB, GL_UNSIGNED_BYTE, TextureImage[0]->data); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } if (TextureImage[0]){ if (TextureImage[0]->data){ free(TextureImage[0]->data); } free(TextureImage[0]); } else printf("紋理不存在"); return re; }
首先宣告一個AUX_RGBImageRec型別的指標,memset初始化之後呼叫之前的函式載入點陣圖,glGenTextures生成紋理,引數分別為生成紋理的數量與指標,使用glBindTexture 將一個命名的紋理繫結到一個紋理目標上,glTexImage2D功能是根據指定的引數,生成一個2D紋理,這裡的長、寬都必須是2的整數次方,glTexParameteri……是神祕的紋理過濾函式,對我們的紋理分別進行了一次縮小與放大的線性過濾。紋理生成之後,釋放原影象的空間。
接下來修改繪製函式,將紋理對映到六個面上:
void Draw(void){ glClear(GL_COLOR_BUFFER_BIT ); glLoadIdentity();
glTranslatef(0.0f, 0.0f, -5.0f); glRotatef(xrot, 1.0f, 0.0f, 0.0f); glRotatef(yrot, 0.0f, 1.0f, 0.0f); glRotatef(zrot, 0.0f, 0.0f, 1.0f); glBindTexture(GL_TEXTURE_2D, texture[0]); glBegin(GL_QUADS); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(1.0f, 1.0f, -1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(1.0f, -1.0f, 1.0f); glTexCoord2f(0.0f, 0.0f); glVertex3f(-1.0f, -1.0f, -1.0f); glTexCoord2f(1.0f, 0.0f); glVertex3f(-1.0f, -1.0f, 1.0f); glTexCoord2f(1.0f, 1.0f); glVertex3f(-1.0f, 1.0f, 1.0f); glTexCoord2f(0.0f, 1.0f); glVertex3f(-1.0f, 1.0f, -1.0f);
glEnd(); glFlush(); }
glRotatef用來設定opengl中繪製實體的自轉方式,為我們的模型動起來做準備。比起之前的draw函式,這裡多了glTexCoord2f,兩引數分別為X軸座標,Y軸座標,用於繪製圖形時指定紋理的座標,其中x、y的座標:0.0是紋理的左側,1.0是紋理的右側;0.0是紋理的底部,1.0是紋理的頂部。
這裡繼續新增一個紋理初始化函式:void init(void){ glClearColor(1.0, 1.0, 1.0, 0.0); glCullFace(GL_BACK); glEnable(GL_CULL_FACE); glEnable(GL_TEXTURE_2D); LoadGLTextures(&texture[0], "111_.bmp", 1); }
使用清屏函式之後,使用glCullFace禁用多邊形背面上的光照、陰影和顏色計算及操作,消除不必要的渲染計算,使用glEnable開啟之前的消除操作。最後載入之前生成的紋理。
給出我們修改以後的主函式:
int main(int argc, char* argv[]){ glutInit(&argc, argv); glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowSize(600, 600); glutInitWindowPosition(100, 100); glutCreateWindow("貼圖立方體?"); init(); LoadGLTextures(&texture[0], "111_.bmp", 1); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMainLoop(); return 0; }
可以注意到新增了初始化函式與紋理載入函式,結果為:
看起來還只是一個平面圖?為了讓它轉起來,寫入滑鼠響應函式:
void RotateRect() { xrot += 1.0f; glutPostRedisplay(); yrot += 1.0f; glutPostRedisplay(); zrot += 1.0f; glutPostRedisplay(); Sleep(10); } void OnMouse(int button, int state, int x, int y){ if (button == GLUT_LEFT_BUTTON && state == GLUT_DOWN){ glutIdleFunc(RotateRect); } if (button == GLUT_RIGHT_BUTTON && state == GLUT_DOWN){ glutIdleFunc(NULL); } }
在RotateRect中修改各個維度的旋轉量,使用glutPostRedisplay進行重繪,為了不使它暴走旋轉,呼叫winbase.h中的Sleep函式,變數改變一次暫停程式10毫秒。定義滑鼠響應函式,左鍵啟動,右鍵暫停(返回NULL),在回撥函式glutIdleFunc中呼叫RotateRect功能。
修改主函式:
int main(int argc, char* argv[]){ glutInit(&argc, argv); //固定格式 glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE); glutInitWindowSize(600, 600); glutInitWindowPosition(100, 100); glutCreateWindow("貼圖立方體?"); init(); LoadGLTextures(&texture[0], "111_.bmp", 1); glutDisplayFunc(display); glutReshapeFunc(reshape); glutMouseFunc(&OnMouse); glutMainLoop(); return 0; }
到這裡 紋理對映實驗就基本結束了。
參考部落格:
https://blog.csdn.net/dcrmg/article/details/53223680?utm_source=blogxgwz2
https://www.cnblogs.com/OctoptusLian/p/7366844.html#commentform
https://blog.csdn.net/tyxkzzf/article/details/40921713
https://blog.csdn.net/yangmeng900816/article/details/4681