三明網(wǎng)站優(yōu)化/推廣優(yōu)化網(wǎng)站排名
在頂點著色器和片段著色器中間還有一個幾何著色器。
幾何著色器的輸入是一個圖元的一組頂點,在幾何著色器中進(jìn)行任意變換之后再給片段著色器,可以變成完全不一樣的圖元、可以生成更多的頂點。
#version 330 core
layout (points) in;
layout (line_strip, max_vertices = 2) out;void main() { gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); EmitVertex();gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);EmitVertex();EndPrimitive();
}
這是一個幾何著色器的例子。
?我們不僅用這一幀的著色器輸出,我們還會使用上一幀的著色器變量來進(jìn)行更多變化。
?使用幾何著色器
首先先寫一個最簡單的畫4個點的程序。
#include <iostream>#define GLEW_STATIC
#include <GL/glew.h>
#include <GLFW/glfw3.h>
float points[] = {-0.5f, 0.5f, // 左上0.5f, 0.5f, // 右上0.5f, -0.5f, // 右下-0.5f, -0.5f // 左下
};const char* vertexShaderSource =
"#version 330 core \n"
"layout(location = 0) in vec2 aPos; // 位置變量的屬性位置值為 0\n"
"void main(){ \n"
" gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); ; \n"
"} \n";const char* fragmentShaderSource =
"#version 330 core \n"
"out vec4 FragColor; \n"
"void main(){ \n"
" FragColor = vec4(0.0, 1.0, 0.0, 1.0);} \n";void processInput(GLFWwindow* window) {if (glfwGetKey(window, GLFW_KEY_ESCAPE) == GLFW_PRESS){glfwSetWindowShouldClose(window, true);}
}int main() {glfwInit();glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 3);glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);//Open GLFW WindowGLFWwindow* window = glfwCreateWindow(800, 600, "My OpenGL Game", NULL, NULL);if (window == NULL){printf("Open window failed.");glfwTerminate();return -1;}glfwMakeContextCurrent(window);//Init GLEWglewExperimental = true;if (glewInit() != GLEW_OK){printf("Init GLEW failed.");glfwTerminate();return -1;}glViewport(0, 0, 800, 600);unsigned int VAO;glGenVertexArrays(1, &VAO);glBindVertexArray(VAO);unsigned int VBO;glGenBuffers(1, &VBO);glBindBuffer(GL_ARRAY_BUFFER, VBO);glBufferData(GL_ARRAY_BUFFER, sizeof(points),points, GL_STATIC_DRAW);unsigned int vertexShader;vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderSource, NULL);glCompileShader(vertexShader);unsigned int fragmentShader;fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderSource, NULL);glCompileShader(fragmentShader);unsigned int shaderProgram;shaderProgram = glCreateProgram();glAttachShader(shaderProgram, vertexShader);glAttachShader(shaderProgram, fragmentShader);glLinkProgram(shaderProgram);// 位置屬性glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);glEnableVertexAttribArray(0);while (!glfwWindowShouldClose(window)){processInput(window);glClearColor(0.2f, 0.3f, 0.3f, 1.0f);glClear(GL_COLOR_BUFFER_BIT);glUseProgram(shaderProgram);glBindVertexArray(VAO);glDrawArrays(GL_POINTS, 0, 4);glfwSwapBuffers(window);glfwPollEvents();}glfwTerminate();return 0;}
會在一個黑暗的場景里面勉強(qiáng)看到4個綠點。
現(xiàn)在加入一個幾何著色器
#version 330 core
layout (points) in;
layout (points, max_vertices = 1) out;void main() { gl_Position = gl_in[0].gl_Position; EmitVertex();EndPrimitive();
}
他做的就是把頂點著色器過來的點數(shù)據(jù)傳給片段著色器。
當(dāng)然這個幾何著色器也是要創(chuàng)建和鏈接、編譯的。
最后結(jié)果還是和上面一樣的。
現(xiàn)在來點不一樣的,建個房子。
剛剛說了幾何著色器可以把一個點變多個點,輸出也可以是其他圖元。
那么一個房子我們一共需要3個三角形。
三角形帶的意思是,我生成一個三角形并不需要生成3個頂點再拋出,可以和之前的點一起拋出。
OpenGL中,三角形帶(Triangle Strip)是繪制三角形更高效的方式,它使用頂點更少。在第一個三角形繪制完之后,每個后續(xù)頂點將會在上一個三角形邊上生成另一個三角形:每3個臨近的頂點將會形成一個三角形。
#version 330 core
layout (points) in;
layout (triangle_strip, max_vertices = 5) out;void build_house(vec4 position)
{ gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:左下EmitVertex(); gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:右下EmitVertex();gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:左上EmitVertex();gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:右上EmitVertex();gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:頂部EmitVertex();EndPrimitive();
}void main() { build_house(gl_in[0].gl_Position);
}
?我們讀一下一個幾何著色器是如何實現(xiàn)的。
首先進(jìn)來的是一個點,先生成第一個點1號,發(fā)射。然后生成右下2號再生成左上3號,就可以生成一個三角形,然后4號會和2、3號一起又變成一個三角形。
?我們把顏色也加進(jìn)去。
更新頂點數(shù)據(jù)
float points[] = {-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // 左上0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // 右上0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // 右下-0.5f, -0.5f, 1.0f, 1.0f, 0.0f // 左下
};#version 330 core
layout (location = 0) in vec2 aPos;
layout (location = 1) in vec3 aColor;out VS_OUT {vec3 color;
} vs_out;void main()
{gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); vs_out.color = aColor;
}
可以看到這里用了接口塊,然后也需要在幾何著色器中聲明同一種接口快來接受,但是要使用不同的接口名
in VS_OUT {vec3 color;
} gs_in[];
這里是數(shù)組的原因是頂點著色器發(fā)來的頂點數(shù)組。同時也需要給片段著色器out一個對應(yīng)的顏色向量。
fColor = gs_in[0].color;
gl_Position = position + vec4(-0.2, -0.2, 0.0, 0.0); // 1:左下
EmitVertex();
gl_Position = position + vec4( 0.2, -0.2, 0.0, 0.0); // 2:右下
EmitVertex();
gl_Position = position + vec4(-0.2, 0.2, 0.0, 0.0); // 3:左上
EmitVertex();
gl_Position = position + vec4( 0.2, 0.2, 0.0, 0.0); // 4:右上
EmitVertex();
gl_Position = position + vec4( 0.0, 0.4, 0.0, 0.0); // 5:頂部
fColor = vec3(1.0, 1.0, 1.0);
EmitVertex();
EndPrimitive();
這里要看清楚,首先我定義了一個fcolor,后面的每一個頂點在發(fā)射的時候都會附帶上fcolor的值,我什么時候修改這個顏色再發(fā)射,那個點的顏色就變了。
?有了幾何著色器,你甚至可以將最簡單的圖元變得十分有創(chuàng)意。因為這些形狀是在GPU的超快硬件中動態(tài)生成的,這會比在頂點緩沖中手動定義圖形要高效很多。因此,幾何緩沖對簡單而且經(jīng)常重復(fù)的形狀來說是一個很好的優(yōu)化工具,比如體素(Voxel)世界中的方塊和室外草地的每一根草。
爆破物體
爆破物體的效果并不是要一個三角形變成多個三角形,只是當(dāng)三角形沿著法向量移動一段距離。這再幾何著色器里面做最好不過了。
首先是法向量怎么得到,注意,我們在頂點著色器中的法向量是點的法向,不是三角形的法向,所以需要通過三個頂點來算法向量。這個簡單,通過叉乘就可以得到了。
vec3 GetNormal()
{vec3 a = vec3(gl_in[0].gl_Position) - vec3(gl_in[1].gl_Position);vec3 b = vec3(gl_in[2].gl_Position) - vec3(gl_in[1].gl_Position);return normalize(cross(a, b));
}
然后就是當(dāng)他沿著法向移動了。
vec4 explode(vec4 position, vec3 normal)
{float magnitude = 2.0;vec3 direction = normal * ((sin(time) + 1.0) / 2.0) * magnitude; return position + vec4(direction, 0.0);
}
一點問題
這里的頂點著色器和教程里面的不一樣。
此外,位移我們只要xy不要z,這是因為z會影響深度測試從而導(dǎo)致出現(xiàn)錯誤。
教程里面的gl_position是在頂點著色器里面算好了,就也是乘玩了mvp。然后再再gemo里面做位移。其實因為把這個計算放在gemo中,也就是放在爆破之后。而且fragpos也應(yīng)該是gemo給的,而不是vertex給的。
正確做法是:先爆破,然后把位移之后的坐標(biāo)mvp,fragpos就用爆破之后的位置乘m就行。
法向量可視化
思路是這樣的:我們首先不使用幾何著色器正常繪制場景。然后再次繪制場景,但這次只顯示通過幾何著色器生成法向量。幾何著色器接收一個三角形圖元,并沿著法向量生成三條線——每個頂點一個法向量。
這次在幾何著色器中,我們會使用模型提供的頂點法線,而不是自己生成,為了適配(觀察和模型矩陣的)縮放和旋轉(zhuǎn),我們在將法線變換到觀察空間坐標(biāo)之前,先使用法線矩陣變換一次(幾何著色器接受的位置向量是觀察空間坐標(biāo),所以我們應(yīng)該將法向量變換到相同的空間中)。
這里先不做了。
?毛發(fā)等也是通過幾何著色器加上去的。