OpenGLEs 紋理的基礎知識
紋理的基礎知識
2D 紋理
2d紋理是OpenGlES中最基礎和普遍的一種紋理結構。一個2d紋理,就是圖片的資料的一個二維陣列。紋理中每一個獨立的資料單元被稱為texels
(“texture pixels”的縮寫)。OpenGLES中紋理影象資料可變被許多種不同的基本格式描繪。
根據影象的基本型別和影象的資料型別,決定了影象中的每一個texel。
當渲染2D紋理是,紋理的座標將成為索引。2D紋理的座標軸別是(s,t)或者是(u,v)。這個座標軸是規範化的,即座標的大小在0.0~1.0之間。取紋理影象的左下角為座標原點(0.0,0.0)
座標超過[0.0,1.0]的範圍是允許的,而對與超出範圍的內容的操作,取決於紋理的包裝方式(wrapping mode)
立方體貼圖紋理(Cubemap Texttures
)
OpenGL ES 3.0 支援立方體貼圖紋理。最基本的,一個立方體紋理由6個獨立的2D紋理面組成。每一個2D紋理都是立方體的一個平面。雖然立方體貼圖在3D渲染中有了多元化的進步,但是最普遍的還是被用在環境貼圖中。典型的,在環境貼圖中,將一臺攝像機放置場景的中心點,拍攝6個方位的影象並儲存。
立體貼圖中的Texels通過使用一個3D的向量(s,t,r)來定位。定位時,先根據r確定是6個平面的哪一個平面,在根據(s,t)確定平面上的一點。
立體貼圖紋理的每一個平面都必須是正方形的。
3D紋理
3D紋理可以看做是2D紋理多個切片的一個數組。將2D紋理當做的一個面,將面疊加起來,便是一個立體。這個立體就是3D紋理。3D紋理使用一個3元座標(s,t,r),r座標決定哪一個切面,(s,t)決定在這個切面中的座標。
2D紋理陣列
2D紋理陣列和3D紋理很類似,當時一個3D紋理是表示一張影象,而一個2D紋理陣列,則是表示一組2D影象形成的動畫序列。
紋理物件和載入紋理
一個紋理物件是一個儲存將要被渲染的紋理資料的容器,包括了圖片資料,過濾方式和封裝方式。在OpenGLES中,一個紋理物件通過一個非負整數來標識,這個數是這個紋理物件的一個控制代碼。生產紋理的函式是glGenTextures
void glGenTextures(GLsizei n, GLuint *textures)
n 指定生產的紋理物件的數量
textures 一個非負整數陣列,用了儲存n個紋理物件的ID
通過glGenTextures
glDeleteTextures
實現。
void glDeleteTextures(GLsizei n, Gluint *textures)
n 指定刪除的紋理物件的數量
textures 一個非負整數陣列,用了儲存n個紋理物件的ID
當一個紋理物件的id被glGenTextures
建立後,程式必須將該紋理進行繫結。只有該紋理被繫結後,後續的操作例如glTexImage2D
和glTexParameter
才能影響到繫結的紋理物件。執行繫結功能的函式是glBindTexture
void glBindTexture(GLenum target, GLuint texture)
target 將紋理物件和目標GL_TEXTURE_2D, GL_TEXTURE_3D,GL_TEXTURE_2D_ARRAY或GL_TEXTURE_CUBE_MAP進行綁 定
texture 進行繫結的紋理的控制代碼
一旦紋理被繫結到一個指定的紋理目標,這個紋理物件將會一直維持對這個目標的繫結直到被刪除。在建立一個紋理目標並繫結後,下一步便是使用紋理去載入圖片資料。一個基本的載入2D和立體貼圖紋理的函式是glTexImage2D
。另外,在OpenGl ES 3.0 中有幾個可以替代的函式,這些函式被用於指定的2D紋理,包括使用不可變紋理glTexStorage2D
和glTexSubImage2D
的結合。
void glTextImage2D (GLenum target, GLint level,
GLenum internalFormat, GLsizei width,
GLsizei height, GLint border,
Glenum formate, Glenum type
const void * pixels)
target 指定紋理的目標,包括GL_TEXTURE_2D或立體貼圖面目標中的一個(GL_TEXTURE_CUBE_MAP_POSITIVE_X,GL_TEXTURE_CUBE_MAP_NEGATIVE_X,等等).
level 指定哪一個級別的mip將被載入。第一個等級被指定為0
internalFormat 紋理儲存的內部格式。包括了無大小限制的內部格式和有大小限制的格式。無大小內部格式包括 GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA GL_LUMINANCE, GL_ALPHA 有大小的內部格式包括 GL_R8, GL_R8_SNORM, GL_R16F, GL_R32F
GL_R8UI, GL_R16UI, GL_R32UI, GL_R32I
GL_RG8, GL_RG8_SNORM, GL_RG16F, GL_RG32F GL_RG8UI, GL_RG8I, GL_RG16UI, GL_RG32UI GL_RG32I, GL_RGB8, GL_SRGB8, GL_RGB565 GL_RGB8_SNORM, GL_R11F_G11F_B10F
GL_RGB9_E5, GL_RGB16F, GL_RGB32F
GL_RGB8UI, GL_RGB16UI, GL_RGB16I, GL_RGB32UI GL_RGB32I, GL_RGBA8, GL_SRGB8_ALPHA8 GL_RGBA8_SNORM, GL_RGB5_A1, GL_RGBA4 GL_RGB10_A2, GL_RGBA16F, GL_RGBA32F GL_RGBA8UI, GL_RGBA8I, GL_RGB10_A2UI GL_RGBA16UI, GL_RGBA16I, GL_RGBA32I GL_RGBA32UI, GL_DEPTH_COMPONENT16 GL_DEPTH_COMPONENT24, GL_DEPTH_COMPONENT32F GL_DEPTH24_STENCIL8, GL_DEPTH24F_STENCIL8
width 圖片的畫素寬度
heigth 圖片的畫素高度
border 必須為0
formate 輸入紋理資料的格式,可能是GL_RED
GL_RED_INTEGER
GL_RG
GL_RG_INTEGER
GL_RGB
GL_RGB_INTEGER
GL_RGBA
GL_RGBA_INTEGER
GL_DEPTH_COMPONENT
GL_DEPTH_STENCIL
GL_LUMINANCE_ALPHA
GL_ALPHA
type 輸入的畫素資料的型別;可能是
GL_UNSIGNED_BYTE
GL_BYTE
GL_UNSIGNED_SHORT
GL_SHORT
GL_UNSIGNED_INT
GL_INT
GL_HALF_FLOAT
GL_FLOAT
GL_UNSIGNED_SHORT_5_6_5
GL_UNSIGNED_SHORT_4_4_4_4
GL_UNSIGNED_SHORT_5_5_5_1
GL_UNSIGNED_INT_2_10_10_10_REV
GL_UNSIGNED_INT_10F_11F_11F_REV
GL_UNSIGNED_INT_5_9_9_9_REV
GL_UNSIGNED_INT_24_8
GL_FLOAT_32_UNSIGNED_INT_24_8_REV
GL_UNSIGNED_SHORT_5_6_5
pixels 包含了圖片的畫素資料。這個資料必須包含了(width*height)個畫素,每個畫素根據指定的格式和型別佔用一定的位元組,畫素行必須用GL_UNPACK_ALIGNMENT設定glPixelStorei設定對齊
和紋理載入相關的函式,還有一個glPixelStorei
,該函式決定了畫素資料儲存方式。
void glPixelStorei(GLenum pname , GLint param)
pname 指定畫素儲存型別。下面的可選引數將影響當glTexImage2D, glTexImage3D, glTexSubImage2D, 和 glTexSubImage3D被呼叫時,資料如何從記憶體中被解包:GL_UNPACK_ROW_LENGTH, GL_UNPACK_IMAGE_HEIGHT, GL_UNPACK_SKIP_PIXELS, GL_UNPACK_SKIP_ROWS, GL_UNPACK_SKIP_IMAGES, GL_UNPACK_ALIGNMENT
下列可選引數將影響glReadPixels被呼叫時,資料如何被打包讀入記憶體:GL_PACK_ROW_LENGTH, GL_PACK_IMAGE_HEIGHT, GL_PACK_SKIP_PIXELS, GL_PACK_SKIP_ROWS, GL_PACK_SKIP_IMAGES, GL_PACK_ALIGNMENT
param 為打包或解包指定的整形數值。
GL_PACK_xxxxx對紋理的更新沒有任何影響。實際上,除了GL_UNPACK_ALIGNMENT
,其他的可選選項很少被使用
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
意味著每一個畫素行從位元組邊界開始,換而言之,資料被緊密打包。該值的預設值是4,表示畫素行被認定從4位元組的邊界開始。
紋理過濾和mip貼圖
紋理座標用於生成一個2D索引,從紋理貼圖中讀取。當縮小和放大過濾器設定為GL_NEAREST
時,就會發生這樣的情況:一個texels將在提供的紋理座標位置上讀取。這樣稱作點取樣或最近取樣。
然而,最近取樣可能產生視覺偽像。一個偽像的產生是因為一個影象在螢幕中變小,導致畫素之間的紋理座標產生了巨大跳躍。這造成了從一個張巨大的紋理圖中讀取了少量的採用點,從而導致了鋸齒偽像的產生。
解決這一問題的方法是,採用mip貼圖。所謂的mip貼圖,就是通過當前的螢幕解析度,生成較低的螢幕解析度的圖片,形成一個圖片鏈。比如現在有一張64*64的圖片,採用mip貼圖,會生成32*32,16*16,8*8,4*4,2*2,1*1,等低解析度的圖片。這些圖片中,最原始的被稱為0級層。每低一個低階的層,都是由上等級的圖片生成的,畫素合併規則是,將每個圖層的2*2的畫素點相加取平均值,作為下一層的一個畫素點。
縮小過濾發生在螢幕中的多邊形小於紋理大大小時。放大過濾發生在螢幕中多邊大於紋理的大小時。對於放大過濾,mip貼圖沒有影響。對於縮小過濾,mip貼圖可以保證畫素的平滑
通過glTexParameter[i|f][v]
來設定紋理的的引數
void glTexParameteri(GLenum target, GLenum pname, GLint param)
void glTexParameteriv(GLenum target, GLenum pname, const GLint *params)
void glTexParameterf(GLenum target, GLenum pname, GLfloat param)
void glTexParameterfv(GLenum target, GLenum pname, const GLfloat *params)
target 紋理目標
pname 進行設定的紋理屬性,可以包括
GL_TEXTURE_BASE_LEVEL
GL_TEXTURE_COMPARE_FUNC
GL_TEXTURE_COMPARE_MODE
GL_TEXTURE_MIN_FILTER
GL_TEXTURE_MAG_FILTER
GL_TEXTURE_MIN_LOD
GL_TEXTURE_MAX_LOD
GL_TEXTURE_MAX_LEVEL
GL_TEXTURE_SWIZZLE_R
GL_TEXTURE_SWIZZLE_G
GL_TEXTURE_SWIZZLE_B
GL_TEXTURE_SWIZZLE_A
GL_TEXTURE_WRAP_S
GL_TEXTURE_WRAP_T
GL_TEXTURE_WRAP_R
params 指定屬性的引數
對於放大過濾GL_TEXTURE_MAG_FILTER
,可以設定為GL_NEAREST
或者是GL_LINEAR
。
對於縮小過濾GL_TEXTURE_MIN_FILTER
,可以設定的值如下
GL_NEAREST
表示根據紋理座標,獲取最近的一個紋理樣本GL_LINEAR
根據紋理座標,採用2次取樣方法,獲取紋理樣本GL_NEAREST_MIPMAP_NEAREST
根據紋理座標,從最近的mip貼圖層中獲取一個點樣本GL_NEAREST_MIPMAP_LINEAR
根據紋理座標,獲取2個最近的mip圖層的取樣點,並取2個樣本之間的插值GL_LINEAR_MIPMAP_NEAREST
根據紋理座標,採用二次線性插值從最近的mip層中獲取一個點樣本GL_LINEAR_MIPMAP_LINEAR
根據紋理座標,獲取最近2個mip圖層中採用二次線性獲取的採用點,並取2個採用點之間的插值
自動生成Mip貼圖
OpenGL ES 3.0 中提供了一個自動生成mip貼圖的方法glGenerateMipmap
;
紋理座標的封裝
紋理封裝方式(Texture wrap modes
)被用於指定超出紋理座標限定範圍[0.0,1.0]的行為。通過glTexParameter[i|f][v]
可以設定紋理封裝方式。這些mode可以獨立設定s座標軸,t座標軸和r座標軸。例如GL_TEXTURE_WRAP_S
mode定義了s座標超出[0.0,1.0]的行為。同樣的GL_TEXTURE_WRAP_T
和GL_TEXTURE_WRAP_R
分別定義了t座標和r座標的超出[0.0,1.0]的行為。
在OpenGL ES 3.0 中,有3中mode值可以設定。
GL_REPEAT 重複紋理
GL_CLAMP_TO_EDGE 限定讀取紋理的邊緣
GL_MIRRORED_REPEAT 重複紋理和映象
紋理的調配
紋理調配控制輸入的R,RG,RGB或RBGA紋理中的顏色分量在著色器中讀取時如何對映到分量。通過呼叫glTexParameter[i|f][v]
來設定紋理調配,key可以設定為GL_TEXTURE_SWIZZLE_R, GL_TEXTURE_SWIZZLE_G, GL_TEXTURE_ SWIZZLE_B, 或 GL_TEXTURE_SWIZZLE_A,而value可能分別從R,G,B,A分量讀取的GL_RED, GL_GREEN, GL_BLUE, 或 GL_ALPHA。也可以通過GL_ZERO和GL_ONE將值設定為0或1
紋理細節級別
在一些程式中,在所有的紋理mip貼圖都可以使用前就能夠開始在螢幕上進行顯示的功能是很有用的。比如GPS的程式,可以先顯示低等級的mip貼圖,等到所有的mip貼圖都下載後,在顯示高清的mip貼圖。可以通過glTexParameter[i|f][v]
中的GL_TEXTURE_BASE_LEVEL
來設定可以被使用的最大mip貼圖等級。預設是0。同樣的,GL_TEXTURE_MAX_LEVEL
設定了最小的mip貼圖等級,預設是1000。
為了選擇用於渲染的mip貼圖級別,OpenGL ES自動計算一個細節級別(LOD)的值。這個浮點值決定了哪一mip貼圖級別被篩選出來。一個程式能夠控制LOD值的最大和最小值。通過設定GL_TEXTURE_MIN_LOD
和GL_TEXTURE_MAX_LOD
。
在著色器中使用紋理
// Vertex shader
#version 300 es
layout(location = 0) in vec4 a_position;
layout(location = 1) in vec2 a_texCoord;
out vec2 v_texCoord;
void main()
{
gl_Position = a_position;
v_texCoord = a_texCoord;
}
// Fragment shader
#version 300 es
precision mediump float;
in vec2 v_texCoord;
layout(location = 0) out vec4 outColor;
uniform sampler2D s_texture;
void main() {
outColor = texture( s_texture, v_texCoord );
}
碎片著色器中的texture
函式,表示從指定的紋理單元中,根據輸入的座標,映射出紋理中相應的vec4
顏色。
vec4 texture(sampler2D sampler, vec2 coord[, float bias])
sampler 取樣器,指定的紋理單元的編號
coord 2D紋理的座標
bias 可選引數,提供用於紋理讀取的mip貼圖偏值。這執行著色器明確偏置用於mip貼圖選擇的LOD計算值
在sampler引數需要傳入的紋理單元編號前,需要先啟用一個紋理單元,並將紋理繫結到該紋理單元上。使用glActiveTexture
進行啟用
void glActiveTexture(GLenum texture)
texture 啟用紋理單元,GL_TEXTURE0, GL_TEXTURE1, ... , GL_TEXTURE31分別代表紋理單元0到31
使用glActiveTexture
啟用一個紋理單元后,便可使用glBindTexture
將指定的紋理繫結到該紋理單元上。具體程式碼如下:
// Get the sampler locations
userData->samplerLoc = glGetUniformLocation(
userData->programObject,
“s_texture”);
// ...
// Bind the texture
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, userData->textureId);
// Set the sampler texture unit to 0
glUniformli(userData->samplerLoc, 0);
載入立體貼圖,3D紋理和2D紋理陣列,也是同樣的操作,對應的texture
分別為
vec4 texture(samplerCube sampler, vec3 coord[, float bias])
vec4 texture(sampler3D sampler, vec3 coord[, float bias])
vec4 texture(sampler2DArray sampler, vec3 coord[, float bias])
壓縮紋理
OpenGL ES 3.0 支援2中壓縮演算法-EAC和ETC2。EAC版本用於壓縮1到2頻道的資料。ETC2版本用於壓縮3到4頻道的資料。使用glCompressedTexImage2D
可以讀取壓縮的2D紋理和立體貼圖紋理。使用glCompressedTexImage3D
讀取已壓縮的2D紋理陣列。注意:ETC2/EAC不支援3D紋理壓縮,但是使用glCompressedTexImage3D可以載入指定供應商的3D紋理壓縮格式
void glCompressedTexImage2D(GLenum target, GLint level,
GLenum internalFormat,
GLsizei width, GLsizei height,
GLint border, GLsizei imageSize,
const void *data)
void glCompressedTexImage3D(GLenum target, GLint level,
GLenum internalFormat,
GLsizei width, GLsizei height,
GLsizei depth, GLint border,
GLsizei imageSize,
const void *data)
target 指定紋理目標
level 指定載入的mip級別,預設是0
internalFormat 紋理的內部儲存格式
width 圖片的畫素寬度
height 圖片的畫素高度
depth 圖片的畫素深度
border 必須設定為0
imageSize 圖片的位元組大小
data 壓縮後的圖片資料
可以通過glGetIntegerv
傳入GL_COMPRESSED_TEXTURE_FORMATS
進行查詢支援的壓縮格式,該函式會返回一個GLenum
的陣列。
紋理子影象規範
在使用glTexImage2D
更新一張紋理圖片後,可能需要更新圖片的一部分。這時候可以使用glTexSubImage2D
來來自2D紋理影象的一部分。
void glTexSubImage2D (GLenum target, GLint level,
GLInt xoffset, GLint yoffset
GLsizei width, GLsizei height,
GLenum format, Glenum type,
const void *pixels)
target 紋理目標
level mip貼圖級別
xoffset 開始進行更新的x序列號
yoffset 開始進行更新的y序列號
width 進行更新的影象子區域的寬度
heigth 進行更新的影象子區域的高度
formate 輸入紋理資料的格式
type 輸入畫素資料的型別
pixels 包含影象子區域的畫素資料
這個函式將會更新區域(xoffset,yoffset)到(xoffset+width-1,yoffset+height-1)。注意:使用這個函式,紋理必須依據被指定,而且子圖片的範圍必須在指定的紋理圖片之內,pixels的對其方式必須被指定為GL_UNPACK_ALIGNMENT
同樣的原理,可以使用glCompressedTexSubImage2D
更新壓縮過的圖片,使用它glTexSubImage3D
更新3D紋理或2D紋理陣列,使用glCompressedTexSubImage3D
更新壓縮過的2D紋理陣列。
從顏色緩衝區複製紋理資料
glReadBuffer
指定被複制的顏色快取區。然後通過glCopyTexImage2D, glCopyTexSubImage2D,和 glCopyTexSubImage3D
從指定的顏色緩衝區張讀取紋理資料
取樣器物件
為了減少大量紋理上使用相同的設定的開銷。引入了取樣器物件,將取樣器狀態與紋理狀態分離。簡而言之,所有可用glTexParameter[i|f][v]
設定都可以對取樣器物件進行設定。可以在一次函式呼叫中與紋理單元繫結使用。
glGenSamplers
用於建立採用器,使用glDeleteSamplers
刪除取樣器。使用glBindSampler
將取樣器與紋理單元進行繫結。使用glSamplerParameter[f|i][v]
設定取樣器引數。
不可變紋理
由於應用程式使用glTexImage2D
和glTexImage3D
等函式獨立指定紋理的每個mip貼圖的級別。這導致了驅動程式無法在繪圖之前確定紋理是否完全指定。也就是說,它必須檢查每一個mip貼圖級別或者子影象的格式是否相符、每一個級別的大小是否正確以及是否有足夠的記憶體。這種繪圖時檢查可能代價很高,而使用不可變紋理可以避免這種情形。
不可變紋理的思路很簡單:程式在載入資料之前指定紋理的格式和大小。OpenGL ES的驅動可以提前進行一致性和記憶體檢查。一旦紋理不可變,它的格式和尺寸也就不能改變。然而程式仍然可以通過使用glTexSubImage2D, glTexSubImage3D,或 glGenerateMipMap
來載入紋理。
為了建立不可變紋理,程式必須先使用glBindTexture
綁定當前紋理,在呼叫glTexStorage2D
或glTexStorage3D
建立不可變的儲存空間。
作者:Zsj_Sky
連結:https://www.jianshu.com/p/45181657e339
來源:簡書
簡書著作權歸作者所有,任何形式的轉載都請聯絡作者獲得授權並註明出處。