OpenGL紋理簡介

1、瞭解紋理
  • 圖像的存儲空間 = 圖片width * 圖片height * 每個像素的字節數
  • OpenGL紋理文件是.tga文件,.tga特點是一個字節一個字節排列起來的,不會有多餘的空間浪費。壓縮圖片.png和.jpeg也可以當做紋理使用,系統會把壓縮圖片還原成位圖供紋理使用,位圖每一個像素點都會有其顏色空間。
2、紋理常用API
  1. 關於像素存儲方式
void glPixelStorei(GLenum pname, GLint param); //改變像素存儲方式
void glPixelStoref(GLenum pname, GLint param); //恢復像素存儲方式
//例子
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
/*
參數1:指定OpenGL如何從數據緩存區解包圖像數據,
GL_UNPACK_ALIGNMENT指內存中每個像素行起點的排列請求,允許設置爲1(byte排列)、2(排列爲偶數byte的行)、4(字word排列)、8(行從雙字節邊界開始)
參數2:表示參數GL_UNPACK_ALIGNMENT設置的值
*/

  1. 從顏色緩衝區讀取紋理文件.tga
void glReadPixels(GLint x, GLint y, GLSizei width, GLSizei height, GLenum format, GLenum type, const void *pixels);
/*
參數1/2:x/y,矩形左下角的窗口座標
參數3/4:width/height,矩形的寬高,以像素爲單位
參數5:format,OpenGL的像素格式,如RGBA
參數6:type,參數pixels指向的數據,讀取出來的顏色分量,像素數據的數據類型
參數7:pixels,指向圖形數據的指針
*/
glReadBuffer(mode); //指定讀取的緩存,從緩衝區讀取
glWriteBuffer(mode); //指定寫入的緩存,寫入到緩存區
  1. 載入紋理圖像
void glTexImage1D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLint border, GLenum format, GLenum type, const void *data);
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLSizei height, GLint border, GLenum format, GLenum type, const void *data);
void glTexImage3D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLSizei height, GLSizei depth, GLint border, GLenum format, GLenum type, const void *data);
/*
常用的是第二個二維紋理函數
參數1:target,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數2:level,指定所加載的mip貼圖層次,一般設爲0
參數3:internalformat,每個紋理單元中存儲多少顏色成分
參數4:width/height/depth,紋理的寬、高、深度,值設置爲2的整數次方
參數5:border,爲紋理貼圖指定一個邊框
參數6:format/type/data,同上一個函數
*/
  1. 更新紋理
void glTexSubImage1D(GLenum target, GLint level, GLint xOffset, GLSizei width, GLenum format, GLenum type, const void *data);
void glTexSubImage2D(GLenum target, GLint level,  GLint xOffset,  GLint yOffset, GLSizei width, GLSizei height, GLenum format, GLenum type, const void *data);
void glTexSubImage3D(GLenum target, GLint level, GLint xOffset,  GLint yOffset, GLint zOffset, GLSizei width, GLSizei height, GLSizei depth, GLenum format, GLenum type, const void *data);
  1. 插入替換紋理
void glCopyTexSubImage1D(GLenum target, GLint level, GLint xOffset, GLint x, GLSizei width);
void glCopyTexSubImage2D(GLenum target, GLint level,  GLint xOffset,  GLint yOffset, GLint x, GLint y, GLSizei width, GLSizei height);
void glCopyTexSubImage3D(GLenum target, GLint level, GLint xOffset,  GLint yOffset, GLint zOffset, GLint x, GLint y, GLint z,  GLSizei width, GLSizei height, GLSizei depth);
  1. copy紋理,從顏色緩衝區加載數據
void glCopyTexImage1D(GLenum target, GLint level, GLint internalformat, GLint x, GLSizei width, GLint border);
void glCopyTexImage2D(GLenum target, GLint level,  GLint internalformat, GLint x, GLint y, GLSizei width, GLSizei height, GLint border);
/*
x,y,在顏色緩衝區中指定了開始讀取紋理數據的位置;
緩存區裏的數據,是源緩存區通過glReadBuffer設置的。
***不存在glCopyTexImage3D,因爲無法從2D顏色緩存區中獲取體積數據***
*/
  1. 紋理對象
//使用函數分配紋理對象,分配對象標識符
//指定紋理對象的數量和指針(指針指向一個無符號整型數據,由紋理對象標識符填充)
void glGenTextures(GLsizei n, GLuint *textures);

//綁定紋理狀態
void glBindTexture(GLenum target, GLuint texture);
/*
參數1:target,GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數2:texture,需要綁定的紋理對象
*/

//刪除綁定紋理對象
//紋理對象以及紋理對象指針
void glDeleteTextures(GLsizei n, GLuint *textures);

//測試紋理對象是否有效
GLboolean glIsTexture(GLuint texture);
/*如果texture是一個已經分配空間的紋理對象,那麼這個函數會返回GL_TRUE,否則會返回GL_FALSE*/
  1. 設置紋理參數
glTexParameterf(GLenum target, GLenum pname, GLFloat param);
glTexParameteri(GLenum target, GLenum pname, GLint param);
glTexParameterfv(GLenum target, GLenum pname, GLFloat *param);
glTexParameteriv(GLenum target, GLenum pname, GLFloat *param);
/*
參數1:target,指定這些參數用在哪個紋理模式上,比如GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數2:pname,指定需要設置哪個紋理參數,設置紋理屬性
參數3:param,設定特定的紋理參數的值,屬性的值
*/
  • 過濾方式:
    鄰近過濾(GL_NEAREST):座標點最近的顏色
    線性過濾(GL_LINEAR):顏色混合
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //建議紋理縮小時,使用鄰近過濾
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //建議紋理放大時,使用線性過濾
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); 
  • 環繞方式:

GL_REPEAT,默認,重複紋理圖像
GL_MIRRORED_REPEAT,重複紋理圖像,每次重複圖片是鏡像放置的
GL_CLAMP_TO_EDGE,紋理座標會被約束在0-1之間,超出的部分會重複紋理座標的邊緣,產生一種邊緣被拉伸的效果
GL_CLAMP_TO_BORDER,超出的座標爲用戶指定的邊緣顏色

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_T, GL_CLAMP_TO_EDGE);
/*
參數1:GL_TEXTURE_1D、GL_TEXTURE_2D、GL_TEXTURE_3D
參數2:GL_TEXTURE_WRAR_S、GL_TEXTURE_WRAR_T、GL_TEXTURE_WRAR_R,對應s、t、r座標
參數3:GL_REPEAT(紋理座標超過1.0的方向上對紋理進行重複)、
      GL_CLAMP(所需的紋理單元取自紋理邊界或TEXTURE_BORDER_COLOR)、
      GL_CLAMP_TO_EDGE(強制對範圍之外的紋理座標沿着合法的紋理單元的最後一行或最後一列進行採樣)、
      GL_CLAMP_TO_BORDER(紋理座標在0.0到1.0範圍之外的只使用邊界紋理單元。邊界紋理單元是作爲圍繞基本圖像的額外的行和列,並與基本紋理圖像一起加載)
*/
3、使用紋理的流程
  1. 讀取紋理文件
void glReadPixels(GLint x, GLint y, GLSizei width, GLSizei height, GLenum format, GLenum type, const void *pixels);
  1. 載入紋理
void glTexImage2D(GLenum target, GLint level, GLint internalformat, GLSizei width, GLSizei height, GLint border, GLenum format, GLenum type, const void *data);
  1. 生成紋理對象
void glGenTextures(GLsizei n, GLuint *textures);
void glBindTexture(GLenum target, GLuint texture);
void glDeleteTextures(GLsizei n, GLuint *textures);
GLboolean glIsTexture(GLuint texture);
  1. 設置紋理相關參數
  • 設置過濾方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); 
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
  • 設置x軸和y軸上的環繞方式
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAR_T, GL_CLAMP_TO_EDGE);
4、紋理座標
  1. 紋理座標圖解


  • 一般設置左上角座標爲(0,0),相當於上圖順時針旋轉90°
  • 紋理座標的對應可以更換順序,但是不能交叉
    以下是紋理填充例子:
  • 立體圖形可以使用三維紋理,也可以每個面都使用二維紋理填充,常使用給每個面填充的方式
  1. 金字塔圖形座標解析:
    金字塔底部四邊形 = 三角形X + 三角形Y


  • 各個頂點座標
    vBackLeft (-1.0,-1.0,-1.0)
    vBackRight (1.0,-1.0,-1.0)
    vFrontLeft (-1.0,-1.0,1.0)
    vFrontRight(1.0,-1.0,1.0)
    vApex (0,1.0,0)
  • 三⻆形X的座標如下:
    vBackLeft(-1.0,-1.0,-1.0)
    vBackRight(1.0,-1.0,-1.0)
    vFrontRight(1.0,-1.0,1.0)
  • 三⻆形Y的座標如下:
    vFrontLeft(-1.0,-1.0,1.0)
    vBackLeft(-1.0,-1.0,-1.0)
    vFrontRight(1.0,-1.0,1.0)
  • 三⻆形X的2D紋理座標如下:
    vBackLeft(0.0,0.0,0.0)
    vBackRight(0.0,1.0,0.0)
    vFrontRight(0.0,1.0,1.0)
  • 三⻆形Y的2D紋理座標如下:
    vFrontLeft(0.0,0.0,1.0)
    vBackLeft(0.0,0.0,0.0)
    vFrontRight(0.0,1.0,1.0)
5、Mip貼圖(多級漸遠紋理)
  1. 介紹:
  • (節⾃<OpenGL 編程指南第9版 >—第6章紋理與幀緩存)


Mip貼圖是一種功能強大的紋理技巧,可以提高渲染性能改善場景的顯示質量。
當有很多很多個遠近不同的物體需要紋理渲染時,其實遠處的物體紋理擁有與近處物體同樣高的分辨率,但是遠處物體可能只產生很少的片段,OpenGL需要從高分辨率紋理中爲這個片段跨過紋理很大一部分片段只拾取一個紋理顏色,這是很困難的。小物體上會產生不真實的感覺,同時使用高分辨率紋理也很浪費內存。


Mip貼圖只有在縮小過濾時纔會使用

Mip紋理是由一系列的紋理圖像組成,每個圖像大小在每個軸的方向上都減小一半,或者是原來圖像像素總和的四分之一。Mip貼圖每個圖像大小都依次減半,知道最後一個圖像大小是1*1的紋理單元爲止。


  1. 設置Mip貼圖
//設置mip貼圖基層 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_BASE_LEVEL,0); 
//設置mip貼圖最大層 glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAX_LEVEL,0);
/*
level參數:指定圖像數據用於哪個Mip層,第一層是0,後面以此類推。如果Mip貼圖未使用,那麼只有第0層纔會被加載。默認情況下,爲了能使用Mip貼圖,所有的Mip層都要加載。GL_TEXTURE_BASE_LEVEL和GL_TEXTURE_MAX_LEVEL設置需要使用的基層和最大層。使用GL_TEXTURE_MIN_LOD和GL_TEXTURE_MAX_LOD參數限制已加載的Mip層的使用範圍。
*/
  1. 什麼時候生成Mip貼圖

只有minFilter(縮小過濾)等於以下四種模式纔可以生成Mip貼圖
· GL_NEAREST_MIPMAP_NEAREST,具有非常好的性能,並且閃爍現象非常弱
· GL_LINEAR_MIPMAP_NEAREST,常常用於對遊戲進行加速,它使用了高質量的線性過濾器
· GL_LINEAR_MIPMAP_LINEAR和GL_NEAREST_MIPMAP_LINEAR,在Mip層之間執行了一些額外的插值,以消除他們之間的過濾痕跡
· GL_LINEAR_MIPMAP_LINEAR,三線性Mip貼圖,紋理過濾的黃金準則,具有最高的精度

if(minFilter == GL_LINEAR_MIPMAP_LINEAR ||
         minFilter == GL_LINEAR_MIPMAP_NEAREST ||
         minFilter == GL_NEAREST_MIPMAP_LINEAR ||
         minFilter == GL_NEAREST_MIPMAP_NEAREST)
//􏰗􏰘􏰤􏰥紋理生成所有的Mip􏴝層
//􏰠􏰌􏵙參數:GL_TEXTURE_1D􏶰GL_TEXTURE_2D􏶰GL_TEXTURE_3D glGenerateMipmap(GL_TEXTURE_2D);

/*
void glGenerateMipmap􏶳(GLenum target);
glGenerateMipmap函數目的:爲紋理對象生成一組完整的mipmap

參數target:指定將生成的mipmap的紋理對象綁定到活動紋理單元的紋理目標,GL_TEXTURE_1D􏶰GL_TEXTURE_2D􏶰GL_TEXTURE_3D 

描述:glGenerateMipmap計算從零級數組派生的一組完整的mipmap數組。無論先前的內容如何,最多包括1*1維度紋理圖像的數組級別都將替換爲派生數組。零級紋理圖像保持不變(原圖)。派生的mipmap數組的內部格式都與零級紋理圖像的內部格式相匹配。通過將零級紋理圖像的寬和高減半來計算派生數組的尺寸,然後將每個陣列級別的尺寸減半,直到達到1*1尺寸的紋理圖像。
*/
  1. Mip貼圖過濾


tips:多級漸遠紋理主要使用在紋理被縮小的情況下,紋理放大不會使用多級漸遠紋理,如果使用會產生一個GL_INVALID_ENUM錯誤代碼並且沒有任何效果

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); //GL_TEXTURE_MIN_FILTER縮小過濾器,GL_NEAREST最鄰近過濾

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //GL_LINEAR線性過濾

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPM AP_NEAREST); //GL_NEAREST_MIPMAP_ NEAREST選擇最鄰近的Mip層,並執行最鄰近過濾

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST_MIPM AP_LINEAR); //GL_NEAREST_MIPMAP_ LINEAR在Mip層之間執行線性插補,並執行最鄰近過濾

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMA P_NEAREST); //GL_LINEAR_MIPMA P_NEAREST在Mip層之間執行線性插補,並執行線性過濾,又稱爲三線性過濾
6、各向異性過濾
  • 介紹

各向異性紋理過濾(Anisotropic texture filtering)並不是OpenGL核心規範中的一部分,但它是一種得到廣泛使用的擴展,可以極大提高紋理過濾操作的質量。
當一個紋理貼圖被過濾時,OpenGL使用紋理座標來判斷一個特定的幾何片段將落在紋理什麼地方,然後緊鄰這個位置的紋理單元使用GL_NEAREST和GL_LINEAR過濾操作進行採樣。
處理紋理過濾時,考慮了觀察角度的過濾方法叫做各向異性過濾。

  • 紋理採樣



    各向同性採樣:觀察方向和觀察點垂直時對紋理進行採樣;
    各向異性採樣:以一定角度傾斜地觀察幾何圖形,對周圍紋理單元進行常規採樣,這樣會導致一些紋理信息丟失,圖像看上去模糊。

爲了更加逼真和準確的採樣,應該沿着包含紋理的平面方向進行延伸。在Mip貼圖紋理過濾模型中,或者其他所有的基本紋理過濾都可以應用各向異性過濾。

  • 應用各向異性過濾,2個步驟
//第一,查詢得到支持的各向異性過濾的最大數量
GLfloat flargest;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT,&fLargest); 

//第二,設置各向異性過濾
//設置紋理參數(各向異性採樣)
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAX_ANISOTROPY_EXT,fLargest) 􏷻

//迴歸同性過濾,設置各向同性過濾,數量爲1.0表示各向同性採樣
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAX_ANISOTROPY_EXT, 1.0f);

注意:各向異性過濾所應用的數量fLargest越大,沿着最大變化方向(沿着最強的觀察點)所採樣的紋理單元就越多。值1.0表示常規的紋理過濾(各向同性過濾)。
各向異性過濾會增加額外的工作,包括其他紋理單元。很可能對性能造成影響,但是在現代硬件上,應用這個特性對速度造成影響不大。
最重要的是,目前它已經成爲流行遊戲、動畫和模擬程序的一個標準特性。

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