LearnOpenGL學習筆記(二)紋理

開始學習OpenGL,參考的是著名的LearnOpenGL這個網站,在這裏作一些總結性的記錄,只是方便本身往後查找或者記錄本身的一些拓展思考,關於OpenGL的具體內容請移步:
https://learnopengl-cn.github.io/
或英文原版:https://learnopengl.com/c++

紋理座標

爲了可以把紋理映射(Map)到三角形上,咱們須要指定三角形的每一個頂點各自對應紋理的哪一個部分。這樣每一個頂點就會關聯着一個紋理座標(Texture Coordinate),用來標明該從紋理圖像的哪一個部分採樣(譯註:採集片斷顏色)。以後在圖形的其它片斷上進行片斷插值(Fragment Interpolation)。git

因此先在頂點數據中加入紋理座標並記得將其傳入頂點着色器:github

float vertices[] = {
        //     ---- 位置 ----       ---- 顏色 ----     - 紋理座標 -
             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    // 左上
    };

讀取圖片

用的stb_image.h這個頭文件:函數

//讀取圖片
int width, height, nrChannels;//圖片寬度,高度,顏色通道數量
stbi_set_flip_vertically_on_load(true);//翻轉y軸
unsigned char* data = stbi_load("wall.jpg", &width, &height, &nrChannels, 0);

生成紋理對象

相似於以前生成VAO、VBO的流程:學習

//生成紋理
    unsigned int texture1;
    glGenTextures(1, &texture1);//第一個參數爲生成紋理的數量
    glBindTexture(GL_TEXTURE_2D, texture1);//綁定紋理對象

設置紋理

設置紋理環繞方式和紋理過濾選項,決定了紋理座標超過1時如何採樣以及多級漸遠紋理級別之間的過濾方式,具體含義和效果見LearnOpenGL。網站

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的第3個和第7個參數,決定了紋理的源格式和咱們但願的處理格式,若是原圖帶有Alpha通道,這裏要改爲GL_RGBA,原文中沒有說明致使我一直加載不出帶透明通道的圖,後來看源碼才發現。code

glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);//生成紋理
glGenerateMipmap(GL_TEXTURE_2D);//生成多級漸遠紋理

結束以後記得釋放圖像的內存:orm

stbi_image_free(data);//釋放圖像內存

片元着色器採樣

紋理座標傳入頂點着色器後直接傳到片元着色器便可,至於紋理對象自己定義爲sampler2D類型的uniform變量,經過GLSL的texture函數便可進行採樣。對象

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D ourTexture;

void main()
{
    FragColor = texture(ourTexture, TexCoord);
}

結果:blog

紋理單元

能夠看到上面片元着色器中定義了uniform的紋理變量,但咱們並無在程序中給這個uniform變量傳值,這是由於OpenGL中有不少紋理單元,若是隻有一個紋理對象,會默認分配至GL_TEXTURE0,sampler變量也會默認從0號單元中採樣,因此若是須要渲染更多的紋理,須要分配不一樣的紋理單元。

更改一下片元着色器,用mix函數讓兩張圖片進行混合:

#version 330 core
out vec4 FragColor;

in vec3 ourColor;
in vec2 TexCoord;

uniform sampler2D texture1;
uniform sampler2D texture2;

void main()
{
     FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.3);
}

先按以前的方式讀入第二張圖(這裏讀了一張帶透明通道的圖因此記得glTexImage2D的參數改成GL_RGBA):

unsigned int texture2;
    glGenTextures(1, &texture2);
    glBindTexture(GL_TEXTURE_2D, texture2);
    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);
    data = stbi_load("face.png", &width, &height, &nrChannels, 0);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);
    glGenerateMipmap(GL_TEXTURE_2D);
    stbi_image_free(data);

而後再渲染循環以前給sampler指定對應的紋理單元(注意必定要先激活shader程序):

shader.use();
    glUniform1i(glGetUniformLocation(shader.ID, "texture1"), 0);
    glUniform1i(glGetUniformLocation(shader.ID, "texture2"), 1);

而後激活對應的紋理單元並將紋理對象綁定上去:

glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, texture1);
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, texture2);

結果:

關於像素對齊的一個小坑

在LearnOpenGL這一章的評論區發現有人說,當讀取的圖片寬度爲奇數時顯示不正常,而我試了一下寬度爲奇數的圖片程序直接就崩潰了,評論區也給出瞭解決方法,加入一行代碼以後就正常了:

glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

這行代碼的做用是設置OpenGL讀取數據的對齊方式,OpenGL默認爲4字節對齊,也就是說一行圖像數據的長度必須是4的倍數,我又嘗試了一下寬度爲偶數但不是4的倍數的圖片,果真顯示不正確:

因此上面的代碼就是設置讀取數據的方式爲1個字節對齊,這樣OpenGL會一個一個字節讀取,不會致使越界,但這樣讀取效率確定也會下降,因此最好的方法仍是提供寬度爲4的倍數的圖片。

相關文章
相關標籤/搜索