OpenGL ES 入門之旅 -- GLSL紋理單元和紋理翻轉解決策略

從上一篇文章中咱們瞭解到片元着色器是如何編寫的:bash

片元着色器框架

片元着色器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(紋理採樣器,紋理座標),獲取對應座標紋素(讀取紋素,讀取每個像素點的顏色值)。ui

咱們知道sampler(採樣器)是GLSL提供的可供紋理對象使用的內建數據,並且sampler一般實在片元着色器中內定義,被uniform修飾符修飾,表示這個變量是不會被修改的。 經過上面的代碼能夠看到聲明sampler的類型還有一個sampler2D。這個只是表明一個二維的紋理類型。sampler1D,sampler2D,sampler3D 表示不一樣維度的紋理類型.spa

在上面的代碼中咱們簡單聲明瞭一個紋理對象. uniform sampler2D,將一個紋理添加片元着色器中.指針

uniform sampler2D colorMap;
複製代碼

同時咱們使用GLSL內建的texture函數來採樣紋理的顏色值.code

gl_FragColor = texture2D(colorMap, varyTextCoord);
複製代碼

紋理單元orm

這裏聲明的sampler2D變量是個uniform,咱們卻沒有用glUniform給它賦值,通常來說咱們須要用glUniform1i()函數進行將紋理對象(數據)從CPU中傳入顯存中的着色器。之因此使用glUniform1i()函數,是由於只須要給紋理採樣器傳入一個索引值(位置)便可,這樣咱們就可以在一個片元着色器中設置多個紋理。cdn

那麼這個索引值就是咱們接下來要介紹的‘紋理單元’: 一個紋理的位置值一般稱爲一個紋理單元(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,這在當咱們須要循環一些紋理單元的時候會頗有用。

紋理混合

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設置uniform採樣器的位置值,或者說紋理單元。經過glUniform1i的設置,咱們保證每一個uniform採樣器對應着正確的紋理單元

其實,使用glUniform1i()函數做爲着色器內部和程序來進行傳入值,須要知道兩個參數,一個是在着色器內部接受信息的對象爲位置。一個是外界的數據對象,嚴格來說傳入數據自己也不是這個函數作的,這個函數只是告訴着色器那個紋理對象對應哪一個採樣器對象。

流程.png
重點介紹一下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)在左下角。

紋理座標.png
而圖片中像素的存儲順序是從左上到右下的,所以咱們須要對咱們的座標系進行一次Y軸的「翻轉」,保持原點座標一致。

  1. 利用旋轉矩陣翻轉圖形,不翻轉紋理 即讓圖形的頂點座標旋轉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);
//獲取圖片字節數 寬*高*4(RGBA)
GLubyte * spriteData = (GLubyte *) calloc(width * height * 4, sizeof(GLubyte));
//建立上下文
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,因此翻轉的話都統一用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,因此翻轉的話都統一用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,
    };

複製代碼

翻轉前效果圖:

翻轉前.png

翻轉後效果圖:

翻轉後.png

文中部份內容參考:CC老師 www.jianshu.com/p/848d982db…

相關文章
相關標籤/搜索