圖形學_opengl紋理對映

那不太可能發表於2020-05-12

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

相關文章