OpenGL ES on iOS --- 2D紋理
簡(jiǎn)介
紋理是用來豐富我們繪制物體細(xì)節(jié)的,它可以是一張2D圖片(除了圖像外,紋理也被用來存儲(chǔ)大量數(shù)據(jù),傳遞到著色器上),就像貼圖一樣貼在繪制的物體上.
紋理屬性
紋理坐標(biāo)
為了將紋理映射到繪制的物體上,我們需要指定 某個(gè)頂點(diǎn)對(duì)應(yīng)著 紋理的那個(gè)位置. 通過紋理坐標(biāo),標(biāo)明頂點(diǎn)從紋理圖像那一部分采樣,之后在圖形的其它片段進(jìn)行片段插值.
紋理坐標(biāo)系和頂點(diǎn)坐標(biāo)系有所不同,頂點(diǎn)坐標(biāo)系 (0,0)點(diǎn)位于窗口中心. 紋理坐標(biāo)系 (0,0)點(diǎn)位于 紋理左下角.


紋理環(huán)繞方式
當(dāng)我們將頂點(diǎn)位置設(shè)置到紋理坐標(biāo)之外時(shí),則需要設(shè)置紋理環(huán)繞方式 來顯示紋理圖案
| 環(huán)繞屬性 | 效果 |
|---|---|
| GL_REPEAT | 重復(fù)紋理圖案(默認(rèn)) |
| GL_MIRRORED_REPEAT | 鏡像重復(fù)紋理圖案 |
| GL_CLAMP_TO_EDGE | 將紋理鎖定在0~1之間,超出部分重復(fù)紋理邊緣圖案,產(chǎn)生拉伸效果 |
| GL_CLAMP_TO_BORDER | 超出部分為用戶指定邊緣顏色 |

紋理環(huán)繞函數(shù)
glTexParameteri (GLenum target, GLenum pname, GLint param);
參數(shù):
target: 指定紋理目標(biāo),若為2D紋理 則為 GL_TEXTURE_2D
pname: 對(duì)應(yīng)的紋理坐標(biāo)軸(這里 s,t,r 對(duì)應(yīng) x,y,z) GL_TEXTURE_WRAP_S ,GL_TEXTURE_WRAP_T
param: 環(huán)繞方式,填入上面的方式.
對(duì)2D紋理時(shí),必須對(duì) s,t坐標(biāo)軸都進(jìn)行設(shè)置. 若是設(shè)置 GL_CLAMP_TO_BORDER 形式,則還需要額外設(shè)置 環(huán)繞顏色
float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);
紋理過濾
紋理坐標(biāo)是不依賴于 紋理大小和分辨率的, 1就表示紋理的邊緣.但是物體和和紋理大小可能不一致,這就造成了對(duì)紋理的放大和拉伸.這時(shí)如何將紋理像素映射到紋理坐標(biāo)上,就需要我們?cè)O(shè)置. 該屬性就是 紋理過濾.
紋理過濾有很多種,下面是最重要的兩種
鄰近過濾
GL_NEAREST 是默認(rèn)的紋理過濾方式,它會(huì)選擇距離 紋理坐標(biāo)最近的像素點(diǎn)作為樣本顏色.當(dāng)紋理被放大時(shí),會(huì)有顆粒感

線性過濾
GL_LINEAR, 會(huì)基于當(dāng)前紋理坐標(biāo)附近的像素點(diǎn)計(jì)算一個(gè)插值,也就是附近紋理的混合色,離得越近的像素點(diǎn),顏色貢獻(xiàn)越大,當(dāng)紋理被放大時(shí),會(huì)比臨近過濾更平滑.


過濾函數(shù)
我們需要對(duì)放大(Magnify)和縮小(Minify)的情況設(shè)置過濾效果
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
多級(jí)漸遠(yuǎn)紋理
在3D世界,根據(jù)物體的遠(yuǎn)近不同,物體也存在縮放的效果,要顯示不同的分辨率,若都使用相同的分辨率,一是會(huì)使物體產(chǎn)生不真實(shí)的效果,二是會(huì)造成內(nèi)存的浪費(fèi). 在研究3D紋理時(shí)再說.~~
紋理代碼
首先是設(shè)置 上下文,著色器,程序?qū)ο?..的方法.
context1 = [[EAGLContext alloc] initWithAPI:(kEAGLRenderingAPIOpenGLES3)];
BOOL isSetCOntextRight = [EAGLContext setCurrentContext:context1];
if (!isSetCOntextRight) {
printf("設(shè)置Context失敗");
}
NSString* verStr = [[NSBundle mainBundle] pathForResource:@"Texture2D_Vert.glsl" ofType:nil];
NSString* fragStr = [[NSBundle mainBundle]pathForResource:@"Texture2D_Frag.glsl" ofType:nil];
program1 = createGLProgramFromFile(verStr.UTF8String, fragStr.UTF8String);
glUseProgram(program1);
//創(chuàng)建,綁定渲染緩存 并分配空間
glGenRenderbuffers(1, &renderBuf1);
glBindRenderbuffer(GL_RENDERBUFFER, renderBuf1);
// 為 color renderbuffer 分配存儲(chǔ)空間
[context1 renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
//創(chuàng)建,綁定幀緩存 并分配空間
glGenFramebuffers(1, &frameBuf1);
// 設(shè)置為當(dāng)前 framebuffer
glBindFramebuffer(GL_FRAMEBUFFER, frameBuf1);
// 將 _colorRenderBuffer 裝配到 GL_COLOR_ATTACHMENT0 這個(gè)裝配點(diǎn)上
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_RENDERBUFFER, renderBuf1);
glClearColor(1.0, 0.0, 0.0, 1.0);
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, self.frame.size.width, self.frame.size.height);
然后是 繪制物體的代碼
float verData[] = {
// 位置 顏色 紋理坐標(biāo)
0.5f,0.5f,0.0f, 1.0f,0.0f,0.0f, 1.0f,1.0f,
0.5f,-0.5f,0.0f, 0.0f,1.0f,0.0f, 1.0f,0.0f,
-0.5f,-0.5f,0.0f, 0.0f,0.0f,1.0f, 0.0f,0.0f,
-0.5f,0.5f,0.0f, 1.0f,1.0f,0.0f, 0.0f,1.0f,
};
unsigned int indices[] = {
0,1,3,
1,2,3
};
glGenVertexArrays(1, &VAO1);
glGenBuffers(1, &VBO1);
glGenBuffers(1, &EBO1);
glBindVertexArray(VAO1);
glBindBuffer(GL_ARRAY_BUFFER, VBO1);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO1);
glBufferData(GL_ARRAY_BUFFER, sizeof(verData), verData, GL_STATIC_DRAW);
glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)0);
glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(3*sizeof(float)));
glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, 8*sizeof(float), (void*)(6*sizeof(float)));
glEnableVertexAttribArray(0);
glEnableVertexAttribArray(1);
glEnableVertexAttribArray(2);
紋理設(shè)置~~
stbi_image 一個(gè)非常流行的單頭文件圖像加載庫 stbi_image
//因?yàn)槭褂?stbi 函數(shù)導(dǎo)入的圖片會(huì)顛倒,所以需要將其擺正
stbi_set_flip_vertically_on_load(true);
NSString* imPath = [[NSBundle mainBundle] pathForResource:@"wall.jpg" ofType:nil];
int width,height,nrChannels;
//加載圖片
unsigned char * imdata = stbi_load(imPath.UTF8String, &width, &height, &nrChannels, 0);
//創(chuàng)建 紋理
unsigned int texture;
glGenTextures(1, &texture);
//激活紋理單元0
glActiveTexture(GL_TEXTURE0);
//綁定紋理
glBindTexture(GL_TEXTURE_2D, texture);
//將圖像傳入紋理
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, imdata);
//glGenerateMipmap(GL_TEXTURE_2D);
//設(shè)置紋理環(huán)繞和紋理過濾
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//將不用的圖像釋放
stbi_image_free(imdata);
//將紋理作為統(tǒng)一變量傳入顯存
glUniform1i(glGetUniformLocation(program1, "outTexture"), 0);
//這里PNG格式 通過stbi_load 拉入時(shí) 會(huì)導(dǎo)致產(chǎn)生 BGRA格式(我也不大清楚原因,懂的朋友 告告我 先O(∩_∩)O謝謝了) ,這時(shí) 圖片作為紋理顯示時(shí)色彩會(huì)出錯(cuò).所以將其轉(zhuǎn)換一蛤~
NSString* imPath1 = [[NSBundle mainBundle] pathForResource:@"face.png" ofType:nil];
int width1,height1,nrChannels1;
unsigned char * imdata1 = stbi_load(imPath1.UTF8String, &width1, &height1, &nrChannels1, STBI_rgb_alpha);
for (int i = 0; i<width1*height1; i++ ) {
char tR = imdata1[i*4+2];
imdata1[i*4+2] = imdata1[i*4];
imdata1[i*4] = tR;
}
unsigned int texture1;
glGenTextures(1, &texture1);
glActiveTexture(GL_TEXTURE1); //必須先寫這個(gè)再綁定
glBindTexture(GL_TEXTURE_2D, texture1);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width1, height1, 0, GL_RGBA, GL_UNSIGNED_BYTE, imdata1);
glGenerateMipmap(GL_TEXTURE_2D);
stbi_image_free(imdata1);
glUniform1i(glGetUniformLocation(program1, "outTexture1"), 1);
//繪制顯示
glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0);
[context1 presentRenderbuffer:GL_RENDERBUFFER];
片段著色器代碼
#version 300 es
precision mediump float;
in vec2 outTexCoord;
uniform sampler2D outTexture;
uniform sampler2D outTexture1;
in vec3 outColor;
out vec4 FragColor;
void main()
{
FragColor = mix(texture(outTexture,outTexCoord),texture(outTexture1,outTexCoord),0.2);
}
GLSL內(nèi)建的mix函數(shù)需要接受兩個(gè)值作為參數(shù),并對(duì)它們根據(jù)第三個(gè)參數(shù)進(jìn)行線性插值。如果第三個(gè)值是0.0,它會(huì)返回第一個(gè)輸入;如果是1.0,會(huì)返回第二個(gè)輸入值。0.2會(huì)返回80%的第一個(gè)輸入顏色和20%的第二個(gè)輸入顏色,即返回兩個(gè)紋理的混合色。
函數(shù)補(bǔ)充說明
glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);
target 紋理目標(biāo) 設(shè)置為GL_TEXTURE_2D意味著會(huì)生成與當(dāng)前綁定的紋理對(duì)象在同一個(gè)目標(biāo)上的紋理
level 為紋理指定多級(jí)漸遠(yuǎn)紋理的級(jí)別,0表示基本級(jí)別
internalformat 希望將紋理存儲(chǔ)為何等格式
width height 圖像寬高
border 設(shè)置為0 說是歷史遺留問題
format 源圖格式 type 數(shù)據(jù)格式
pixels 圖像數(shù)據(jù)
紋理單元
在代碼中 使用 glUniform1i方法進(jìn)行紋理傳遞 是因?yàn)?紋理單元 這個(gè)概念.
使用glUniform1i可以為紋理采樣器分配一個(gè)位置值,通過把紋理單元賦值給采樣器,就可以一次綁定多個(gè)紋理.
OpenGL至少保證有16個(gè)紋理單元供你使用,也就是說你可以激活從GL_TEXTURE0到GL_TEXTRUE15。它們都是按順序定義的,所以我們也可以通過GL_TEXTURE0 + 8的方式獲得GL_TEXTURE8,這在當(dāng)我們需要循環(huán)一些紋理單元的時(shí)候會(huì)很有用。
需要注意的是 在綁定紋理時(shí) 先要激活紋理單元才可以
