当前位置: 首页 > news >正文

【OpenGL】LearnOpenGL学习笔记19 - 几何着色器 Geometry Shader

上接:https://blog.csdn.net/weixin_44506615/article/details/151043459?spm=1001.2014.3001.5501
完整代码:https://gitee.com/Duo1J/learn-open-gl | https://github.com/Duo1J/LearnOpenGL

几何着色器 (Geometry Shader)

接下来认识一种新的着色器 几何着色器,它在顶点和片段着色器之间执行,其输入是一个图元 (点或三角形),它可以对其进行随意的变换

#version 330 corelayout (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();// 结束line_strip图元EndPrimitive();
}

第二行 layout (points) in; 表明了绘制图元的类型

修饰符图元
pointsGL_POINTS
linesGL_LINES或GL_LINE_STRIP
lines_adjacencyGL_LINES_ADJACENCY 或 GL_LINE_STRIP_ADJACENCY
trianglesGL_TRIANGLES、GL_TRIANGLE_STRIP 或 GL_TRIANGLE_FAN
triangles_adjacencyGL_TRIANGLES_ADJACENCY 或 GL_TRIANGLE_STRIP_ADJACENCY

第三行 layout (line_strip, max_vertices = 2) out; 表示输出的图元类型

这里的 gl_in 是一个GLSL的内建变量

in gl_Vertex
{vec4  gl_Position;float gl_PointSize;float gl_ClipDistance[];
} gl_in[];

接下来尝试几个简单的几何着色器应用
在这之前我们先修改一下Shader类以加载几何着色器
Shader.h

// 增加可选的geoPath
Shader(const char* vertexPath, const char* fragmentPath, const char* geoPath = nullptr);

Shader.cpp
增加几何着色器

Shader::Shader(const char* vertexPath, const char* fragmentPath, const char* geoPath)
{std::string vertexCode;std::string fragmentCode;std::string geoCode;try{if (vertexPath != nullptr){std::ifstream vertexShaderFile;vertexShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);vertexShaderFile.open(vertexPath);std::stringstream vShaderStream;vShaderStream << vertexShaderFile.rdbuf();vertexShaderFile.close();vertexCode = vShaderStream.str();}if (fragmentPath != nullptr){std::ifstream fragmentShaderFile;fragmentShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);fragmentShaderFile.open(fragmentPath);std::stringstream fShaderStream;fShaderStream << fragmentShaderFile.rdbuf();fragmentShaderFile.close();fragmentCode = fShaderStream.str();}if (geoPath != nullptr){std::ifstream geoShaderFile;geoShaderFile.exceptions(std::ifstream::failbit | std::ifstream::badbit);geoShaderFile.open(geoPath);std::stringstream gShaderStream;gShaderStream << geoShaderFile.rdbuf();geoShaderFile.close();geoCode = gShaderStream.str();}}catch (std::ifstream::failure e){std::cout << "[Error] Failed to read shader file" << std::endl;}ProgramID = glCreateProgram();int success;char infoLog[512];// 顶点着色器unsigned int vertexShader = 0;if (!vertexCode.empty()){const char* vertexShaderCode = vertexCode.c_str();vertexShader = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertexShader, 1, &vertexShaderCode, NULL);glCompileShader(vertexShader);glGetShaderiv(vertexShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertexShader, 512, NULL, infoLog);std::cout << "[Error] Vertex shader compile failed!\n" << infoLog << std::endl;};glAttachShader(ProgramID, vertexShader);}// 片段着色器unsigned int fragmentShader = 0;if (!fragmentCode.empty()){const char* fragmentShaderCode = fragmentCode.c_str();fragmentShader = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragmentShader, 1, &fragmentShaderCode, NULL);glCompileShader(fragmentShader);glGetShaderiv(fragmentShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragmentShader, 512, NULL, infoLog);std::cout << "[Error] Fragment shader compile failed!\n" << infoLog << std::endl;}glAttachShader(ProgramID, fragmentShader);}// 几何着色器unsigned int geoShader = 0;if (!geoCode.empty()){const char* geoShaderCode = geoCode.c_str();geoShader = glCreateShader(GL_GEOMETRY_SHADER);glShaderSource(geoShader, 1, &geoShaderCode, NULL);glCompileShader(geoShader);glGetShaderiv(geoShader, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(geoShader, 512, NULL, infoLog);std::cout << "[Error] Geo shader compile failed!\n" << infoLog << std::endl;}glAttachShader(ProgramID, geoShader);}glLinkProgram(ProgramID);glGetProgramiv(ProgramID, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(ProgramID, 512, NULL, infoLog);std::cout << "[Error] Shader program link failed!\n" << infoLog << std::endl;}glDeleteShader(vertexShader);glDeleteShader(fragmentShader);glDeleteShader(geoShader);
}

1、绘制小房子

首先我们需要一个点

// 几何着色器小房子顶点
float housePoints[] = {-0.5f,  0.5f,
};unsigned int houseVAO, houseVBO;
glGenVertexArrays(1, &houseVAO);
glGenBuffers(1, &houseVBO);
glBindVertexArray(houseVAO);
glBindBuffer(GL_ARRAY_BUFFER, houseVBO);
glBufferData(GL_ARRAY_BUFFER, sizeof(housePoints), &housePoints, GL_STATIC_DRAW);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 2 * sizeof(float), (void*)0);

接着创建小房子的顶点、片段和几何着色器
HouseVertex.glsl 新建

#version 330 corelayout (location = 0) in vec2 aPos;void main()
{gl_Position = vec4(aPos.x, aPos.y, 0.0, 1.0); 
}

HouseFragment.glsl 新建

#version 330 coreout vec4 FragColor;void main()
{FragColor = vec4(0.0, 1.0, 0.0, 1.0);   
}

HouseGeo.glsl 新建
依次绘制组成小房子的三个三角形

#version 330 corelayout (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);
}

最后创建着色器并绘制

Shader houseShader("Shader/HouseVertex.glsl", "Shader/HouseFragment.glsl", "Shader/HouseGeo.glsl");
//...
houseShader.Use();
glBindVertexArray(houseVAO);
glDrawArrays(GL_POINTS, 0, 1);

编译运行,顺利的话可以看见以下图像
几何着色器
线框模式

glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
houseShader.Use();
glBindVertexArray(houseVAO);
glDrawArrays(GL_POINTS, 0, 1);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);

几何着色器

2、“爆破”

接下来我们实现一个背包爆破的效果
首先通过叉乘计算图元法线,接着将顶点沿图元法线方向偏移
BagExplodeGeo.glsl 新增

#version 330 corelayout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;in VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;vec3 Position;
} gs_in[];out VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;vec3 Position;
} gs_out;uniform float time;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);
}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));
}void main() {    vec3 normal = GetNormal();gl_Position = Explode(gl_in[0].gl_Position, normal);gs_out.FragPos = gs_in[0].FragPos;gs_out.Normal = gs_in[0].Normal;gs_out.TexCoords = gs_in[0].TexCoords;gs_out.Position = gs_in[0].Position;EmitVertex();gl_Position = Explode(gl_in[1].gl_Position, normal);gs_out.FragPos = gs_in[1].FragPos;gs_out.Normal = gs_in[1].Normal;gs_out.TexCoords = gs_in[1].TexCoords;gs_out.Position = gs_in[1].Position;EmitVertex();gl_Position = Explode(gl_in[2].gl_Position, normal);gs_out.FragPos = gs_in[2].FragPos;gs_out.Normal = gs_in[2].Normal;gs_out.TexCoords = gs_in[2].TexCoords;gs_out.Position = gs_in[2].Position;EmitVertex();EndPrimitive();
}

顶点和片段着色器需要一并换成接口块
VertexShader.glsl

#version 330 corelayout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;// 先保留输出
out vec3 FragPos;
out vec3 Normal;
out vec2 TexCoords;
out vec3 Position;uniform mat4 model;
layout (std140) uniform Matrices
{mat4 view;mat4 projection;
};out VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;vec3 Position;
} vs_out;void main()
{gl_Position = projection * view * model * vec4(aPos, 1.0);FragPos = vec3(model * vec4(aPos, 1.0));vs_out.FragPos = FragPos;Normal = mat3(transpose(inverse(model))) * aNormal;vs_out.Normal = Normal;TexCoords = aTexCoords;vs_out.TexCoords = aTexCoords;Position = vec3(model * vec4(aPos, 1.0));vs_out.Position = Position;
}

FragmentShader.glsl

// ...
in VS_OUT {vec3 FragPos;vec3 Normal;vec2 TexCoords;vec3 Position;
} fs_in;
// ...

Main.cpp

// 加上几何着色器
Shader shader("Shader/VertexShader.glsl", "Shader/FragmentShader.glsl", "Shader/BagExplodeGeo.glsl");// 主循环
// 设置时间
shader.SetFloat("time", glfwGetTime());

编译运行,顺利的话可以看见背包在爆破又恢复接着又爆破
几何着色器

3、法线可视化

最后来做一个很使用的功能 法线可视化
我们从顶点着色器中传递法向量到几何着色器,然后在几何着色器中沿着法向量画一条线

NormalVisualVertex.glsl 新增

#version 330 corelayout (location = 0) in vec3 aPos;
layout (location = 1) in vec3 aNormal;
layout (location = 2) in vec2 aTexCoords;out VS_OUT {vec3 normal;
} vs_out;layout (std140) uniform Matrices
{mat4 view;mat4 projection;
};
uniform mat4 model;void main()
{gl_Position = view * model * vec4(aPos, 1.0); mat3 normalMatrix = mat3(transpose(inverse(view * model)));vs_out.normal = normalize(vec3(vec4(normalMatrix * aNormal, 0.0)));
}

NormalVisualGeo.glsl 新增

#version 330 corelayout (triangles) in;
layout (line_strip, max_vertices = 6) out;in VS_OUT {vec3 normal;
} gs_in[];const float MAGNITUDE = 0.2;layout (std140) uniform Matrices
{mat4 view;mat4 projection;
};void GenerateLine(int index)
{gl_Position = projection * gl_in[index].gl_Position;EmitVertex();gl_Position = projection * (gl_in[index].gl_Position +  vec4(gs_in[index].normal, 0.0) * MAGNITUDE);EmitVertex();EndPrimitive();
}void main()
{GenerateLine(0); GenerateLine(1); GenerateLine(2); 
}

GreenFragment.glsl 由HouseFragment.glsl改名

#version 330 coreout vec4 FragColor;void main()
{FragColor = vec4(0.0, 1.0, 0.0, 1.0);   
}

Main.cpp

// 法线可视化Shader
Shader normalVisualShader("Shader/NormalVisualVertex.glsl", "Shader/GreenFragment.glsl", "Shader/NormalVisualGeo.glsl");
// 绑定UBO槽
BindMatericesBlock(normalVisual, normalVisualShader, 0);// 主循环
// 绘制背包之后
// 法线可视化
normalVisualShader.Use();
normalVisualShader.SetMat4("model", modelMatrix);
model.Draw(normalVisualShader);

编译运行,顺利的话可以看见以下图像
当出现光照问题时,我们可以通过法向量可视化来排查
法向量可视化
完整代码可在顶部git仓库中找到

http://www.dtcms.com/a/365992.html

相关文章:

  • 【机器学习深度学习】RAG边界处理策略
  • Vision Pro图像处理工具全解析
  • ClickHouse使用Docker部署
  • 刷新记录:TapData Oracle 日志同步性能达 80K TPS,重塑实时同步新标准
  • mysy2使用
  • Windows 开发环境这么难配,为什么还有那么多人在用?
  • Axure文件上传高保真交互原型:实现Web端真实上传体验
  • 机器能否真正语言?人工智能NLP面临的“理解鸿沟与突破
  • 深度学习---pytorch卷积神经网络保存和使用最优模型
  • awk相关知识
  • C++完美转发
  • 【FastDDS】Layer DDS之Domain ( 04-DomainParticipantFactory)
  • 专项智能练习(Photoshop软件基础)
  • 智能高效内存分配器测试报告
  • 【CMake】message函数
  • C++对象构造与析构
  • numpy meshgrid 转换成pygimli规则网格
  • cppreference_docs
  • 稳居全球TOP3:鹏辉能源“3+N” 布局,100Ah/50Ah等户储电芯产品筑牢市场优势
  • 【C++】Vector核心实现:类设计到迭代器陷阱
  • MySQL:表的约束上
  • C# 代码中的“熵增”概念
  • 单片机:GPIO、按键、中断、定时器、蜂鸣器
  • 《单链表经典问题全解析:5 大核心题型(移除元素 / 反转 / 找中点 / 合并 / 回文判断)实现与详解》
  • 【面试题】词汇表大小如何选择?
  • PS大神级AI建模技巧!效率翻倍工作流,悄悄收藏!
  • 本地化AI问答:告别云端依赖,用ChromaDB + HuggingFace Transformers 搭建离线RAG检索系统
  • OpenCV的阈值处理
  • ChartView的基本介绍与使用
  • shell编程从0基础--进阶 1