OpenGL ES on iOS --- 2D紋理

OpenGL ES on iOS --- 2D紋理

簡介

紋理是用來豐富我們繪制物體細節的,它可以是一張2D圖片(除了圖像外,紋理也被用來存儲大量數據,傳遞到著色器上),就像貼圖一樣貼在繪制的物體上.

紋理屬性

紋理坐標

為了將紋理映射到繪制的物體上,我們需要指定 某個頂點對應著 紋理的那個位置. 通過紋理坐標,標明頂點從紋理圖像那一部分采樣,之后在圖形的其它片段進行片段插值.

紋理坐標系和頂點坐標系有所不同,頂點坐標系 (0,0)點位于窗口中心. 紋理坐標系 (0,0)點位于 紋理左下角.

頂點坐標系
紋理坐標系

紋理環繞方式

當我們將頂點位置設置到紋理坐標之外時,則需要設置紋理環繞方式 來顯示紋理圖案

環繞屬性 效果
GL_REPEAT 重復紋理圖案(默認)
GL_MIRRORED_REPEAT 鏡像重復紋理圖案
GL_CLAMP_TO_EDGE 將紋理鎖定在0~1之間,超出部分重復紋理邊緣圖案,產生拉伸效果
GL_CLAMP_TO_BORDER 超出部分為用戶指定邊緣顏色
環繞示意圖

紋理環繞函數

glTexParameteri (GLenum target, GLenum pname, GLint param);
參數:

target: 指定紋理目標,若為2D紋理 則為 GL_TEXTURE_2D
pname: 對應的紋理坐標軸(這里 s,t,r 對應 x,y,z) GL_TEXTURE_WRAP_S ,GL_TEXTURE_WRAP_T
param: 環繞方式,填入上面的方式.

對2D紋理時,必須對 s,t坐標軸都進行設置. 若是設置 GL_CLAMP_TO_BORDER 形式,則還需要額外設置 環繞顏色

float borderColor[] = { 1.0f, 1.0f, 0.0f, 1.0f };
glTexParameterfv(GL_TEXTURE_2D, GL_TEXTURE_BORDER_COLOR, borderColor);

紋理過濾

紋理坐標是不依賴于 紋理大小和分辨率的, 1就表示紋理的邊緣.但是物體和和紋理大小可能不一致,這就造成了對紋理的放大和拉伸.這時如何將紋理像素映射到紋理坐標上,就需要我們設置. 該屬性就是 紋理過濾.

紋理過濾有很多種,下面是最重要的兩種

鄰近過濾

GL_NEAREST 是默認的紋理過濾方式,它會選擇距離 紋理坐標最近的像素點作為樣本顏色.當紋理被放大時,會有顆粒感


臨近過濾

線性過濾

GL_LINEAR, 會基于當前紋理坐標附近的像素點計算一個插值,也就是附近紋理的混合色,離得越近的像素點,顏色貢獻越大,當紋理被放大時,會比臨近過濾更平滑.


線性過濾
臨近過濾和線性過濾比較

過濾函數

我們需要對放大(Magnify)和縮小(Minify)的情況設置過濾效果

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

多級漸遠紋理

在3D世界,根據物體的遠近不同,物體也存在縮放的效果,要顯示不同的分辨率,若都使用相同的分辨率,一是會使物體產生不真實的效果,二是會造成內存的浪費. 在研究3D紋理時再說.~~

紋理代碼

首先是設置 上下文,著色器,程序對象...的方法.

context1 = [[EAGLContext alloc] initWithAPI:(kEAGLRenderingAPIOpenGLES3)];
    BOOL isSetCOntextRight = [EAGLContext setCurrentContext:context1];
    if (!isSetCOntextRight) {
        printf("設置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);

    //創建,綁定渲染緩存 并分配空間
    glGenRenderbuffers(1, &renderBuf1);
    glBindRenderbuffer(GL_RENDERBUFFER, renderBuf1);
    // 為 color renderbuffer 分配存儲空間
    [context1 renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];

    //創建,綁定幀緩存 并分配空間
    glGenFramebuffers(1, &frameBuf1);
    // 設置為當前 framebuffer
    glBindFramebuffer(GL_FRAMEBUFFER, frameBuf1);
    // 將 _colorRenderBuffer 裝配到 GL_COLOR_ATTACHMENT0 這個裝配點上
    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[] = {
        // 位置              顏色                 紋理坐標
        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);

紋理設置~~

stbi_image 一個非常流行的單頭文件圖像加載庫 stbi_image

    //因為使用 stbi 函數導入的圖片會顛倒,所以需要將其擺正
    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);

    //創建 紋理
    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);

    //設置紋理環繞和紋理過濾
    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);

    //將紋理作為統一變量傳入顯存
    glUniform1i(glGetUniformLocation(program1, "outTexture"), 0);


    //這里PNG格式 通過stbi_load 拉入時 會導致產生 BGRA格式(我也不大清楚原因,懂的朋友 告告我 先O(∩_∩)O謝謝了) ,這時 圖片作為紋理顯示時色彩會出錯.所以將其轉換一蛤~
    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);       //必須先寫這個再綁定
    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內建的mix函數需要接受兩個值作為參數,并對它們根據第三個參數進行線性插值。如果第三個值是0.0,它會返回第一個輸入;如果是1.0,會返回第二個輸入值。0.2會返回80%的第一個輸入顏色和20%的第二個輸入顏色,即返回兩個紋理的混合色。

函數補充說明

glTexImage2D (GLenum target, GLint level, GLint internalformat, GLsizei width, GLsizei height, GLint border, GLenum format, GLenum type, const GLvoid* pixels);

target 紋理目標 設置為GL_TEXTURE_2D意味著會生成與當前綁定的紋理對象在同一個目標上的紋理
level 為紋理指定多級漸遠紋理的級別,0表示基本級別
internalformat 希望將紋理存儲為何等格式
width height 圖像寬高
border 設置為0 說是歷史遺留問題
format 源圖格式 type 數據格式
pixels 圖像數據

紋理單元

在代碼中 使用 glUniform1i方法進行紋理傳遞 是因為 紋理單元 這個概念.
使用glUniform1i可以為紋理采樣器分配一個位置值,通過把紋理單元賦值給采樣器,就可以一次綁定多個紋理.

OpenGL至少保證有16個紋理單元供你使用,也就是說你可以激活從GL_TEXTURE0到GL_TEXTRUE15。它們都是按順序定義的,所以我們也可以通過GL_TEXTURE0 + 8的方式獲得GL_TEXTURE8,這在當我們需要循環一些紋理單元的時候會很有用。

需要注意的是 在綁定紋理時 先要激活紋理單元才可以

結果
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,748評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,165評論 3 414
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,595評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,633評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,435評論 6 405
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 54,943評論 1 321
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,035評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,175評論 0 287
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,713評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,599評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,788評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,303評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,034評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,412評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,664評論 1 280
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,408評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,747評論 2 370