OpenGL學習——紋理貼圖一

本篇文章爲紋理貼圖的第一次學習,沒太多時間去深入的學,把每個函數都搞得明明白白,所以這裏自己的理解寫的比較少。別人總結的還蠻好的,所以我主要是總結了幾篇其他文章的內容,如果不去深究每個函數,我覺得足以讓你明白紋理貼圖的一個大致操作流程。

這篇就當做入門的吧,紋理映射還是比較難的,後面肯定會結合着代碼再寫的。

開篇先放一個大致流程,方便以後快速查看:

啓用2D紋理
加載紋理圖像(即導入圖片)
創建紋理對象
綁定紋理對象
設定紋理過濾的參數
爲紋理對象指定紋理圖像數據
紋理映射,繪製紋理圖像

一、創建紋理對象,併爲它指定一個紋理

啓用紋理貼圖

glEnable(GL_TEXTURE_2D);

定義一個紋理對象編號

static GLuint texName;

創建一個紋理對象,&texName指向紋理索引

glGenTextures(1, &texName);

綁定紋理:改變OpenGL狀態,使得後續的紋理操作都對texName指向的2D紋理生效

glBindTexture(GL_TEXTURE_2D, texName);

二、載入紋理,設置紋理參數

  1. 紋理過濾函數

glTexParameteri()

圖象從紋理圖象空間映射到幀緩衝圖象空間(映射需要重新構造紋理圖像,這樣就會造成應用到多邊形上的圖像失真),這時就可用glTexParmeteri()函數來確定如何把紋理象素映射成像素。

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);

這些參數先不用管,就是說貼圖時的各種限制和模式。

以後還會總結的。

  1. 指定紋理貼圖與材質的混合模式

glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);

材質這個不是太懂。

  1. 爲紋理對象指定一個紋理

glTexImage2D (
GLenum target, //指定的目標,可以使用GL_TEXTURE_2D
GLint level, //“多重細節層次”,不考慮多重紋理的話設置爲零。
GLint internalformat,//RGB數據存儲的格式,比如GL_RGB
GLsizei width,
GLsizei height, //二維紋理像素的寬度和高度。
GLint border, //紋理邊框的大小,不使用紋理邊框設置爲零。
GLenum format, //
GLenum type, //數據格式和數據保存形式。
const GLvoid *pixels //保存了紋理圖像的內參塊地址。
);

此函數的參數可以像下面這樣設置:

glTexImage2D(
GL_TEXTURE_2D,        //target,目標紋理
0, 			          //分辨率級數參數,默認爲0
GL_RGBA, 	       	  //紋理單元格式
checkImageWidth,      //紋理圖像的寬
checkImageHeight,     //紋理圖像的高
0, 			          //紋理圖像邊框的寬度,0或1
GL_RGBA,              //紋理像素數據的格式
GL_UNSIGNED_BYTE, 	  //像素數據類型
checkImage	          //內存中指向紋理圖像的指針
);

三、紋理映射,繪製紋理圖像

紋理映射跟顏色的繪製一樣,需要指定每一個頂點在紋理圖像中所對應的位置,OpenGL會自動計算出頂點之間的其他點在紋理圖像中應該對應的位置。這裏注意紋理圖像的座標範圍是從(0,0)到(1,1),左下角爲(0,0),右上角爲(1,1)。比如,將圖像映射到多個四邊形上:

    // 繪製底面以及紋理
    glBindTexture(GL_TEXTURE_2D, texSea);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);   //紋理座標配置函數
    glVertex3f(-8.0f, -8.0f, 0.0f);
    glTexCoord2f(0.0f, 3.0f);
    glVertex3f(-8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 3.0f);
    glVertex3f(8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 0.0f);
    glVertex3f(8.0f, -8.0f, 0.0f);
    glEnd();

此代碼顯示3行3列個位圖(共9個)(如果座標是小於1的,那就是正常的映射),座標可以這樣映射:(0.0f, 0.0f)、(0.0f, 3.0f)、(3.0f, 3.0f)、(3.0f, 0.0f)。

glTexCoord2f()主要與glVertex3f()配合使用,glTexCoord2f()是配置紋理座標,glVertex3f()是配置圖形座標。

四、其他補充

參考文章:
https://blog.csdn.net/dcrmg/article/details/53180369

更詳細的解釋可以看這篇文章(我以後會再總結):

https://www.cnblogs.com/sunliming/archive/2011/07/17/2108917.html

下面這個代碼的原文章有大致這樣說明,讀取紋理圖片到內存時,位圖的大小有規定的大小,寬度什麼的有些要求,如果長寬有超過當前版本所支持最大長寬數值,還需要對圖像進行縮放。這裏我們先不管這個格式問題了,給的位圖是標準的。

還有代碼中關於讀取位圖的操作,我覺得有點麻煩,我看有些直接使用stb_image.h來加載圖像,這個我還沒有研究,但肯定比這篇文章的示例代碼簡單些。

他這個代碼是手動分配內存的,就先別管了,重點是看紋理的使用流程。

ps:給的代碼放到桌面上運行。
在這裏插入圖片描述

#define WindowWidth  400
#define WindowHeight 400
#define WindowTitle  "紋理貼圖"

#include <gl/glut.h>
#include <stdio.h>
#include <stdlib.h>
#define GL_BGR_EXT 0x80E0   //我這個版本找不到這個宏,需要自己定義

//定義兩個紋理對象編號
GLuint texSea;
GLuint texSky;

#define BMP_Header_Length 54  //圖像數據在內存塊中的偏移量

static GLfloat angle = 0.0f;   //旋轉角度

//讀取一個BMP文件作爲紋理。如果失敗,返回0,如果成功,返回紋理編號.
//這個代碼的處理位圖的操作太麻煩了,可以先不管,知道他把位圖保存到了一個字節變量pixel中就好
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 )
    {
        printf("Wrong!!!!\n");
        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;
    }


    // 分配一個新的紋理編號
    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);   //綁定紋理

    //設置4個常用的紋理參數。。如何把紋理像素映射成像素
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_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); //指定紋理貼圖與材質的混合模式

    //參數: 目標紋理,多重細節層次(不考慮多重紋理爲0),RGB數據存儲格式,二維紋理像素寬高,紋理邊框大小
    //       紋理像素數據的格式,數據保存形式(像素數據類型,字節),內存中指向紋理圖像的指針(紋理圖像的內參快地址)
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0,
                 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(75, 1, 1, 21);

    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();
    gluLookAt(-4, 7,7, 0, 0, 0, 0, 0, 1);

    glRotatef(angle, 0.0f, 0.0f, 1.0f); //旋轉矩形,實現物體的旋轉


    // 繪製底面以及紋理
    glBindTexture(GL_TEXTURE_2D, texSea);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);   //紋理座標配置函數
    glVertex3f(-8.0f, -8.0f, 0.0f);
    glTexCoord2f(0.0f, 3.0f);
    glVertex3f(-8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 3.0f);
    glVertex3f(8.0f, 8.0f, 0.0f);
    glTexCoord2f(3.0f, 0.0f);
    glVertex3f(8.0f, -8.0f, 0.0f);
    glEnd();
    //比如顯示3行2列個位圖(共6個),座標可以這樣:(0.0f, 0.0f)、(0.0f, 3.0f)、(2.0f, 3.0f)、(2.0f, 0.0f)。
    //該函數主要與glVertex3f()配合使用,glTexCoord2f()是配置紋理座標,glVertex3f()是配置圖形座標。

    // 繪製立面
    glBindTexture(GL_TEXTURE_2D, texSky);
    glBegin(GL_QUADS);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(-6.0f, -3.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(-6.0f, -3.0f, 5.0f);
    glTexCoord2f(2.0f, 1.0f);
    glVertex3f(6.0f, -3.0f, 5.0f);
    glTexCoord2f(2.0f, 0.0f);
    glVertex3f(6.0f, -3.0f, 0.0f);
    glEnd();

    //繪製另外一個立面
    glBegin(GL_QUADS);
    glTexCoord2f(2.0f, 0.0f);
    glVertex3f(6.0f, -3.0f, 0.0f);
    glTexCoord2f(0.0f, 0.0f);
    glVertex3f(6.0f, 9.0f, 0.0f);
    glTexCoord2f(0.0f, 1.0f);
    glVertex3f(6.0f, 9.0f, 5.0f);
    glTexCoord2f(2.0f, 1.0f);
    glVertex3f(6.0f, -3.0f, 5.0f);
    glEnd();

    glutSwapBuffers();
}

//旋轉角度
void myIdle(void)
{
    angle += 0.01f; //1.8f
    if( angle >= 360.0f )
        angle = 0.0f;
    display();
}

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);    // 啓用2D紋理功能

    texSea = load_texture("C:\\Users\\user\\Desktop\\貼圖1_方塊\\sea.bmp");
    texSky = load_texture("C:\\Users\\user\\Desktop\\貼圖1_方塊\\sky.bmp");  //加載紋理

    //繪圖
    glutDisplayFunc(&display);
    glutIdleFunc(&myIdle);
    glutMainLoop();
    return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章