【OpenGL】笔记二十一、Alpha测试、混合测试
1. 流程
OpenGL中,混合(Blending)通常是實現(xiàn)物體透明度(Transparency)的一種技術(shù)。透明就是說一個物體(或者其中的一部分)不是純色(Solid Color)的,它的顏色是物體本身的顏色和它背后其它物體的顏色的不同強(qiáng)度結(jié)合。這在我們之前的教程中其實有過類似的功能,就是在片段著色器中對兩個紋理的顏色進(jìn)行mix,使得最終呈現(xiàn)出來的顏色是兩個紋理不同程度的混合:
而這次我們可以用一種更通用的方法,那就是alpha測試
1.1 Alpha測試
在我們讀取圖片的時候,有的圖片數(shù)據(jù)除了RGB通道之外,還包含了Alpha通道,我們生成該紋理的時候可以設(shè)置為GL_RGBA來讀取:
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, data);有了這個Alpha參數(shù),我們可以怎么用呢?
同樣看到之前混合紋理時用的片段著色器:
FragColor = mix(texture(texture1, TexCoord), texture(texture2, TexCoord), visibility);我們使用了一個mix函數(shù),使得兩個紋理根據(jù)一個0-1之間的值visibility來分別設(shè)定它們的可見度,而這個visibility其實就可以看作我們讀取的Alpha值,為0時原圖片完全透明(著色時不使用它的顏色),為1時原圖片完全不透明(著色時完全使用它的顏色)
現(xiàn)在我們想要實現(xiàn)一種效果,我們要在游戲場景中渲染一塊草地,只用一個貼圖,如下圖:
直接將它渲染在場景中可能會是這樣:
這是因為空白部分的純色紋理也被渲染出來了,現(xiàn)在我們只要這個圖片中草的片段被渲染出來怎么辦?
這時候就要用到之前讀取的圖片alpha值了,在讀取的時候,空白部分的alpha值被設(shè)定為0,我們可以在片段著色器里這樣設(shè)置:
#version 330 core out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture1;void main() { vec4 texColor = texture(texture1, TexCoords);if(texColor.a < 0.1)discard;FragColor = texColor; }沒錯,GLSL提供了一個discard功能,在我們讀取紋理值,發(fā)現(xiàn)它的alpha值小于0.1時,我們就調(diào)用discard丟棄這個片段而不著色,這樣就能夠只渲染草片段的紋理了:
這只是一種簡單的應(yīng)用,實際應(yīng)用時,我們還更可能碰到一種情況,就是在渲染半透明物體時可能需要將物體本身的紋理與背景的顏色混合,而使用mix函數(shù)肯定無法完成這種較為復(fù)雜的操作,這時候就不得不提到OpenGL的另一個功能了
1.2 混合測試
在OpenGL中,也提供了一種類似于mix的方法,那就是混合(Blend)
我們在開始渲染時,需要開啟這個功能:
glEnable(GL_BLEND);然后,我們需要使用一個函數(shù)來指定混合效果:
glBlendFunc(GLenum sfactor, GLenum dfactor);它的參數(shù)有這些類型:
對于一般的mix效果(使用源顏色向量的alpha作為源因子,使用1?alpha作為目標(biāo)因子),我們可以這樣設(shè)置:
我們還可以通過glBlendEquation(GLenum mode)來設(shè)置運算符達(dá)到其他效果(默認(rèn)是兩個顏色相加):
1.3 半透明
接下來,我們就來渲染一個有色玻璃:
首先,在初始化時我們啟用混合,并設(shè)定相應(yīng)的混合函數(shù):
glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);由于啟用了混合,我們就不需要丟棄片段了,所以我們把片段著色器還原:
#version 330 core out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture1;void main() { FragColor = texture(texture1, TexCoords); }效果:
發(fā)現(xiàn)有一個問題,前面的玻璃怎么把后面的玻璃遮擋住了?
這是因為開啟了深度測試的原因,如果我們從近到遠(yuǎn)渲染玻璃的話,后面玻璃被遮擋的部分就不會渲染了,所以這個是比較麻煩的事,一般情況下,我們每次渲染半透明物體的時候,都是把它們按照與相機(jī)的距離從后到前渲染,我們可以利用map存儲玻璃,鍵值設(shè)為距離,這樣它就會自動按照距離存儲了:
std::map<float, glm::vec3> sorted; for (unsigned int i = 0; i < windows.size(); i++) {float distance = glm::length(camera.Position - windows[i]);sorted[distance] = windows[i]; }這里使用了map的一個反向迭代器(Reverse Iterator),反向遍歷其中的條目,實現(xiàn)從遠(yuǎn)到近
for(std::map<float,glm::vec3>::reverse_iterator it = sorted.rbegin(); it != sorted.rend(); ++it) {model = glm::mat4();model = glm::translate(model, it->second); shader.setMat4("model", model);glDrawArrays(GL_TRIANGLES, 0, 6); }1.4 深度測試、模板測試、Alpha測試和混合測試
那么我們最后結(jié)合深度測試、模板測試、Alpha測試和混合測試來一遍
首先開啟深度和Alpha測試
glEnable(GL_DEPTH_TEST);glEnable(GL_BLEND);glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);然后渲染箱子和燈兩類不透明物體:
先渲染燈
while (!glfwWindowShouldClose(window)){// 輸入processInput(window);//調(diào)用glClear函數(shù),清除顏色緩沖之后,整個顏色緩沖都會被填充為glClearColor里所設(shè)置的顏色glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);// 渲染指令float timeValue = glfwGetTime();deltaTime = timeValue - lastFrame;lastFrame = timeValue;view = projection = glm::mat4(1.0f);projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);view = camera.GetViewMatrix();light.view = view;light.projection = projection;light.Draw(lightShader);接著開啟模板測試渲染箱子,讓模板緩存先寫入:
glEnable(GL_STENCIL_TEST);glStencilOp(GL_KEEP, GL_REPLACE, GL_REPLACE);glStencilFunc(GL_ALWAYS, 1, 0xFF);glStencilMask(0xFF);box.view = view;box.projection = projection;box.Draw(ourShader, camera, timeValue);之后我們關(guān)閉模板測試,開始從遠(yuǎn)到近繪制半透明的玻璃:
glDisable(GL_STENCIL_TEST);glass.view = view;glass.projection = projection;glass.Draw(glassShader, camera);最后我們開啟模板測試,繪制箱子邊框:
glEnable(GL_STENCIL_TEST);glStencilFunc(GL_NOTEQUAL, 1, 0xFF);//glStencilMask(0x00);glDisable(GL_DEPTH_TEST);box.Draw(singleShader, camera, timeValue, 1.05f);glEnable(GL_DEPTH_TEST);glDisable(GL_STENCIL_TEST);glfwSwapBuffers(window);glfwPollEvents();}之所以先渲染不透明再渲染半透明,是因為半透明的物體顏色需要混合背景的不透明物體
而之所以要在第一遍渲染箱子之后關(guān)閉模板測試,是為了不讓渲染玻璃時影響模板緩存
效果(正面):
效果(反面):
1.5 測試順序
alpha的操作會在multisample fragment operations里面
也就是測試順序是:裁剪 Alpha 模板 深度 混合
總結(jié)
以上是生活随笔為你收集整理的【OpenGL】笔记二十一、Alpha测试、混合测试的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: EasyBoot教程一:制作WIN7原版
- 下一篇: 网页截图软件