版本記錄
版本號 | 時間 |
---|---|
V1.0 | 2018.01.18 |
前言
OpenGL 圖形庫項目中一直也沒用過楼誓,最近也想學著使用這個圖形庫凡桥,感覺還是很有意思技羔,也就自然想著好好的總結一下,希望對大家能有所幫助泌类。下面內(nèi)容來自歡迎來到OpenGL的世界癞谒。
1. OpenGL 圖形庫使用(一) —— 概念基礎
2. OpenGL 圖形庫使用(二) —— 渲染模式、對象刃榨、擴展和狀態(tài)機
3. OpenGL 圖形庫使用(三) —— 著色器弹砚、數(shù)據(jù)類型與輸入輸出
4. OpenGL 圖形庫使用(四) —— Uniform及更多屬性
5. OpenGL 圖形庫使用(五) —— 紋理
6. OpenGL 圖形庫使用(六) —— 變換
7. OpenGL 圖形庫的使用(七)—— 坐標系統(tǒng)之五種不同的坐標系統(tǒng)(一)
8. OpenGL 圖形庫的使用(八)—— 坐標系統(tǒng)之3D效果(二)
9. OpenGL 圖形庫的使用(九)—— 攝像機(一)
10. OpenGL 圖形庫的使用(十)—— 攝像機(二)
11. OpenGL 圖形庫的使用(十一)—— 光照之顏色
12. OpenGL 圖形庫的使用(十二)—— 光照之基礎光照
13. OpenGL 圖形庫的使用(十三)—— 光照之材質
14. OpenGL 圖形庫的使用(十四)—— 光照之光照貼圖
15. OpenGL 圖形庫的使用(十五)—— 光照之投光物
16. OpenGL 圖形庫的使用(十六)—— 光照之多光源
17. OpenGL 圖形庫的使用(十七)—— 光照之復習總結
18. OpenGL 圖形庫的使用(十八)—— 模型加載之Assimp
19. OpenGL 圖形庫的使用(十九)—— 模型加載之網(wǎng)格
20. OpenGL 圖形庫的使用(二十)—— 模型加載之模型
21. OpenGL 圖形庫的使用(二十一)—— 高級OpenGL之深度測試
22. OpenGL 圖形庫的使用(二十二)—— 高級OpenGL之模板測試Stencil testing
23. OpenGL 圖形庫的使用(二十三)—— 高級OpenGL之混合Blending
24. OpenGL 圖形庫的使用(二十四)—— 高級OpenGL之面剔除Face culling
25. OpenGL 圖形庫的使用(二十五)—— 高級OpenGL之幀緩沖Framebuffers
26. OpenGL 圖形庫的使用(二十六)—— 高級OpenGL之立方體貼圖Cubemaps
27. OpenGL 圖形庫的使用(二十七)—— 高級OpenGL之高級數(shù)據(jù)Advanced Data
28. OpenGL 圖形庫的使用(二十八)—— 高級OpenGL之高級GLSL Advanced GLSL
29. OpenGL 圖形庫的使用(二十九)—— 高級OpenGL之幾何著色器Geometry Shader
30. OpenGL 圖形庫的使用(三十)—— 高級OpenGL之實例化Instancing
抗鋸齒
在你的渲染大冒險中,你可能會遇到模型邊緣有鋸齒的問題枢希。鋸齒邊緣(Jagged Edge)
出現(xiàn)的原因是由頂點數(shù)據(jù)光柵化成為片段(fragment)的方式所引起的桌吃。舉個例子,我們隨手繪制一個簡單的正方體就已經(jīng)能很清楚地看到鋸齒邊緣的效果:
盡管可能不會被立刻察覺到苞轿,如果你更近的看看立方體的邊茅诱,你就會發(fā)現(xiàn)鋸齒狀的圖案。如果我們放大就會看到下面的情境:
這當然不是我們在實際應用中想要的效果搬卒。這個效果让簿,我們很明顯能看到邊緣像素的形態(tài),這種現(xiàn)象被稱之為走樣(Aliasing)
秀睛。有很多技術能夠減少走樣現(xiàn)象,產(chǎn)生更平滑的邊緣莲祸,這些技術叫做抗鋸齒技術(Anti-aliasing蹂安,也被稱為反走樣技術)
。
最開始我們有一個叫做超采樣抗鋸齒技術(Super Sample Anti-aliasing, SSAA)
锐帜,它暫時使用一個更高的分辨率(以超采樣方式)來渲染場景田盈,當輸出圖像在幀緩沖中被更新時,圖像的分辨率會被下采樣(down sample)回原來的分辨率缴阎。它使用額外的分辨率來防止鋸齒邊緣允瞧。雖然它確實為我們提供了一種解決走樣問題的方案,但卻由于必須繪制比平時更多的片段而降低了性能。所以這個技術只流行了一段時間述暂。
這個技術的基礎上誕生了更為現(xiàn)代的技術痹升,叫做多采樣抗鋸齒(Multisample Anti-aliasing)
或叫MSAA
,雖然它借用了SSAA的理念畦韭,但卻以更加高效的方式實現(xiàn)了它疼蛾。這節(jié)教程我們會展開討論這個OpenGL內(nèi)建的MSAA技術。
多重采樣
為了理解什么是多重采樣(Multisampling)
艺配,以及它是如何解決鋸齒問題的察郁,我們先要更深入了解一下OpenGL光柵化的工作方式。
光柵化表示在我們輸出的頂點(Vertices)
和片段著色器(fragment shader)
中間的所有算法和處理過程的集合(譯者注: 在OpenGL中转唉,光柵化步驟在幾何著色器后皮钠,片段著色器前)。光柵化將屬于一個圖元的所有頂點轉化為一系列片段赠法。頂點坐標理論上可以是任意值麦轰,但片段卻不是這樣,這是因為它們受你的窗口的分辨率限制期虾。幾乎永遠都不會有頂點坐標和片段的一對一映射原朝,所以光柵化必須以某種方式?jīng)Q定每個頂點最終位于哪個片段/屏幕坐標上。
這里我們看到一個屏幕像素網(wǎng)格镶苞,每個像素中心包含一個采樣點(sample point)
喳坠,它被用來決定一個像素是否被三角形所覆蓋。紅色的采樣點表示該點被三角形所覆蓋茂蚓,片段著色器會給該屏幕像素著色壕鹉。不過即使三角形覆蓋了某一個屏幕像素的一部分,但是中心的采樣點沒被覆蓋到聋涨,這個像素仍然不會受到片段著色器的影響晾浴。
你可能現(xiàn)在已經(jīng)明白走樣的原因是什么了。上述三角形渲染后在你的屏幕上會是這樣的:
由于屏幕像素總量的限制牍白,有些邊上的像素能被渲染出來脊凰,而有些則不會。結果就是我們渲染出的圖元的邊緣產(chǎn)生了上圖的鋸齒茂腥。
多采樣所做的正是不再使用單一采樣點來決定三角形的覆蓋范圍狸涌,而是采用多個采樣點(這大概就是它名字的由來)。我們不再使用每個像素中心的采樣點最岗,取而代之的是4個子采樣(subsample)
帕胆,用它們來決定像素是否被覆蓋。這意味著顏色緩沖的大小也由于每個像素的子樣本的增加而增加了般渡。
上圖的左側顯示了我們普通決定一個三角形是否覆蓋屏幕像素的方式(譯者注: 中心點懒豹,單采樣)芙盘。這個像素并不會被一個片段著色器著色(因此它保持空白),因為它的采樣點沒有被三角形所覆蓋脸秽。右邊示了多采樣的版本儒老,每個像素包含4個采樣點。這里我們可以看到只有2個采樣點被三角形覆蓋豹储。
采樣點的數(shù)量是任意的贷盲,更多的采樣點能帶來更精確的覆蓋率。
從這兒開始我們的多重采樣變得有趣起來了剥扣。我們知道了只有2個子采樣被三角覆蓋巩剖,下一步就是決定這個像素的顏色。我們可以猜測一下钠怯,我們會為每個被覆蓋的子采樣運行片段著色器佳魔,然后對每個像素的所有子采樣的顏色進行平均化。在這種情況下晦炊,我們需要為每一個被插值后的頂點數(shù)據(jù)的每一個子采樣運行兩次兩次片段著色器鞠鲜,然后把采樣點的顏色儲存起來。幸好断国,它不是這么運作的贤姆,因為這等于說我們必須運行更多的片段著色器,會明顯降低性能稳衬。
MSAA的真正工作方式是霞捡,每個像素只運行一次片段著色器,無論該像素有多少子樣本被三角形所覆蓋薄疚。片段著色器接受的頂點數(shù)據(jù)是被插值到每一個像素的中心坐標碧信,而其著色的顏色會被每個被三角形覆蓋的子采樣儲存。一旦一個我們繪制的圖元的子采樣顏色緩沖被填滿了街夭,每個像素對應的所有顏色將會被平均化砰碴,這使得每個像素最終有了一個唯一顏色。比如在前面的圖片中4個子采樣中只有2個被三角形覆蓋板丽,像素的顏色事實上是一個均值呈枉,該均值由三角形的顏色和其他2個子采樣的顏色(aka. 背景色)平均化而成,最后該像素被著色為一種淺藍色埃碱。
結果是碴卧,顏色緩沖中所有圖元的邊變得更加平滑了。讓我們看看多重采樣對前面的一個三角形來說是怎樣做的:
這里每個像素包含著4個子采樣(不相關采樣點的沒有被標注出來)藍色的子采樣是被三角形覆蓋了的乃正,灰色的則沒有被覆蓋。三角形內(nèi)部區(qū)域中的所有像素都會運行一次片段著色器婶博,它輸出的顏色由4個子采樣決定瓮具。三角形的邊緣并不是所有的子采樣都會被覆蓋,所以片段著色器的結果僅由部分的子采樣決定。根據(jù)被覆蓋子采樣的數(shù)量名党,最終的像素顏色由三角形顏色和其他子采樣所儲存的顏色所決定叹阔。(譯者注: 其實有點類似于Blending的原理。)
大致上來說传睹,如果更多的采樣點被覆蓋耳幢,那么像素的顏色就會更接近于三角形。如果我們用這種方去給我們前面的三角形的填充像素顏色欧啤,我們會獲得這樣的結果:
對于每個像素來說睛藻,被三角形覆蓋的子采樣越少,像素受到三角形的顏色的影響也越少⌒纤恚現(xiàn)在原本三角形不平滑的邊被比實際顏色淺一些的顏色像素所包圍店印,因此觀察者從遠處看上去就比較平滑了。
不僅顏色值會被多重采樣技術影響倒慧,深度測試和模板測試也同樣使用了多重采樣技術按摘。比如深度測試,頂點的深度值在運行深度測試前被插值到每個子采樣中纫谅,對于模板測試炫贤,我們?yōu)槊總€子采樣儲存模板值,而不是每個像素付秕。這意味著深度和模板緩沖的大小隨著像素子樣本的增加也增加了兰珍。
到目前為止我們所討論的不過是多重采樣技術的工作原理。光柵化背后實際的邏輯要比我們討論的復雜盹牧,但你現(xiàn)在可以理解MSAA背后的概念和邏輯了俩垃。 (譯者注: 如果看到這里還是對原理似懂非懂,可以簡單看看知乎上@文刀秋二 對抗鋸齒技術的精彩介紹)
OpenGL中的MSAA
如果我們打算在OpenGL中使用MSAA汰寓,那么我們必須使用一個可以為每個像素儲存一個以上的顏色值的顏色緩沖(因為多采樣需要我們?yōu)槊總€采樣點儲存一個顏色)口柳。我們這就需要一個新的緩沖類型,它可以儲存要求數(shù)量的多重采樣樣本有滑,它叫做多樣本緩沖(Multisample Buffer)
跃闹。
多數(shù)窗口系統(tǒng)可以為我們提供一個多樣本緩沖,以代替默認的顏色緩沖毛好。GLFW同樣給了我們這個功能望艺,我們所要作的就是提示GLFW,我們希望使用一個帶有N個樣本的多樣本緩沖肌访,而不是普通的顏色緩沖找默,這要在創(chuàng)建窗口前調用glfwWindowHint
來完成:
glfwWindowHint(GLFW_SAMPLES, 4);
當我們現(xiàn)在調用glfwCreateWindow
,用于渲染的窗口就被創(chuàng)建了吼驶,這次每個屏幕坐標使用一個包含4個子樣本的顏色緩沖惩激。這意味著所有緩沖的大小都增長4倍店煞。
現(xiàn)在我們請求GLFW提供了多樣本緩沖,我們還要調用glEnable來開啟多采樣风钻,參數(shù)是 GL_MULTISAMPLE
顷蟀。大多數(shù)OpenGL驅動,多采樣默認是開啟的骡技,所以這個調用有點多余鸣个,但通常記得開啟它是個好主意。這樣所有OpenGL實現(xiàn)的多采樣都開啟了布朦。
glEnable(GL_MULTISAMPLE);
當默認幀緩沖有了多采樣緩沖附件的時候囤萤,我們所要做的全部就是調用 glEnable開啟多采樣。因為實際的多采樣算法在OpenGL驅動光柵化里已經(jīng)實現(xiàn)了喝滞,所以我們無需再做什么了阁将。如果我們現(xiàn)在來渲染教程開頭的那個綠色立方體,我們會看到邊緣變得平滑了:
這個箱子看起來平滑多了右遭,在場景中繪制任何物體都可以利用這個技術做盅。可以從這里找到這個簡單的例子窘哈。
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GL includes
#include "Shader.h"
#include "Camera.h"
// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Properties
GLuint screenWidth = 800, screenHeight = 600;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void Do_Movement();
// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
// The MAIN function, from here we start our application and run our Game loop
int main()
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
glfwWindowHint(GLFW_SAMPLES, 4);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// Options
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
// Setup OpenGL options
glEnable(GL_MULTISAMPLE); // Enabled by default on some drivers, but not all so always enable to make sure
glEnable(GL_DEPTH_TEST);
// Setup and compile our shaders
Shader shader("shaders/advanced.vs", "shaders/advanced.frag");
#pragma region "object_initialization"
// Set the object data (buffers, vertex attributes)
GLfloat cubeVertices[] = {
// Positions
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f
};
// Setup cube VAO
GLuint cubeVAO, cubeVBO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glBindVertexArray(0);
#pragma endregion
// Game loop
while(!glfwWindowShouldClose(window))
{
// Set frame time
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// Check and call events
glfwPollEvents();
Do_Movement();
// Clear buffers
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set transformation matrices
shader.Use();
glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)screenWidth/(GLfloat)screenHeight, 0.1f, 1000.0f);
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(glm::mat4()));
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// Swap the buffers
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
#pragma region "User input"
// Moves/alters the camera positions based on user input
void Do_Movement()
{
// Camera controls
if(keys[GLFW_KEY_W])
camera.ProcessKeyboard(FORWARD, deltaTime);
if(keys[GLFW_KEY_S])
camera.ProcessKeyboard(BACKWARD, deltaTime);
if(keys[GLFW_KEY_A])
camera.ProcessKeyboard(LEFT, deltaTime);
if(keys[GLFW_KEY_D])
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if(action == GLFW_PRESS)
keys[key] = true;
else if(action == GLFW_RELEASE)
keys[key] = false;
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if(firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
#pragma endregion
離屏MSAA
因為GLFW
負責創(chuàng)建多采樣緩沖吹榴,開啟MSAA非常簡單。如果我們打算使用我們自己的幀緩沖滚婉,來進行離屏渲染图筹,那么我們就必須自己生成多采樣緩沖了;現(xiàn)在我們需要自己負責創(chuàng)建多采樣緩沖让腹。
有兩種方式可以創(chuàng)建多采樣緩沖远剩,并使其成為幀緩沖的附件:紋理附件和渲染緩沖附件,和幀緩沖教程里討論過的普通的附件很相似骇窍。
1. 多采樣紋理附件
為了創(chuàng)建一個支持儲存多采樣點的紋理瓜晤,我們使用glTexImage2DMultisample
來替代 glTexImage2D
,它的紋理目標是GL_TEXTURE_2D_MULTISAMPLE
:
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, tex);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, width, height, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
第二個參數(shù)現(xiàn)在設置了我們打算讓紋理擁有的樣本數(shù)腹纳。如果最后一個參數(shù)等于 GL_TRUE
痢掠,圖像上的每一個紋理像素(texel)將會使用相同的樣本位置,以及同樣的子樣本數(shù)量嘲恍。
為將多采樣紋理附加到幀緩沖上足画,我們使用glFramebufferTexture2D
,不過這次紋理類型是GL_TEXTURE_2D_MULTISAMPLE
:
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, tex, 0);
當前綁定的幀緩沖現(xiàn)在有了一個紋理圖像形式的多采樣顏色緩沖佃牛。
2. 多采樣渲染緩沖對象
和紋理一樣淹辞,創(chuàng)建一個多采樣渲染緩沖對象(Multisampled Renderbuffer Objects)
不難。而且還很簡單俘侠,因為我們所要做的全部就是當我們指定渲染緩沖的內(nèi)存的時候將glRenderbuffeStorage
改為glRenderbufferStorageMuiltisample
:
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, width, height);
有一樣東西在這里有變化象缀,就是緩沖目標后面那個額外的參數(shù)彬向,我們將其設置為樣本數(shù)量,當前的例子中應該是4攻冷。
3. 渲染到多采樣幀緩沖
渲染到多采樣幀緩沖對象是自動的。當我們繪制任何東西時遍希,幀緩沖對象就綁定了等曼,光柵化會對負責所有多采樣操作。我們接著得到了一個多采樣顏色緩沖凿蒜,以及深度和模板緩沖禁谦。因為多采樣緩沖有點特別,我們不能為其他操作直接使用它們的緩沖圖像废封,比如在著色器中進行采樣州泊。
一個多采樣圖像包含了比普通圖像更多的信息,所以我們需要做的是壓縮或還原圖像漂洋。還原一個多采樣幀緩沖遥皂,通常用glBlitFramebuffer
來完成,它從一個幀緩沖中復制一個區(qū)域粘貼另一個里面刽漂,同時也將任何多采樣緩沖還原演训。
glBlitFramebuffer
把一個4屏幕坐標源區(qū)域傳遞到一個也是4空間坐標的目標區(qū)域。你可能還記得幀緩沖教程中贝咙,如果我們綁定到GL_FRAMEBUFFER
样悟,我們實際上就同時綁定到了讀和寫的幀緩沖目標。我們還可以通過GL_READ_FRAMEBUFFER
和GL_DRAW_FRAMEBUFFER
綁定到各自的目標上庭猩。glBlitFramebuffer函數(shù)從這兩個目標讀取窟她,并決定哪一個是源哪一個是目標幀緩沖。接著我們就可以通過把圖像位塊傳送(Blitting)到默認幀緩沖里蔼水,將多采樣幀緩沖輸出傳遞到實際的屏幕了:
glBindFramebuffer(GL_READ_FRAMEBUFFER, multisampledFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
如果我們渲染應用震糖,我們將得到和沒用幀緩沖一樣的結果:一個綠色立方體,它使用MSAA顯示出來徙缴,但邊緣鋸齒明顯少了:
你可以在這里找到源代碼试伙。
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GL includes
#include "Shader.h"
#include "Camera.h"
// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Properties
GLuint screenWidth = 800, screenHeight = 600;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void Do_Movement();
GLuint generateMultiSampleTexture(GLuint samples);
// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
// The MAIN function, from here we start our application and run our Game loop
int main()
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// Options
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
// Setup OpenGL options
glEnable(GL_MULTISAMPLE); // Enabled by default on some drivers, but not all so always enable to make sure
glEnable(GL_DEPTH_TEST);
// Setup and compile our shaders
Shader shader("shaders/advanced.vs", "shaders/advanced.frag");
#pragma region "object_initialization"
// Set the object data (buffers, vertex attributes)
GLfloat cubeVertices[] = {
// Positions
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f
};
// Setup cube VAO
GLuint cubeVAO, cubeVBO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glBindVertexArray(0);
#pragma endregion
// Framebuffers
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// Create a multisampled color attachment texture
GLuint textureColorBufferMultiSampled = generateMultiSampleTexture(4);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
// Create a renderbuffer object for depth and stencil attachments
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, screenWidth, screenHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Game loop
while(!glfwWindowShouldClose(window))
{
// Set frame time
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// Check and call events
glfwPollEvents();
Do_Movement();
// 1. Draw scene as normal in multisampled buffers
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Set transformation matrices
shader.Use();
glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)screenWidth/(GLfloat)screenHeight, 0.1f, 1000.0f);
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(glm::mat4()));
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// 2. Now blit multisampled buffer(s) to default framebuffers
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Swap the buffers
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
GLuint generateMultiSampleTexture(GLuint samples)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, screenWidth, screenHeight, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
return texture;
}
#pragma region "User input"
// Moves/alters the camera positions based on user input
void Do_Movement()
{
// Camera controls
if(keys[GLFW_KEY_W])
camera.ProcessKeyboard(FORWARD, deltaTime);
if(keys[GLFW_KEY_S])
camera.ProcessKeyboard(BACKWARD, deltaTime);
if(keys[GLFW_KEY_A])
camera.ProcessKeyboard(LEFT, deltaTime);
if(keys[GLFW_KEY_D])
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if(action == GLFW_PRESS)
keys[key] = true;
else if(action == GLFW_RELEASE)
keys[key] = false;
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if(firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
#pragma endregion
但是如果我們打算使用一個多采樣幀緩沖的紋理結果來做這件事,就像后處理一樣會怎樣于样?我們不能在片段著色器中直接使用多采樣紋理疏叨。我們可以做的事情是把多緩沖位塊傳送(Blit)到另一個帶有非多采樣紋理附件的FBO中。之后我們使用這個普通的顏色附件紋理進行后處理穿剖,通過多采樣來對一個圖像渲染進行后處理效率很高蚤蔓。這意味著我們必須生成一個新的FBO,它僅作為一個將多采樣緩沖還原為一個我們可以在片段著色器中使用的普通2D紋理中介糊余。偽代碼是這樣的:
GLuint msFBO = CreateFBOWithMultiSampledAttachments();
// Then create another FBO with a normal texture color attachment
...
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0);
...
while(!glfwWindowShouldClose(window))
{
...
glBindFramebuffer(msFBO);
ClearFrameBuffer();
DrawScene();
// Now resolve multisampled buffer(s) into intermediate FBO
glBindFramebuffer(GL_READ_FRAMEBUFFER, msFBO);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO);
glBlitFramebuffer(0, 0, width, height, 0, 0, width, height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// Now scene is stored as 2D texture image, so use that image for post-processing
glBindFramebuffer(GL_FRAMEBUFFER, 0);
ClearFramebuffer();
glBindTexture(GL_TEXTURE_2D, screenTexture);
DrawPostProcessingQuad();
...
}
如果我們實現(xiàn)幀緩沖教程中講的后處理代碼秀又,我們就能創(chuàng)造出沒有鋸齒邊的所有效果很酷的后處理特效单寂。使用模糊kernel過濾器夺脾,看起來會像這樣:
你可以在這里找到所有MSAA版本的后處理源碼迷捧。
// GLEW
#define GLEW_STATIC
#include <GL/glew.h>
// GLFW
#include <GLFW/glfw3.h>
// GL includes
#include "Shader.h"
#include "Camera.h"
// GLM Mathemtics
#include <glm/glm.hpp>
#include <glm/gtc/matrix_transform.hpp>
#include <glm/gtc/type_ptr.hpp>
// Properties
GLuint screenWidth = 800, screenHeight = 600;
// Function prototypes
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode);
void mouse_callback(GLFWwindow* window, double xpos, double ypos);
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset);
void Do_Movement();
GLuint generateMultiSampleTexture(GLuint samples);
GLuint generateAttachmentTexture(GLboolean depth, GLboolean stencil);
// Camera
Camera camera(glm::vec3(0.0f, 0.0f, 3.0f));
bool keys[1024];
GLfloat lastX = 400, lastY = 300;
bool firstMouse = true;
GLfloat deltaTime = 0.0f;
GLfloat lastFrame = 0.0f;
// The MAIN function, from here we start our application and run our Game loop
int main()
{
// Init GLFW
glfwInit();
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint(GLFW_RESIZABLE, GL_FALSE);
GLFWwindow* window = glfwCreateWindow(screenWidth, screenHeight, "LearnOpenGL", nullptr, nullptr); // Windowed
glfwMakeContextCurrent(window);
// Set the required callback functions
glfwSetKeyCallback(window, key_callback);
glfwSetCursorPosCallback(window, mouse_callback);
glfwSetScrollCallback(window, scroll_callback);
// Options
glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED);
// Initialize GLEW to setup the OpenGL Function pointers
glewExperimental = GL_TRUE;
glewInit();
// Define the viewport dimensions
glViewport(0, 0, screenWidth, screenHeight);
// Setup OpenGL options
glEnable(GL_MULTISAMPLE); // Enabled by default on some drivers, but not all so always enable to make sure
glEnable(GL_DEPTH_TEST);
// Setup and compile our shaders
Shader shader("shaders/advanced.vs", "shaders/advanced.frag");
Shader screenShader("shaders/post_processing.vs", "shaders/post_processing.frag");
#pragma region "object_initialization"
// Set the object data (buffers, vertex attributes)
GLfloat cubeVertices[] = {
// Positions
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
0.5f, -0.5f, -0.5f,
0.5f, -0.5f, 0.5f,
0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, 0.5f,
-0.5f, -0.5f, -0.5f,
-0.5f, 0.5f, -0.5f,
0.5f, 0.5f, -0.5f,
0.5f, 0.5f, 0.5f,
0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, 0.5f,
-0.5f, 0.5f, -0.5f
};
GLfloat quadVertices[] = { // Vertex attributes for a quad that fills the entire screen in Normalized Device Coordinates.
// Positions // TexCoords
-1.0f, 1.0f, 0.0f, 1.0f,
-1.0f, -1.0f, 0.0f, 0.0f,
1.0f, -1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f, 1.0f,
1.0f, -1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 1.0f, 1.0f
};
// Setup cube VAO
GLuint cubeVAO, cubeVBO;
glGenVertexArrays(1, &cubeVAO);
glGenBuffers(1, &cubeVBO);
glBindVertexArray(cubeVAO);
glBindBuffer(GL_ARRAY_BUFFER, cubeVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(cubeVertices), &cubeVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 3 * sizeof(GLfloat), (GLvoid*)0);
glBindVertexArray(0);
// Setup screen VAO
GLuint quadVAO, quadVBO;
glGenVertexArrays(1, &quadVAO);
glGenBuffers(1, &quadVBO);
glBindVertexArray(quadVAO);
glBindBuffer(GL_ARRAY_BUFFER, quadVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(quadVertices), &quadVertices, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)0);
glEnableVertexAttribArray(1);
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 4 * sizeof(GLfloat), (GLvoid*)(2 * sizeof(GLfloat)));
glBindVertexArray(0);
#pragma endregion
// Framebuffers
GLuint framebuffer;
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
// Create a multisampled color attachment texture
GLuint textureColorBufferMultiSampled = generateMultiSampleTexture(4);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D_MULTISAMPLE, textureColorBufferMultiSampled, 0);
// Create a renderbuffer object for depth and stencil attachments
GLuint rbo;
glGenRenderbuffers(1, &rbo);
glBindRenderbuffer(GL_RENDERBUFFER, rbo);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, 4, GL_DEPTH24_STENCIL8, screenWidth, screenHeight);
glBindRenderbuffer(GL_RENDERBUFFER, 0);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, rbo);
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// second framebuffer
GLuint intermediateFBO;
GLuint screenTexture = generateAttachmentTexture(false, false);
glGenFramebuffers(1, &intermediateFBO);
glBindFramebuffer(GL_FRAMEBUFFER, intermediateFBO);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, screenTexture, 0); // We only need a color buffer
if(glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
cout << "ERROR::FRAMEBUFFER:: Intermediate framebuffer is not complete!" << endl;
glBindFramebuffer(GL_FRAMEBUFFER, 0);
// Game loop
while(!glfwWindowShouldClose(window))
{
// Set frame time
GLfloat currentFrame = glfwGetTime();
deltaTime = currentFrame - lastFrame;
lastFrame = currentFrame;
// Check and call events
glfwPollEvents();
Do_Movement();
// 1. Draw scene as normal in multisampled buffers
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
glClearColor(0.1f, 0.1f, 0.1f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
glEnable(GL_DEPTH_TEST);
// Set transformation matrices
shader.Use();
glm::mat4 projection = glm::perspective(camera.Zoom, (GLfloat)screenWidth/(GLfloat)screenHeight, 0.1f, 1000.0f);
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "projection"), 1, GL_FALSE, glm::value_ptr(projection));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "view"), 1, GL_FALSE, glm::value_ptr(camera.GetViewMatrix()));
glUniformMatrix4fv(glGetUniformLocation(shader.Program, "model"), 1, GL_FALSE, glm::value_ptr(glm::mat4()));
glBindVertexArray(cubeVAO);
glDrawArrays(GL_TRIANGLES, 0, 36);
glBindVertexArray(0);
// 2. Now blit multisampled buffer(s) to normal colorbuffer of intermediate FBO. Image is stored in screenTexture
glBindFramebuffer(GL_READ_FRAMEBUFFER, framebuffer);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, intermediateFBO);
glBlitFramebuffer(0, 0, screenWidth, screenHeight, 0, 0, screenWidth, screenHeight, GL_COLOR_BUFFER_BIT, GL_NEAREST);
// 3. Now render quad with scene's visuals as its texture image
glBindFramebuffer(GL_FRAMEBUFFER, 0);
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDisable(GL_DEPTH_TEST);
// Draw Screen quad
screenShader.Use();
glBindVertexArray(quadVAO);
glBindTexture(GL_TEXTURE_2D, screenTexture); // Use the now resolved color attachment as the quad's texture
glDrawArrays(GL_TRIANGLES, 0, 6);
glBindVertexArray(0);
// Swap the buffers
glfwSwapBuffers(window);
}
glfwTerminate();
return 0;
}
GLuint generateMultiSampleTexture(GLuint samples)
{
GLuint texture;
glGenTextures(1, &texture);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, texture);
glTexImage2DMultisample(GL_TEXTURE_2D_MULTISAMPLE, samples, GL_RGB, screenWidth, screenHeight, GL_TRUE);
glBindTexture(GL_TEXTURE_2D_MULTISAMPLE, 0);
return texture;
}
// Generates a texture that is suited for attachments to a framebuffer
GLuint generateAttachmentTexture(GLboolean depth, GLboolean stencil)
{
// What enum to use?
GLenum attachment_type;
if(!depth && !stencil)
attachment_type = GL_RGB;
else if(depth && !stencil)
attachment_type = GL_DEPTH_COMPONENT;
else if(!depth && stencil)
attachment_type = GL_STENCIL_INDEX;
//Generate texture ID and load texture data
GLuint textureID;
glGenTextures(1, &textureID);
glBindTexture(GL_TEXTURE_2D, textureID);
if(!depth && !stencil)
glTexImage2D(GL_TEXTURE_2D, 0, attachment_type, screenWidth, screenHeight, 0, attachment_type, GL_UNSIGNED_BYTE, NULL);
else // Using both a stencil and depth test, needs special format arguments
glTexImage2D(GL_TEXTURE_2D, 0, GL_DEPTH24_STENCIL8, screenWidth, screenHeight, 0, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, NULL);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR );
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glBindTexture(GL_TEXTURE_2D, 0);
return textureID;
}
#pragma region "User input"
// Moves/alters the camera positions based on user input
void Do_Movement()
{
// Camera controls
if(keys[GLFW_KEY_W])
camera.ProcessKeyboard(FORWARD, deltaTime);
if(keys[GLFW_KEY_S])
camera.ProcessKeyboard(BACKWARD, deltaTime);
if(keys[GLFW_KEY_A])
camera.ProcessKeyboard(LEFT, deltaTime);
if(keys[GLFW_KEY_D])
camera.ProcessKeyboard(RIGHT, deltaTime);
}
// Is called whenever a key is pressed/released via GLFW
void key_callback(GLFWwindow* window, int key, int scancode, int action, int mode)
{
if(key == GLFW_KEY_ESCAPE && action == GLFW_PRESS)
glfwSetWindowShouldClose(window, GL_TRUE);
if(action == GLFW_PRESS)
keys[key] = true;
else if(action == GLFW_RELEASE)
keys[key] = false;
}
void mouse_callback(GLFWwindow* window, double xpos, double ypos)
{
if(firstMouse)
{
lastX = xpos;
lastY = ypos;
firstMouse = false;
}
GLfloat xoffset = xpos - lastX;
GLfloat yoffset = lastY - ypos;
lastX = xpos;
lastY = ypos;
camera.ProcessMouseMovement(xoffset, yoffset);
}
void scroll_callback(GLFWwindow* window, double xoffset, double yoffset)
{
camera.ProcessMouseScroll(yoffset);
}
#pragma endregion
因為屏幕紋理重新變回了只有一個采樣點的普通紋理顶猜,有些后處理過濾器或油,比如邊檢測(edge-detection)將會再次導致鋸齒邊問題睬魂。為了修正此問題嗦哆,之后你應該對紋理進行模糊處理科盛,或者創(chuàng)建你自己的抗鋸齒算法妇多。
當我們希望將多采樣和離屏渲染結合起來時贤惯,我們需要自己負責一些細節(jié)洼专。所有細節(jié)都是值得付出這些額外努力的,因為多采樣可以明顯提升場景視頻輸出的質量孵构。要注意屁商,開啟多采樣會明顯降低性能,樣本越多越明顯颈墅。本文寫作時蜡镶,MSAA4樣本很常用。
自定義抗鋸齒算法
可以直接把一個多采樣紋理圖像傳遞到著色器中精盅,以取代必須先還原的方式帽哑。GLSL給我們一個選項來為每個子樣本進行紋理圖像采樣,所以我們可以創(chuàng)建自己的抗鋸齒算法叹俏,在比較大的圖形應用中妻枕,通常這么做。
為獲取每個子樣本的顏色值粘驰,你必須將紋理uniform采樣器定義為sampler2DMS
屡谐,而不是使用sampler2D:
uniform sampler2DMS screenTextureMS;
使用texelFetch
函數(shù),就可以獲取每個樣本的顏色值了:
vec4 colorSample = texelFetch(screenTextureMS, TexCoords, 3); // 4th subsample
我們不會深究自定義抗鋸齒技術的創(chuàng)建細節(jié)蝌数,但是會給你自己去實現(xiàn)它提供一些提示愕掏。
后記
本篇已結束,下一篇是高級光照顶伞。