學了半學期的圖形學,除了幾個用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/46816007