紋理的基礎(chǔ)知識
2D 紋理
2d紋理是OpenGlES中最基礎(chǔ)和普遍的一種紋理結(jié)構(gòu)乍迄。一個2d紋理弧呐,就是圖片的數(shù)據(jù)的一個二維數(shù)組闸迷。紋理中每一個獨立的數(shù)據(jù)單元被稱為texels
(“texture pixels”的縮寫)。OpenGLES中紋理圖像數(shù)據(jù)可變被許多種不同的基本格式描繪泉懦。
根據(jù)圖像的基本類型和圖像的數(shù)據(jù)類型稿黍,決定了圖像中的每一個texel。
當渲染2D紋理是崩哩,紋理的坐標將成為索引巡球。2D紋理的坐標軸別是(s,t)或者是(u邓嘹,v)酣栈。這個坐標軸是規(guī)范化的,即坐標的大小在0.0~1.0之間汹押。取紋理圖像的左下角為坐標原點(0.0矿筝,0.0)
坐標超過[0.0,1.0]的范圍是允許的,而對與超出范圍的內(nèi)容的操作棚贾,取決于紋理的包裝方式(wrapping mode)
立方體貼圖紋理(Cubemap Texttures
)
OpenGL ES 3.0 支持立方體貼圖紋理窖维。最基本的,一個立方體紋理由6個獨立的2D紋理面組成妙痹。每一個2D紋理都是立方體的一個平面铸史。雖然立方體貼圖在3D渲染中有了多元化的進步,但是最普遍的還是被用在環(huán)境貼圖中怯伊。典型的琳轿,在環(huán)境貼圖中,將一臺攝像機放置場景的中心點耿芹,拍攝6個方位的圖像并保存崭篡。
立體貼圖中的Texels通過使用一個3D的向量(s,t吧秕,r)來定位琉闪。定位時,先根據(jù)r確定是6個平面的哪一個平面砸彬,在根據(jù)(s塘偎,t)確定平面上的一點疗涉。
立體貼圖紋理的每一個平面都必須是正方形的。
3D紋理
3D紋理可以看做是2D紋理多個切片的一個數(shù)組吟秩。將2D紋理當做的一個面,將面疊加起來绽淘,便是一個立體涵防。這個立體就是3D紋理。3D紋理使用一個3元坐標(s,t,r)沪铭,r坐標決定哪一個切面壮池,(s,t)決定在這個切面中的坐標。
2D紋理數(shù)組
2D紋理數(shù)組和3D紋理很類似杀怠,當時一個3D紋理是表示一張圖像椰憋,而一個2D紋理數(shù)組,則是表示一組2D圖像形成的動畫序列赔退。
紋理對象和加載紋理
一個紋理對象是一個存儲將要被渲染的紋理數(shù)據(jù)的容器橙依,包括了圖片數(shù)據(jù),過濾方式和封裝方式硕旗。在OpenGLES中窗骑,一個紋理對象通過一個非負整數(shù)來標識,這個數(shù)是這個紋理對象的一個句柄漆枚。生產(chǎn)紋理的函數(shù)是glGenTextures
void glGenTextures(GLsizei n, GLuint *textures)
n 指定生產(chǎn)的紋理對象的數(shù)量
textures 一個非負整數(shù)數(shù)組呻袭,用了存儲n個紋理對象的ID
通過glGenTextures
創(chuàng)建了空的容器啥酱,將會被拿去加載紋理數(shù)據(jù)和參數(shù)。當程序不在需要時,需要將紋理對象刪除连舍。通過glDeleteTextures
實現(xiàn)。
void glDeleteTextures(GLsizei n, Gluint *textures)
n 指定刪除的紋理對象的數(shù)量
textures 一個非負整數(shù)數(shù)組遭贸,用了存儲n個紋理對象的ID
當一個紋理對象的id被glGenTextures
創(chuàng)建后斩狱,程序必須將該紋理進行綁定。只有該紋理被綁定后痘拆,后續(xù)的操作例如glTexImage2D
和glTexParameter
才能影響到綁定的紋理對象仰禽。執(zhí)行綁定功能的函數(shù)是glBindTexture
void glBindTexture(GLenum target, GLuint texture)
target 將紋理對象和目標GL_TEXTURE_2D, GL_TEXTURE_3D,GL_TEXTURE_2D_ARRAY或GL_TEXTURE_CUBE_MAP進行綁 定
texture 進行綁定的紋理的句柄
一旦紋理被綁定到一個指定的紋理目標,這個紋理對象將會一直維持對這個目標的綁定直到被刪除纺蛆。在創(chuàng)建一個紋理目標并綁定后吐葵,下一步便是使用紋理去加載圖片數(shù)據(jù)。一個基本的加載2D和立體貼圖紋理的函數(shù)是glTexImage2D
桥氏。另外温峭,在OpenGl ES 3.0 中有幾個可以替代的函數(shù),這些函數(shù)被用于指定的2D紋理字支,包括使用不可變紋理glTexStorage2D
和glTexSubImage2D
的結(jié)合凤藏。
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 紋理存儲的內(nèi)部格式揖庄。包括了無大小限制的內(nèi)部格式和有大小限制的格式栗菜。無大小內(nèi)部格式包括 GL_RGBA, GL_RGB, GL_LUMINANCE_ALPHA GL_LUMINANCE, GL_ALPHA 有大小的內(nèi)部格式包括 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 輸入紋理數(shù)據(jù)的格式,可能是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 輸入的像素數(shù)據(jù)的類型蹄梢;可能是
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 包含了圖片的像素數(shù)據(jù)疙筹。這個數(shù)據(jù)必須包含了(width*height)個像素,每個像素根據(jù)指定的格式和類型占用一定的字節(jié)禁炒,像素行必須用GL_UNPACK_ALIGNMENT設(shè)置glPixelStorei設(shè)置對齊
和紋理加載相關(guān)的函數(shù)而咆,還有一個glPixelStorei
,該函數(shù)決定了像素數(shù)據(jù)存儲方式。
void glPixelStorei(GLenum pname , GLint param)
pname 指定像素存儲類型幕袱。下面的可選參數(shù)將影響當glTexImage2D, glTexImage3D, glTexSubImage2D, 和 glTexSubImage3D被調(diào)用時暴备,數(shù)據(jù)如何從內(nèi)存中被解包:GL_UNPACK_ROW_LENGTH, GL_UNPACK_IMAGE_HEIGHT, GL_UNPACK_SKIP_PIXELS, GL_UNPACK_SKIP_ROWS, GL_UNPACK_SKIP_IMAGES, GL_UNPACK_ALIGNMENT
下列可選參數(shù)將影響glReadPixels被調(diào)用時,數(shù)據(jù)如何被打包讀入內(nèi)存:GL_PACK_ROW_LENGTH, GL_PACK_IMAGE_HEIGHT, GL_PACK_SKIP_PIXELS, GL_PACK_SKIP_ROWS, GL_PACK_SKIP_IMAGES, GL_PACK_ALIGNMENT
param 為打包或解包指定的整形數(shù)值们豌。
GL_PACK_xxxxx對紋理的更新沒有任何影響涯捻。實際上,除了GL_UNPACK_ALIGNMENT
玛痊,其他的可選選項很少被使用
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
意味著每一個像素行從字節(jié)邊界開始汰瘫,換而言之,數(shù)據(jù)被緊密打包擂煞。該值的默認值是4混弥,表示像素行被認定從4字節(jié)的邊界開始。
紋理過濾和mip貼圖
紋理坐標用于生成一個2D索引对省,從紋理貼圖中讀取蝗拿。當縮小和放大過濾器設(shè)置為GL_NEAREST
時,就會發(fā)生這樣的情況:一個texels將在提供的紋理坐標位置上讀取蒿涎。這樣稱作點采樣或最近采樣哀托。
然而,最近采樣可能產(chǎn)生視覺偽像劳秋。一個偽像的產(chǎn)生是因為一個圖像在屏幕中變小仓手,導致像素之間的紋理坐標產(chǎn)生了巨大跳躍。這造成了從一個張巨大的紋理圖中讀取了少量的采用點玻淑,從而導致了鋸齒偽像的產(chǎn)生嗽冒。
解決這一問題的方法是,采用mip貼圖补履。所謂的mip貼圖添坊,就是通過當前的屏幕分辨率,生成較低的屏幕分辨率的圖片箫锤,形成一個圖片鏈贬蛙。比如現(xiàn)在有一張64*64的圖片雨女,采用mip貼圖,會生成32*32,16*16,8*8,4*4,2*2,1*1,等低分辨率的圖片阳准。這些圖片中氛堕,最原始的被稱為0級層。每低一個低級的層溺职,都是由上等級的圖片生成的岔擂,像素合并規(guī)則是,將每個圖層的2*2的像素點相加取平均值浪耘,作為下一層的一個像素點。
縮小過濾發(fā)生在屏幕中的多邊形小于紋理大大小時塑崖。放大過濾發(fā)生在屏幕中多邊大于紋理的大小時七冲。對于放大過濾,mip貼圖沒有影響规婆。對于縮小過濾澜躺,mip貼圖可以保證像素的平滑
通過glTexParameter[i|f][v]
來設(shè)置紋理的的參數(shù)
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 進行設(shè)置的紋理屬性,可以包括
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 指定屬性的參數(shù)
對于放大過濾GL_TEXTURE_MAG_FILTER
,可以設(shè)置為GL_NEAREST
或者是GL_LINEAR
抒蚜。
對于縮小過濾GL_TEXTURE_MIN_FILTER
掘鄙,可以設(shè)置的值如下
-
GL_NEAREST
表示根據(jù)紋理坐標,獲取最近的一個紋理樣本 -
GL_LINEAR
根據(jù)紋理坐標嗡髓,采用2次采樣方法操漠,獲取紋理樣本 -
GL_NEAREST_MIPMAP_NEAREST
根據(jù)紋理坐標,從最近的mip貼圖層中獲取一個點樣本 -
GL_NEAREST_MIPMAP_LINEAR
根據(jù)紋理坐標饿这,獲取2個最近的mip圖層的采樣點浊伙,并取2個樣本之間的插值 -
GL_LINEAR_MIPMAP_NEAREST
根據(jù)紋理坐標,采用二次線性插值從最近的mip層中獲取一個點樣本 -
GL_LINEAR_MIPMAP_LINEAR
根據(jù)紋理坐標长捧,獲取最近2個mip圖層中采用二次線性獲取的采用點嚣鄙,并取2個采用點之間的插值
自動生成Mip貼圖
OpenGL ES 3.0 中提供了一個自動生成mip貼圖的方法glGenerateMipmap
;
紋理坐標的封裝
紋理封裝方式(Texture wrap modes
)被用于指定超出紋理坐標限定范圍[0.0,1.0]的行為。通過glTexParameter[i|f][v]
可以設(shè)置紋理封裝方式串结。這些mode可以獨立設(shè)置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值可以設(shè)置声功。
GL_REPEAT 重復紋理
GL_CLAMP_TO_EDGE 限定讀取紋理的邊緣
GL_MIRRORED_REPEAT 重復紋理和鏡像
紋理的調(diào)配
紋理調(diào)配控制輸入的R烦却,RG,RGB或RBGA紋理中的顏色分量在著色器中讀取時如何映射到分量先巴。通過調(diào)用glTexParameter[i|f][v]
來設(shè)置紋理調(diào)配其爵,key可以設(shè)置為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將值設(shè)置為0或1
紋理細節(jié)級別
在一些程序中摇幻,在所有的紋理mip貼圖都可以使用前就能夠開始在屏幕上進行顯示的功能是很有用的横侦。比如GPS的程序,可以先顯示低等級的mip貼圖绰姻,等到所有的mip貼圖都下載后枉侧,在顯示高清的mip貼圖】裼螅可以通過glTexParameter[i|f][v]
中的GL_TEXTURE_BASE_LEVEL
來設(shè)置可以被使用的最大mip貼圖等級榨馁。默認是0。同樣的帜矾,GL_TEXTURE_MAX_LEVEL
設(shè)置了最小的mip貼圖等級翼虫,默認是1000。
為了選擇用于渲染的mip貼圖級別屡萤,OpenGL ES自動計算一個細節(jié)級別(LOD)的值珍剑。這個浮點值決定了哪一mip貼圖級別被篩選出來。一個程序能夠控制LOD值的最大和最小值死陆。通過設(shè)置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
函數(shù),表示從指定的紋理單元中翔曲,根據(jù)輸入的坐標迫像,映射出紋理中相應的vec4
顏色。
vec4 texture(sampler2D sampler, vec2 coord[, float bias])
sampler 采樣器瞳遍,指定的紋理單元的編號
coord 2D紋理的坐標
bias 可選參數(shù)闻妓,提供用于紋理讀取的mip貼圖偏值。這運行著色器明確偏置用于mip貼圖選擇的LOD計算值
在sampler參數(shù)需要傳入的紋理單元編號前掠械,需要先激活一個紋理單元由缆,并將紋理綁定到該紋理單元上。使用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紋理數(shù)組舔箭,也是同樣的操作,對應的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頻道的數(shù)據(jù)层扶。ETC2版本用于壓縮3到4頻道的數(shù)據(jù)箫章。使用glCompressedTexImage2D
可以讀取壓縮的2D紋理和立體貼圖紋理。使用glCompressedTexImage3D
讀取已壓縮的2D紋理數(shù)組镜会。注意: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 紋理的內(nèi)部存儲格式
width 圖片的像素寬度
height 圖片的像素高度
depth 圖片的像素深度
border 必須設(shè)置為0
imageSize 圖片的字節(jié)大小
data 壓縮后的圖片數(shù)據(jù)
可以通過glGetIntegerv
傳入GL_COMPRESSED_TEXTURE_FORMATS
進行查詢支持的壓縮格式戳表,該函數(shù)會返回一個GLenum
的數(shù)組桶至。
紋理子圖像規(guī)范
在使用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 進行更新的圖像子區(qū)域的寬度
heigth 進行更新的圖像子區(qū)域的高度
formate 輸入紋理數(shù)據(jù)的格式
type 輸入像素數(shù)據(jù)的類型
pixels 包含圖像子區(qū)域的像素數(shù)據(jù)
這個函數(shù)將會更新區(qū)域(xoffset,yoffset)到(xoffset+width-1价涝,yoffset+height-1)野瘦。注意:使用這個函數(shù),紋理必須依據(jù)被指定飒泻,而且子圖片的范圍必須在指定的紋理圖片之內(nèi),pixels的對其方式必須被指定為GL_UNPACK_ALIGNMENT
同樣的原理吏廉,可以使用glCompressedTexSubImage2D
更新壓縮過的圖片泞遗,使用它glTexSubImage3D
更新3D紋理或2D紋理數(shù)組,使用glCompressedTexSubImage3D
更新壓縮過的2D紋理數(shù)組席覆。
從顏色緩沖區(qū)復制紋理數(shù)據(jù)
glReadBuffer
指定被復制的顏色緩存區(qū)史辙。然后通過glCopyTexImage2D, glCopyTexSubImage2D,和 glCopyTexSubImage3D
從指定的顏色緩沖區(qū)張讀取紋理數(shù)據(jù)
采樣器對象
為了減少大量紋理上使用相同的設(shè)置的開銷。引入了采樣器對象佩伤,將采樣器狀態(tài)與紋理狀態(tài)分離聊倔。簡而言之,所有可用glTexParameter[i|f][v]
設(shè)置都可以對采樣器對象進行設(shè)置生巡“颐铮可以在一次函數(shù)調(diào)用中與紋理單元綁定使用。
glGenSamplers
用于創(chuàng)建采用器孤荣,使用glDeleteSamplers
刪除采樣器甸陌。使用glBindSampler
將采樣器與紋理單元進行綁定。使用glSamplerParameter[f|i][v]
設(shè)置采樣器參數(shù)盐股。
不可變紋理
由于應用程序使用glTexImage2D
和glTexImage3D
等函數(shù)獨立指定紋理的每個mip貼圖的級別钱豁。這導致了驅(qū)動程序無法在繪圖之前確定紋理是否完全指定。也就是說疯汁,它必須檢查每一個mip貼圖級別或者子圖像的格式是否相符牲尺、每一個級別的大小是否正確以及是否有足夠的內(nèi)存。這種繪圖時檢查可能代價很高幌蚊,而使用不可變紋理可以避免這種情形谤碳。
不可變紋理的思路很簡單:程序在加載數(shù)據(jù)之前指定紋理的格式和大小溃卡。OpenGL ES的驅(qū)動可以提前進行一致性和內(nèi)存檢查。一旦紋理不可變估蹄,它的格式和尺寸也就不能改變塑煎。然而程序仍然可以通過使用glTexSubImage2D, glTexSubImage3D,或 glGenerateMipMap
來加載紋理。
為了創(chuàng)建不可變紋理臭蚁,程序必須先使用glBindTexture
綁定當前紋理最铁,在調(diào)用glTexStorage2D
或glTexStorage3D
創(chuàng)建不可變的存儲空間。