從上一篇文章中我們了解到片元著色器是如何編寫的:
片元著色器
片元著色器shaderf.fsh
//傳遞過來的紋理坐標
varying lowp vec2 varyTextCoord;
// 紋理采樣器 (獲取對應的紋理ID)
uniform sampler2D colorMap;
void main() {
//將紋理顏色添加到對應的像素點上
gl_FragColor = texture2D(colorMap, varyTextCoord);
//返回值應該是一個vec4 即是RGBA--顏色值挟冠。
}
gl_FragColor GLSL內建變量 (賦值像素點顏色值)GLSL語言已經提前定義好的變量刃泌,有相應的特殊含義寇甸。
內建函數 GLSL提前封裝好的函數
texture2D(紋理采樣器增显,紋理坐標),獲取對應坐標紋素(讀取紋素涨享,讀取每一個像素點的顏色值)筋搏。
我們知道sampler(采樣器)是GLSL提供的可供紋理對象使用的內建數據,而且sampler通常實在片元著色器中內定義厕隧,被uniform修飾符修飾奔脐,表示這個變量是不會被修改的。
通過上面的代碼可以看到聲明sampler的類型還有一個sampler2D吁讨。這個只是代表一個二維的紋理類型髓迎。sampler1D,sampler2D,sampler3D 表示不同維度的紋理類型.
在上面的代碼中我們簡單聲明了一個紋理對象. uniform sampler2D,將一個紋理添加片元著色器中.
uniform sampler2D colorMap;
同時我們使用GLSL內建的texture函數來采樣紋理的顏色值.
gl_FragColor = texture2D(colorMap, varyTextCoord);
紋理單元
這里聲明的sampler2D變量是個uniform,我們卻沒有用glUniform給它賦值建丧,一般來講我們需要用glUniform1i()函數進行將紋理對象(數據)從CPU中傳入顯存中的著色器排龄。之所以使用glUniform1i()函數,是因為只需要給紋理采樣器傳入一個索引值(位置)即可翎朱,這樣我們就能夠在一個片元著色器中設置多個紋理橄维。
那么這個索引值就是我們接下來要介紹的‘紋理單元’:
一個紋理的位置值通常稱為一個紋理單元(Texture Unit)尺铣。一個紋理的默認紋理單元是0,它是默認的激活紋理單元争舞。紋理單元的主要目的是讓我們在著色器中可以使用多于一個的紋理凛忿。
如果我們只傳入一個紋理對象,那么倒是不用考慮紋理單元的問題竞川。但是當有多個紋理對象要傳入的時候店溢,我們必須指定紋理對象,然后在主函數用glUniform1i()函數將紋理對象一個一個綁定到著色器內部流译。
通過把紋理單元賦值給采樣器逞怨,我們可以一次綁定多個紋理,只要我們首先激活對應的紋理單元福澡。就像glBindTexture一樣,我們可以使用管理ActiveTexture激活紋理單元驹马,傳入我們需要使用的紋理單元革砸。
//在綁定紋理之前先激活紋理單元
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture);
激活紋理單元之后,接下來的glBindTexture函數調用會綁定這個紋理到當前激活的紋理單元糯累,紋理單元GL_TEXTURE0默認總是被激活.
OpenGL提供有16個紋理單元供我們使用算利,也就是說我們可以激活從GL_TEXTURE0到GL_TEXTRUE15。它們都是按順序定義的泳姐,所以我們也可以通過GL_TEXTURE0 + 8的方式獲得GL_TEXTURE8效拭,這在當我們需要循環(huán)一些紋理單元的時候會很有用。
紋理混合
GLSL內建的mix函數會將兩個紋理進行結合并輸出最終顏色值胖秒。
varying vec2 TexCoord;
uniform sampler2D ourTexture1;
uniform sampler2D ourTexture2;
void main()
{
gl_FragColor = mix(texture(ourTexture1, TexCoord), texture(ourTexture2, TexCoord), 0.2);
}
genType mix (genType x, genType y, float a)
返回線性混合的x和y缎患,如:x?(1?a)+y?a
mix函數需要接受兩個值作為參數,并對它們根據第三個參數進行線性插值(線性插值是一種針對一維數據的插值方法阎肝,它根據一維數據序列中需要插值的點的左右鄰近兩個數據點來進行數值的估計挤渔。當然了它不是求這兩個點數據大小的平均值(當然也有求平均值的情況),而是根據到這兩個點的距離來分配它們的比重的)风题。
例如:如果第三個值是0.0判导,它會返回第一個輸入;如果是1.0沛硅,會返回第二個輸入值眼刃。輸入0.2則會返回80%的第一個輸入顏色和20%的第二個輸入顏色,即返回兩個紋理的混合色摇肌。
設置多個紋理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, texture1);
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture1"), 0); // 手動設置
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, texture2);
glUniform1i(glGetUniformLocation(ourShader.Program, "ourTexture2"), 1);
其實弟头,使用glUniform1i()函數作為著色器內部和程序來進行傳入值,需要知道兩個參數涉茧,一個是在著色器內部接受信息的對象為位置赴恨。一個是外界的數據對象,嚴格來講傳入數據本身也不是這個函數做的伴栓,這個函數只是告訴著色器那個紋理對象對應哪個采樣器對象伦连。注意擂红,我們使用glUniform1i設置uniform采樣器的位置值,或者說紋理單元朦蕴。通過glUniform1i的設置篮条,我們保證每個uniform采樣器對應著正確的紋理單元
重點介紹一下glTexImage2D函數:
glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels)
參數1:紋理模式(綁定紋理對象的種類),GL_TEXTURE_1D钳垮、GL_TEXTURE_2D惑淳、GL_TEXTURE_3D
參數2:加載的層次,一般設置為0, 0表示沒有進行縮小的原始圖片等級饺窿。
參數3:紋理的顏色值GL_RGBA, 表示了紋理所采用的內部格式歧焦,內部格式是我們的像素數據在顯卡中存儲的格式,這里的GL_RGB顯然就表示紋理中像素的顏色值是以RGB的格式存儲的肚医。
參數4:紋理的寬
參數5:紋理的高
參數6:border绢馍,邊界寬度,通常為0.
參數7:format(描述了像素在內存中的存儲格式)
參數8:type(描述了像素在內存中的數據類型)
參數9:紋理數據
紋理翻轉
在使用OpenGL函數加載紋理到圖形時肠套,經常遇到紋理上下顛倒的問題舰涌。原因是因為OpenGL要求紋理坐標原點(0,0)在左下角你稚。
- 利用旋轉矩陣翻轉圖形搁痛,不翻轉紋理
即讓圖形的頂點坐標旋轉180度,而紋理坐標保持不變乾闰。
//rotate等于shaderv.vsh中的uniform屬性落追,rotateMatrix
GLuint rotate = glGetUniformLocation(self.myPrograme, "rotateMatrix");
//獲取渲旋轉的弧度
float radians = 180 * 3.14159f / 180.0f;
//求得弧度對于的sin\cos值
float s = sin(radians);
float c = cos(radians);
GLfloat zRotation[16] = {
c, -s, 0, 0,
s, c, 0, 0,
0, 0, 1.0, 0,
0.0, 0, 0, 1.0
};
//設置旋轉矩陣
glUniformMatrix4fv(rotate, 1, GL_FALSE, (GLfloat *)&zRotation[0]);
/*
glUniformMatrix4fv (GLint location, GLsizei count, GLboolean transpose, const GLfloat* value)
location : 對于shader 中的ID
count : 個數
transpose : 轉置
value : 指針
*/
2.解壓圖片時,將圖片源文件翻轉
//將 UIImage 轉換為 CGImageRef
CGImageRef spriteImage = [UIImage imageNamed:fileName].CGImage;
//讀取圖片的大小,寬和高
size_t width = CGImageGetWidth(spriteImage);
size_t height = CGImageGetHeight(spriteImage);
//獲取圖片字節(jié)數 寬*高*4(RGBA)
GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
//創(chuàng)建上下文
CGContextRef spriteContext = CGBitmapContextCreate(spriteData, width, height, 8, width*4,CGImageGetColorSpace(spriteImage), kCGImageAlphaPremultipliedLast);
//在CGContextRef上,將圖片繪制出來
CGRect rect = CGRectMake(0, 0, width, height);
CGContextDrawImage(spriteContext, CGRectMake(0, 0, width, height), spriteImage);
CGContextTranslateCTM(spriteContext, rect.origin.x, rect.origin.y);
CGContextTranslateCTM(spriteContext, 0, rect.size.height);
CGContextScaleCTM(spriteContext, 1.0, -1.0);
CGContextTranslateCTM(spriteContext, -rect.origin.x, -rect.origin.y);
CGContextDrawImage(spriteContext, rect, spriteImage);
//釋放上下文
CGContextRelease(spriteContext);
//綁定紋理到默認的紋理ID
glBindTexture(GL_TEXTURE_2D, 0);
CGContextDrawImage 使用的是Core Graphics框架涯肩,坐標系與UIKit 不一樣轿钠。UIKit框架的原點在屏幕的左上角,Core Graphics框架的原點在屏幕的左下角病苗。
CGContextDrawImage
參數1:繪圖上下文
參數2:rect坐標
參數3:繪制的圖片
3.修改片元著色器的紋理坐標
varying lowp vec2 varyTextCoord;
uniform sampler2D colorMap;
void main()
{
//gl_FragColor = texture2D(colorMap, varyTextCoord);
gl_FragColor = texture2D(colorMap, vec2(varyTextCoord.x,1.0-varyTextCoord.y));
//因為紋理坐標的范圍是0-1疗垛,所以翻轉的話都統(tǒng)一用1去減
}
4.修改頂點著色器紋理坐標
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
//varyTextCoord = textCoordinate;
varyTextCoord = vec2(textCoordinate.x,1.0-textCoordinate.y);
gl_Position = position;
//因為紋理坐標的范圍是0-1,所以翻轉的話都統(tǒng)一用1去減
}
同時也可以在頂點著色器中直接翻轉頂點坐標:
attribute vec4 position;
attribute vec2 textCoordinate;
varying lowp vec2 varyTextCoord;
void main()
{
varyTextCoord = textCoordinate;
//varyTextCoord = vec2(textCoordinate.x,1.0-textCoordinate.y);
gl_Position = vec4(position.x硫朦,-position.y贷腕,position.z,1.0f);
}
//在翻轉頂點時,就不是直接對Y值用1去減泽裳,因為頂點的取值范圍是-1 - 1 ,所以我們直接加上負號做翻轉即可
5.直接從源紋理坐標數據修改
//原始坐標
GLfloat attrArr[] =
{
0.5f, -0.5f, 0.0f, 1.0f, 1.0f, //右下
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, // 左上
-0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 左下
0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // 右上
-0.5f, 0.5f, 0.0f, 0.0f, 0.0f, // 左上
0.5f, -0.5f, 0.0f, 1.0f, 1.0f, // 右下
};
//更改后的坐標
GLfloat attrArr[] =
{
0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
-0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
-0.5f, -0.5f, -1.0f, 0.0f, 0.0f,
0.5f, 0.5f, -1.0f, 1.0f, 1.0f,
-0.5f, 0.5f, -1.0f, 0.0f, 1.0f,
0.5f, -0.5f, -1.0f, 1.0f, 0.0f,
};
翻轉前效果圖:文中部分內容參考:CC老師 http://www.reibang.com/p/848d982db9f2