本篇文章爲紋理貼圖的第一次學習,沒太多時間去深入的學,把每個函數都搞得明明白白,所以這裏自己的理解寫的比較少。別人總結的還蠻好的,所以我主要是總結了幾篇其他文章的內容,如果不去深究每個函數,我覺得足以讓你明白紋理貼圖的一個大致操作流程。
這篇就當做入門的吧,紋理映射還是比較難的,後面肯定會結合着代碼再寫的。
開篇先放一個大致流程,方便以後快速查看:
啓用2D紋理
加載紋理圖像(即導入圖片)
創建紋理對象
綁定紋理對象
設定紋理過濾的參數
爲紋理對象指定紋理圖像數據
紋理映射,繪製紋理圖像
一、創建紋理對象,併爲它指定一個紋理
啓用紋理貼圖
glEnable(GL_TEXTURE_2D);
定義一個紋理對象編號
static GLuint texName;
創建一個紋理對象,&texName指向紋理索引
glGenTextures(1, &texName);
綁定紋理:改變OpenGL狀態,使得後續的紋理操作都對texName指向的2D紋理生效
glBindTexture(GL_TEXTURE_2D, texName);
二、載入紋理,設置紋理參數
- 紋理過濾函數
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);
這些參數先不用管,就是說貼圖時的各種限制和模式。
以後還會總結的。
- 指定紋理貼圖與材質的混合模式
glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
材質這個不是太懂。
- 爲紋理對象指定一個紋理
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;
}