學了半學期的圖形學,除了幾個用python或是matlab比較方便的實驗外,用的大多數是opengl,在這總結一下紋理貼圖實驗中opengl的用法。html
一、編譯器鏈接靜態庫python
有用到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函數
二、畫一個簡單立方體oop
最終目的是要把貼圖映射到一個立方體上,首先咱們須要構建一個不存在任何紋理的立方體。首先構建繪製函數:ui
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的位置相對大小比較合適。spa
模板同樣的東西套完之後,能夠注意到glBegin()和glEnd()之間有一萬個(不是)glVertex3f的堆疊,用於肯定立方體的點。glBegin的參數QUADS表示接下來要繪製以四個點爲一組的四邊形,其餘參數的含義以下:.net
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/46816007