前言
前面文章中叹誉,我們通過為頂點添加顏色,來創(chuàng)建有趣的圖形宣吱,但是現(xiàn)實世界中的物體(例如磚墻,草坪等等)表面是有很多細節(jié)的瞳别,如果我們想要讓圖形看起來更加真實征候,必須要有足夠的頂點,從而能指定足夠多的顏色祟敛,描述圖形的細節(jié)疤坝。但是繪制一個立方體的時候,我們就需要繪制六個面垒棋,已經感覺很繁瑣卒煞,更不用說為每個面繪制不同的細節(jié)痪宰。
所以接下來我們介紹讓圖形看起來更加真實的技術------紋理叼架。
紋理類型包含以下幾種:2D紋理,立方圖紋理衣撬,3D紋理乖订,2D紋理數(shù)組,1D紋理具练,下面我們介紹最基本的2D紋理乍构,來幫助我們了解紋理這個概念。
下面我們在正方形上加載一個紋理扛点。
片段著色器:
#version 300 es
precision mediump float;
in vec2 oTextCoord;
uniform sampler2D texture1;
out vec4 fragColor;
void main() {
fragColor = texture(texture1,oTextCoord);
}
片段著色器中哥遮,我們用sampler2D 這個類表示紋理,texture函數(shù)加載紋理像素陵究,需要傳入兩個對象:紋理對象眠饮,紋理坐標。
這里我們說明下紋理坐標的概念:
這里我們使用的是2D紋理铜邮,紋理坐標在X軸和Y軸仪召,范圍在0~1之間,紋理坐標標明從紋理圖像的哪個部分采樣松蒜,通過使用紋理坐標把紋理圖像映射到屏幕的過程叫做紋理映射也叫紋理貼圖扔茅。
代碼如下:
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect {
[super glkView:view drawInRect:rect];
_esContext.width = view.drawableWidth;
_esContext.height = view.drawableHeight;
GLfloat factor = (view.frame.size.width / view.frame.size.height) * 0.5;
//(x,y,z) (s,t)
float position[] = {
-0.5, factor, 0.0, 0.0, 0.0,
-0.5, -factor, 0.0, 0.0, 1.0,
0.5, -factor, 0.0, 1.0, 1.0,
0.5, factor, 0.0, 1.0, 0.0,
};
//開啟深度測試,為了確定繪制的時候哪一個面繪制在上面
glClear(GL_COLOR_BUFFER_BIT);
GLuint vboIndex = 0;
glGenBuffers(1, &vboIndex);
glBindBuffer(GL_ARRAY_BUFFER, vboIndex);
glBufferData(GL_ARRAY_BUFFER, sizeof(position), position, GL_STATIC_DRAW);
GLuint positionIndex = glGetAttribLocation(_esContext.program, "vPosition");
GLuint textCoordIndex = glGetAttribLocation(_esContext.program, "textCoord");
GLuint textureLocation = glGetUniformLocation(_esContext.program, "texture1");
glEnableVertexAttribArray(positionIndex);
glEnableVertexAttribArray(textCoordIndex);
GLuint offset = 3 * sizeof(float);
glVertexAttribPointer(positionIndex, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), NULL);
glVertexAttribPointer(textCoordIndex, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (const void *)offset);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self.baseTexture.name);
glUniform1i(textureLocation, 0);
_esContext.drawFunc(&_esContext);
//關閉頂點屬性
glDisableVertexAttribArray(positionIndex);
glDisableVertexAttribArray(textCoordIndex);
glDeleteBuffers(1, &vboIndex);
}
代碼中注意以下兩點:
1.這里我們設置頂點坐標的時候用上了屏幕寬高比秸苗,這是因為手機屏幕寬高不一樣召娜,如果按照0.5的比例來渲染的話,圖像會拉伸惊楼,所以需要乘以這個寬高比玖瘸。
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, self.baseTexture.name);
glUniform1i(textureLocation, 0);
這里的baseTexture是通過GLkit庫提供的GLKTextLoader類加載的紋理對象吐句。得益于這個API,幫我們省去了很多工作店读,示例代碼中注釋的部分還含有自己生成紋理對象蠢古,并綁定使用的過程实幕。
紋理環(huán)繞
上面我們說的紋理坐標范圍是0~1如果我們超過了這個范圍,怎么辦呢,OpenGL ES默認的行為是重復這個紋理圖像(但OpenGL ES提供了更多的選擇:
#define GL_REPEAT 對紋理的默認行為前普。重復紋理圖像。
#define GL_CLAMP_TO_EDGE 紋理坐標會被約束在0到1之間假栓,超出的部分會重復紋理坐標的邊緣情臭,產生一種邊緣被拉伸的效果。
#define GL_MIRRORED_REPEAT 和GL_REPEAT一樣趴久,但每次重復圖片是鏡像放置的丸相。
紋理過濾:
紋理坐標不依賴于屏幕分辨率,它可以是任意浮點值彼棍,當你有一個很大的物體灭忠,但是紋理的分辨率很低的時候,ES需要知道怎樣將紋理映射到屏幕座硕,ES這里提供了對于紋理過濾的選項弛作。這里我們介紹最重要的兩項:
1.GL_NEARST(鄰近過濾),這個是默認的紋理過濾方式华匾,當設置為這個選項時映琳,ES會選擇中心點最接近紋理坐標的那個像素。如下圖所示
2.GL_LINEAR,當設置為這個選項時蜘拉,它會基于紋理坐標附近的紋理像素萨西,計算一個差值。一個紋理像素的中心距離紋理坐標越近旭旭,那么它在插值的計算中谎脯,占比越大。如下圖所示:
看一下兩種方式對最終成像效果的影響:
我們可以看到GL_NEARST方式的紋理具有顆粒感您机,可以看清像素穿肄,但是GL_LINEAR效果比較真實,過渡比較自然际看。
mipmap貼圖
想象一下咸产,假設我們繪制一段城墻,這個城墻表面是由一個個磚塊紋理拼接而成仲闽,近處的看起來效果很自然脑溢,但是距離很遠的時候,由于遠處的物體可能只產生很少的片段,OpenGL ES從高分辨率紋理中為這些片段獲取正確的顏色值就很困難屑彻,因為它需要對一個跨過紋理很大部分的片段只拾取一個紋理顏色验庙,在小物體上會產生不真實的感覺,而且對它們使用高分辨率的紋理也會浪費資源社牲,造成性能問題粪薛。
OPenGL ES使用一種叫做多級漸遠紋理(Mipmap)的概念來解決這個問題,它簡單來說就是一系列的紋理圖像搏恤,后一個紋理圖像是前一個的二分之一违寿。多級漸遠紋理背后的理念很簡單:距觀察者的距離超過一定的閾值,OpenGL ES 會使用不同的多級漸遠紋理熟空,即最適合物體的距離的那個藤巢。由于距離遠,解析度不高也不會被用戶注意到息罗。同時掂咒,多級漸遠紋理另一加分之處是它的性能非常好。
mipmap也有幾種過濾方式:
GL_NEAREST_MIPMAP_NEAREST 使用最鄰近的多級漸遠紋理來匹配像素大小迈喉,并使用鄰近插值進行紋理采樣
GL_LINEAR_MIPMAP_NEAREST 使用最鄰近的多級漸遠紋理級別绍刮,并使用線性插值進行采樣
GL_NEAREST_MIPMAP_LINEAR 在兩個最匹配像素大小的多級漸遠紋理之間進行線性插值,使用鄰近插值進行采樣
GL_LINEAR_MIPMAP_LINEAR 在兩個鄰近的多級漸遠紋理之間使用線性插值弊添,并使用線性插值進行采樣
1.生成和綁定紋理
GLuint textId = 0;
glGenTextures(1, &textId);
glBindTexture(GL_TEXTURE_2D, textId)
2.加載紋理數(shù)據(jù)
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 750, 750, 0, GL_RGBA, GL_UNSIGNED_BYTE, (unsigned char *)_imageData.bytes);
- 設置紋理過濾方式
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_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
4.綁定紋理到著色器
glUniform1i(textureLocation, 0);
使用紋理大致分為上面幾步录淡,示例程序中有兩種方式捌木,第一種簡易方式是借助GLKit.后面一種是實際的操作過程油坝。