開始學習OpenGL,參考的是著名的LearnOpenGL這個網站,在這里做一些總結性的記錄,只是方便自己日后查找或者記錄自己的一些拓展思考,關于OpenGL的具體內容請移步:
https://learnopengl-cn.github.io/
或英文原版:https://learnopengl.com/
紋理坐標
為了能夠把紋理映射(Map)到三角形上,我們需要指定三角形的每個頂點各自對應紋理的哪個部分,這樣每個頂點就會關聯著一個紋理坐標(Texture Coordinate),用來標明該從紋理影像的哪個部分采樣(譯注:采集片段顏色),之后在圖形的其它片段上進行片段插值(Fragment Interpolation),

所以先在頂點資料中加入紋理坐標并記得將其傳入頂點著色器:
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 = https://www.cnblogs.com/LiveForGame/p/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,原文中沒有說明導致我一直加載不出帶透明通道的圖,后來看原始碼才發現,
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, width, height, 0, GL_RGB, GL_UNSIGNED_BYTE, data);//生成紋理
glGenerateMipmap(GL_TEXTURE_2D);//生成多級漸遠紋理
結束之后記得釋放影像的記憶體:
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);
}
結果:
紋理單元
可以看到上面片元著色器中定義了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 = https://www.cnblogs.com/LiveForGame/p/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的倍數的圖片,
轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/2106.html
標籤:其他
