本文主要解決2個問題:
1、不同格式的圖片,如何作為紋理使用(png、bmp)?
2、兩張紋理如何進行融合操作,并且通過融合因子去調整顯示效果?
一、不同格式的圖片,如何作為紋理使用(png、bmp)?
1.1 png格式
不管三七二十一,直接把圖片替換掉,看看效果。
嗯?這詭異的顯示是啥?
第一反應,某些參數設置出錯,尤其是glTexImage2D
函數。直接瞄準參數GL_RGB,png圖片有度的信息,所以格式應該設置為GL_RGBA才對。想明白了,馬上修改試試。
不出所料!
1.2 bmp格式
還是那樣,先直接改文件看效果。
不對啊,bmp格式不是有透明通道嗎?怎么回事?來檢查一下圖片的屬性。
哦,原來位深度只有24,也就是只有RGB三個通道了。把格式換回GL_RGB應該沒問題了。
對的,我們分析地沒錯。那么問題來了,難道我們需要一張張查看圖片的屬性來設置這個格式嗎?這也太復雜了,或者根據圖片的后綴確定用什么參數。要是相同后綴的圖片有不同格式咋辦?
沒有開玩笑哦,這樣的圖片還是很常見的。
仔細看看代碼,原來我們在加載圖片的時候已經讀取出通道數量了,保存在nrChannels變量中。這樣就方便多了!我們完全可以根據通道數量來設置格式。簡單點,直接用一行代碼搞定:
GLint format = nrChannels == 3 ? GL_RGB : GL_RGBA;
然后在調用的時候就可以用format變量了。
glTexImage2D(GL_TEXTURE_2D, 0, format, width, height, 0, format, GL_UNSIGNED_BYTE, data);
效果圖:
工程源碼:http://pan.baidu.com/s/1mh6oQMW
二、兩張紋理如何進行融合操作,并且通過融合因子去調整顯示效果?
2.1 紋理單元
細心的你可能早就發現了,我們之前的使用紋理采樣的時候,并沒有對片元著色器中的采樣器進行賦值,但顯示的效果確實正確的。為什么呢?
這里就要引出一個紋理單元的概念。什么是紋理單元,說實話,筆者也沒有找到確切的定義,只知道紋理是通過紋理單元這個東西綁定到OpenGL環境中的。根據筆者的理解,紋理單元應該是OpenGL中內置的關于紋理的一些配置,OpenGL會根據這些配置來操作紋理。在OpenGL中,紋理單元的數量至少有16個,我們可以通過GL_TEXTURE0...GL_TEXTURE15來激活使用。默認激活的是GL_TEXTURE0,所以我們之前的操作都是針對GL_TEXTURE0的。
讓我們來激活另一個紋理單元并且對它進行一些操作。
首先,我們用glActiveTexture(GL_TEXTURE1)
來激活紋理單元1,使之可操作。
然后,將一個新的紋理ID綁定到這個紋理對象上,我們不妨將這個新ID定義成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_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
再接著,我們把圖片加載進去,綁定到當前的紋理單元上:
//加載圖片
stbi_set_flip_vertically_on_load(true);
unsigned char* data2 = stbi_load("awesomeface.png", &width, &height, &nrChannels, 0);
if (data2) {
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data2);
glGenerateMipmap(GL_TEXTURE_2D);
}
else
std::cout << "無法加載問題,請檢查代碼或資源是否有誤。" << std::endl;
stbi_image_free(data2);
最后,非常重要的一個步驟是,我們需要告訴OpenGL著色器采樣器和紋理單元之間的對應關系。
//告訴OpenGL哪個采樣器屬于哪個紋理單元
shader.use(); //著色器要使用之后才能操作
shader.setInt("texture1", 0); //采樣器1對應編號0的紋理單元
shader.setInt("texture2", 1); //采樣器2對應編號1的紋理單元
我們的片元著色器中,也需要做相應的改動:
uniform sampler2D texture1;
uniform sampler2D texture2;
void main()
{
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), 0.2);
}
mix函數是對某個點的紋理進行混合運算,0.2表示該點的顏色20%來自采樣器2,80%來自采樣器1。好了,編譯運行我們的程序。
效果很贊,但是還沒達到我們想要的狀態。別忘了,我們還要通過融合因子對其進行控制!
2.2 融合因子控制
直覺上的,我們需要三步走:
- 第一步:定義一個全局的融合因子
- 第二步:點擊上下箭頭的時候設置這個值
- 第三步:設置這個融合因子變量值
第一步:定義一個全局的融合因子
float factor = 0.2f;
第二步:點擊上下箭頭的時候設置這個值
查找上箭頭和下箭頭的按鈕值:GLFW_KEY_UP和GLFW_KEY_DOWN,在處理輸入的函數(processInput)中,添加對其的處理。每次按向上箭頭時融合因子增加0.01(最大為1.0),每次按向下箭頭時融合因子減少0.01,(最小為0.0)。
if (glfwGetKey(window, GLFW_KEY_UP) == GLFW_PRESS) {
factor += 0.01f;
if (factor > 1.0f)
factor = 1.0f;
}
if (glfwGetKey(window, GLFW_KEY_DOWN) == GLFW_PRESS) {
factor -= 0.01f;
if (factor < 0.0f)
factor = 0.0f;
}
第三步:設置這個融合因子變量值
在使用了著色器之后,設置其值。
shader.setFloat("factor", factor);
好了,非常簡單,但是一定要自己實現才行。編譯運行,這下我們可以通過按鈕來控制了?。]法放視頻,就不截圖了。)
整個工程的源碼可以在這里找到:http://pan.baidu.com/s/1eRWj7kY
參考資料:
www.learningopengl.com(非常好的網站,建議學習)