Array Texture這個東西的意思是,一個紋理對象,可以存儲不止一張圖片信息,就是說是是一個數組,每個元素都是一張圖片。這樣免了頻繁地去切換當前需要bind的紋理,而且可以節省系統資源。本文主要討論的是2D array textures. 1D的使用很少 不討論。
那么,在shader里面應該怎么去訪問我想要的紋理呢?普通的紋理坐標是 (x,y)
這里就不夠了,這里需要 (x,y,z)
三個值,XY代表2d紋理坐標,Z值代表選擇讀取哪一張紋理的數據,從0開始。
初始化Array Texture
普通 2D texture對象
初始化一個普通的2D紋理對象的方式是這樣的:
genTexture-->BindTexture-->指定圖片信息(使用 glTexImage2D
函數)
2D texture array對象
前兩步一樣的,指定圖片信息的時候不一樣。這里需要兩歩操作,開辟存儲空間和上傳數據。
第一步,開辟空間:
爲紋理開辟內存,確定存儲的結構
glTexImage3D
(這個和glTexImage2D
長得很像的函數)
target :GL_TEXTURE_2D_ARRAY
depth: 用於指定我們的數組的長度(若是3D texture就是紋理的深度信息了,我們用的是2D texture array)。
mipmap的level,一般我們給0,也就是說每次只能確定一個mipmap的level.
需要注意的是,我們生成普通紋理的時候數據是在這個地方指定的,同樣,這個函數的最后一個參數也代表源像素數據,但是這里可以指定也可以寫NULL,后面再上傳數據。我推薦這樣做。
以上是舊的做法,目前官方更推薦使用 glTexStorage3D
函數,功能類似。
glTexStorage3D — simultaneously specify storage for all levels of a three-dimensional or two-dimensional array texture
void glTexStorage3D( GLenum target,
GLsizei levels,
GLenum internalformat,
GLsizei width,
GLsizei height,
GLsizei depth);
參數和上面的相同,需要注意:levels 最小設置爲1;
這個函數比上面的好在什么地方呢?根據大牛的解釋,glTexImage3D
生成的紋理對象是不完整的,比如缺少mipmap的信息,缺少環繞方式等等,而且是可變的,這一秒創建好下一秒被改的亂七八糟了。而這個函數生成一個完整的不可變的對象,更可靠更安全。而且不用再顯式地去調用glGenerateMipmap
。它已經自動幫你做好了處理。
第二步,指定數據
glTexSubImage3D
這個函數用於上傳真正的紋理數據,參數較多,可以參考官方的文檔。提供指定xyz的offset,即偏移量。一般xy的設置0,z方向的便宜就代表了是第幾張紋理,所以這個需要按需要設置。后面的設置寬高很簡單,然后是depth,這一次調用上傳的數據的depth,如果兩張紋理圖片放在一起,然后一起上傳(這樣只需要調用一次這個函數),那么需要設爲2.后面再設置format data等等即可。如果需要指定mipmap(level大於1) 那么還需要把每個level的數據都上傳。
Example:
TexStorage3D(...3... W, H, 2);
// allocates W x H x 2 level0, W/2 x H/2 x 2 level1, W/4 x H/4 x 2 level2.
// Contents are undefined at this point.
TexSubImage3D(...level0, 0, 0, 0, W, H, 1... lod0_slice0_pixels);
TexSubImage3D(...level0, 0, 0, 1, W, H, 1... lod0_slice1_pixels);
TexSubImage3D(...level1, 0, 0, 0, W/2, H/2, 1... lod1_slice0_pixels);
TexSubImage3D(...level1, 0, 0, 1, W/2, H/2, 1... lod1_slice1_pixels);
TexSubImage3D(...level2, 0, 0, 0, W/4, H/4, 1... lod2_slice0_pixels);
TexSubImage3D(...level2, 0, 0, 1, W/4, H/4, 1... lod2_slice1_pixels);
// all slices of all mipmaps now transferred
如果相同mipmap的兩張紋理是前后相連地存儲在一起的,可以這樣做:
TexSubImage3D(...level0, 0, 0, 0, W, H, 2... lod0_slice0and1_pixels);
TexSubImage3D(...level1, 0, 0, 0, W/2, H/2, 2... lod1_slice0and1_pixels);
TexSubImage3D(...level2, 0, 0, 0, W/4, H/4, 2... lod2_slice0and1_pixels);
shader內訪問
需要定義2darray的sampler,然后還是調用texture
函數,但是第二個參數需要三維的紋理坐標,z值代表在第幾張紋理上面取值,從 0 開始。
"precision mediump sampler2DArray;\n"
"uniform sampler2DArray texture_array;\n"
...
...
" color = texture(texture_array, vec3(texCoord.xy, layer));\n"
注意:如果上傳三維的紋理坐標,假設我們有兩張紋理圖片,我們上傳的z值只有 0 和 1 兩個,但是在光柵化階段的插值計算會生成一些處於0和1之間的非整數的z值,這不是我們想要的,所以不能使用三維紋理坐標。我們需要單獨上傳這個數據,然后在shader內構造出一個新的三維的向量,這個時候z值就只有0、1兩個值了。
參考資料:Array-Texture-confusion 討論帖子
例子1--zwqxin
例子2--老外的