在顶点着色器和片段着色器中间还有一个几何着色器。
几何着色器的输入是一个图元的一组顶点,在几何着色器中进行任意变换之后再给片段着色器,可以变成完全不一样的图元、可以生成更多的顶点。
#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();
}
这是一个几何着色器的例子。
我们不仅用这一帧的着色器输出,我们还会使用上一帧的着色器变量来进行更多变化。
使用几何着色器
首先先写一个最简单的画4个点的程序。
#include
#define GLEW_STATIC
#include
#include
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; // 位置变量的属性位置值为 0n"
"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 Window
GLFWwindow* window = glfwCreateWindow(800, 600, "My OpenGL Game", NULL, NULL);
if (window == NULL)
{
printf("Open window failed.");
glfwTerminate();
return -1;
}
glfwMakeContextCurrent(window);
//Init GLEW
glewExperimental = 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;
}
会在一个黑暗的场景里面勉强看到4个绿点。
现在加入一个几何着色器
#version 330 core
layout (points) in;
layout (points, max_vertices = 1) out;
void main() {
gl_Position = gl_in[0].gl_Position;
EmitVertex();
EndPrimitive();
}
他做的就是把顶点着色器过来的点数据传给片段着色器。
当然这个几何着色器也是要创建和链接、编译的。
最后结果还是和上面一样的。
现在来点不一样的,建个房子。
刚刚说了几何着色器可以把一个点变多个点,输出也可以是其他图元。
那么一个房子我们一共需要3个三角形。
三角形带的意思是,我生成一个三角形并不需要生成3个顶点再抛出,可以和之前的点一起抛出。
OpenGL中,三角形带(Triangle Strip)是绘制三角形更高效的方式,它使用顶点更少。在第一个三角形绘制完之后,每个后续顶点将会在上一个三角形边上生成另一个三角形:每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);
}
我们读一下一个几何着色器是如何实现的。
首先进来的是一个点,先生成第一个点1号,发射。然后生成右下2号再生成左上3号,就可以生成一个三角形,然后4号会和2、3号一起又变成一个三角形。
我们把颜色也加进去。
更新顶点数据
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[];
这里是数组的原因是顶点着色器发来的顶点数组。同时也需要给片段着色器out一个对应的颜色向量。
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,后面的每一个顶点在发射的时候都会附带上fcolor的值,我什么时候修改这个颜色再发射,那个点的颜色就变了。
有了几何着色器,你甚至可以将最简单的图元变得十分有创意。因为这些形状是在GPU的超快硬件中动态生成的,这会比在顶点缓冲中手动定义图形要高效很多。因此,几何缓冲对简单而且经常重复的形状来说是一个很好的优化工具,比如体素(Voxel)世界中的方块和室外草地的每一根草。
爆破物体
爆破物体的效果并不是要一个三角形变成多个三角形,只是当三角形沿着法向量移动一段距离。这再几何着色器里面做最好不过了。
首先是法向量怎么得到,注意,我们在顶点着色器中的法向量是点的法向,不是三角形的法向,所以需要通过三个顶点来算法向量。这个简单,通过叉乘就可以得到了。
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));
}
然后就是当他沿着法向移动了。
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会影响深度测试从而导致出现错误。
教程里面的gl_position是在顶点着色器里面算好了,就也是乘玩了mvp。然后再再gemo里面做位移。其实因为把这个计算放在gemo中,也就是放在爆破之后。而且fragpos也应该是gemo给的,而不是vertex给的。
正确做法是:先爆破,然后把位移之后的坐标mvp,fragpos就用爆破之后的位置乘m就行。
法向量可视化
思路是这样的:我们首先不使用几何着色器正常绘制场景。然后再次绘制场景,但这次只显示通过几何着色器生成法向量。几何着色器接收一个三角形图元,并沿着法向量生成三条线——每个顶点一个法向量。
这次在几何着色器中,我们会使用模型提供的顶点法线,而不是自己生成,为了适配(观察和模型矩阵的)缩放和旋转,我们在将法线变换到观察空间坐标之前,先使用法线矩阵变换一次(几何着色器接受的位置向量是观察空间坐标,所以我们应该将法向量变换到相同的空间中)。
这里先不做了。
毛发等也是通过几何着色器加上去的。
服务器托管,北京服务器托管,服务器租用 http://www.fwqtg.net
相关推荐: 基于python flask茶叶网站数据大屏设计与实现,可以做期末课程设计或者毕业设计
基于Python的茶叶网站数据大屏设计与实现是一个适合期末课程设计或毕业设计的项目。该项目旨在利用Python技术和数据可视化方法,设计和开发一个针对茶叶行业的数据大屏,用于展示和分析茶叶网站的相关数据。 项目背景 随着互联网的快速发展,越来越多的茶叶企业开始…