LearnOpenGL-高级OpenGL-9.几何着色器
本人初学者,文中定有代码、术语等错误,欢迎指正
文章目录
- 几何着色器
- 使用几何着色器
- 造几个房子
- 爆破物体
- 法向量可视化
几何着色器
简介
- 在顶点和片段着色器之间有一个可选的几何着色器
- 几何着色器的输入是一个图元(如点或三角形)的一组顶点。
- 几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换
代码例子
#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(); }
- 输入的图元类型:layout (points) in;
points
:绘制GL_POINTS图元时(一个图元包含最小1个顶点数)。lines
:绘制GL_LINES或GL_LINE_STRIP时(2)lines_adjacency
:GL_LINES_ADJACENCY或GL_LINE_STRIP_ADJACENCY(4)triangles
:GL_TRIANGLES、GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN(3)triangles_adjacency
:GL_TRIANGLES_ADJACENCY或GL_TRIANGLE_STRIP_ADJACENCY(6)
- 几何着色器输出的图元类型:layout (line_strip, max_vertices = 2) out;
points
line_strip
triangle_strip
- 输入的图元类型:layout (points) in;
说明line_strip
layout (line_strip, max_vertices = 5) out;
内建变量
我们需要某种方式来获取前一着色器阶段的输出
in gl_Vertex// 4.8节讲的接口块 {vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[]; } gl_in[];
要注意的是,它被声明为一个数组,因为大多数的渲染图元包含多于1个的顶点,而几何着色器的输入是一个图元的所有顶点。
生成线条
void main() {gl_Position = gl_in[0].gl_Position + vec4(-0.1, 0.0, 0.0, 0.0); EmitVertex();// gl_Position添加到图元中gl_Position = gl_in[0].gl_Position + vec4( 0.1, 0.0, 0.0, 0.0);EmitVertex();EndPrimitive();// 合成 }
- 每次我们调用EmitVertex时,gl_Position中的向量会被添加到图元中来
- 当EndPrimitive被调用时,所有发射出的(Emitted)顶点都会合成为指定的输出渲染图元。
使用几何着色器
造几个房子
分析
我们可以将几何着色器的输出设置为triangle_strip,并绘制三个三角形:其中两个组成一个正方形,另一个用作房顶。
triangle_strip说明
在第一个三角形绘制完之后,每个后续顶点将会在上一个三角形边上生成另一个三角形:每3个临近的顶点将会形成一个三角形
例子
顶点为:123456
生成的三角形有:(1, 2, 3)、(2, 3, 4)、(3, 4, 5)和(4, 5, 6),共形成4个三角形
图示
从而推出房子需要的顶点,以及顺序
顶点为:12345
生成的三角形有:(1, 2, 3)、(2, 3, 4)和(3, 4, 5),共形成3个三角形
代码
#version 330 core layout (points) in;//输入 layout (triangle_strip, max_vertices = 5) out;// 输出,5个顶点in VS_OUT{// 4.8节讲的接口块vec3 color; }gs_in[];out vec3 fColor;void build_house(vec4 position){// 因为points只有一个顶点,所以下标为0fColor = gs_in[0].color;//1234顶点使用同一个颜色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(); } void main(){build_house(gl_in[0].gl_Position); }
float points[] = {-0.5f, 0.5f, 1.0f, 0.0f, 0.0f, // top-left0.5f, 0.5f, 0.0f, 1.0f, 0.0f, // top-right0.5f, -0.5f, 0.0f, 0.0f, 1.0f, // bottom-right-0.5f, -0.5f, 1.0f, 1.0f, 0.0f // bottom-left };glClearColor(0.1f, 0.1f, 0.1f, 1.0f); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glEnable(GL_DEPTH_TEST);shader.use(); glBindVertexArray(VAO); glDrawArrays(GL_POINTS, 0 ,4);
效果
爆破物体
分析
我们是要将每个三角形沿着法向量的方向移动一小段时间。效果就是,整个物体看起来像是沿着每个三角形的法线向量爆炸一样。
代码
vs:顶点着色器
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 2) in vec2 aTexCoords;out VS_OUT{// 4.8的接口块vec2 texCoords; }vs_out;uniform mat4 projection; uniform mat4 model; uniform mat4 view;void main() {gl_Position = projection * view * model * vec4(aPos, 1.0);// 变换到裁剪空间vs_out.texCoords = aTexCoords; }
gs:几何着色器-关键地方
#version 330 core layout (triangles) in; layout (triangle_strip, max_vertices = 3) out;// 输出,3个顶点// 从顶点着色器传入 in VS_OUT{vec2 texCoords; }gs_in[];// 为了传入给片段着色器 out vec2 TexCoords;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));// a、b向量的叉积:第三个向量(法线)并垂直于a、b } void main(){vec3 normal = GetNormal();gl_Position = explode(gl_in[0].gl_Position, normal);TexCoords = gs_in[0].texCoords;EmitVertex();gl_Position = explode(gl_in[1].gl_Position, normal);TexCoords = gs_in[1].texCoords;EmitVertex();gl_Position = explode(gl_in[2].gl_Position, normal);TexCoords = gs_in[2].texCoords;EmitVertex();EndPrimitive(); }
分析:
- vs顶点着色器将顶点变换到裁剪空间后传给几何着色器
- 几何着色器的顶点处于裁剪空间中,那么这里计算的法线是计算裁剪空间顶点的法线
fs
#version 330 core out vec4 FragColor;in vec2 TexCoords;uniform sampler2D texture_diffuse1;void main(){ FragColor = texture(texture_diffuse1, TexCoords); }
cpp
// 渲染这个模型 model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f)); model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f)); shader.use(); shader.setMat4("model", model); shader.setMat4("view", view); shader.setMat4("projection", projection);shader.setFloat("time", static_cast<float>(glfwGetTime()));
效果
法向量可视化
引出
检测法向量是否正确的一个很好的方式就是对它们进行可视化,几何着色器正是实现这一目的非常有用的工具。
实现思路
我们首先不使用几何着色器正常绘制场景
然后再次绘制场景,但这次只显示通过几何着色器生成法向量。
几何着色器接收一个三角形图元,并沿着法向量生成三条线——>每个顶点一个法向量
代码
法线可视化的着色器
vs
#version 330 core layout (location = 0) in vec3 aPos; layout (location = 1) in vec3 aNormal;out VS_OUT{vec3 normal; }vs_out;uniform mat4 model; uniform mat4 view;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))); }
gs
#version 330 core layout (triangles) in; // 输入:一个三角形3个顶点 layout (line_strip, max_vertices = 6) out;// 输出:3条线,每条线2个顶点,共6个顶点// 从顶点着色器传入 in VS_OUT{vec3 normal; }gs_in[];const float MAGNITUDE = 0.02;uniform mat4 projection;// 投影矩阵 // 从点变成线 void GenerateLine(int index){gl_Position = projection * gl_in[index].gl_Position;// 起始点变换到裁剪空间EmitVertex();// 1.在观察空间中线的终顶点沿着法线增长 2.顶点再变换到裁剪空间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); }
分析:
vs顶点着色器将顶点变换到观察空间后传给几何着色器
所以法线也要变换到观察空间再传给几何着色器
几何着色器的顶点
在观察空间沿着法线增长
(gl_in[index].gl_Position + vec4(gs_in[index].normal, 0.0) * MAGNITUDE)
增长后的顶点与projection投影矩阵相乘在裁剪空间
然后传给片段着色器之前:经过透视除法到标准化设备坐标系,再经过视口变换到屏幕坐标(opengl自动执行)
fs
#version 330 core out vec4 FragColor;void main(){ FragColor = vec4(1.0, 1.0, 0.0, 1.0); }
cpp
Shader shader("assest/shader/3模型/3.1.模型加载.vs", "assest/shader/3模型/3.1.模型加载.fs"); Shader normalshader("assest/shader/4高级OpenGL/6.9.3.几何着色器-模型法向量可视化.vs", "assest/shader/4高级OpenGL/6.9.3.几何着色器-模型法向量可视化.fs", "assest/shader/4高级OpenGL/6.9.3.几何着色器-模型法向量可视化.gs"); while (!glfwWindowShouldClose(window)) {glm::mat4 model = glm::mat4(1.0f);glm::mat4 view = camera.GetViewMatrix();glm::mat4 projection = glm::perspective(glm::radians(camera.Zoom), (float)SCR_WIDTH / (float)SCR_HEIGHT, 0.1f, 100.0f);// 渲染这个模型model = glm::translate(model, glm::vec3(0.0f, 0.0f, 0.0f));model = glm::scale(model, glm::vec3(0.1f, 0.1f, 0.1f));shader.use();shader.setMat4("model", model);shader.setMat4("view", view);shader.setMat4("projection", projection);ourModel.Draw(shader);// 由几何着色器的设置,顶点的位置,渲染为法线normalshader.use();normalshader.setMat4("model", model);normalshader.setMat4("view", view);normalshader.setMat4("projection", projection);ourModel.Draw(normalshader);
效果
疑问点
为什么要在观察空间中顶点沿着法线增长变成线。
几何着色器不可以直接在裁剪空间下对顶点增长吗?
测试代码:
void main() { // 顶点变换到裁剪空间gl_Position = projection * view * model * vec4(aPos, 1.0);// 将法线变换到裁剪空间mat3 normalMatrix = mat3(transpose(inverse(projection * view * model)));vs_out.normal = normalize(vec3(vec4(normalMatrix * aNormal, 0.0))); }
void GenerateLine(int index){// 已经在裁剪空间下,不需要乘以投影矩阵了gl_Position = gl_in[index].gl_Position;EmitVertex();gl_Position = (gl_in[index].gl_Position + vec4(gs_in[index].normal, 0.0) * MAGNITUDE);EmitVertex();EndPrimitive(); }
会发现绘制出来的线很奇怪
个人猜测:
前置知识
由1.8所讲的坐标系统中提到的:一旦顶点进入到裁剪空间,那么OpenGL会自动执行
透视除法到标准化设备坐标系
再经过视口变换到屏幕坐标
所以
在几何着色器的时候,顶点此时不在裁剪空间,而是在屏幕坐标系,从而绘制出来的法线不正确!
LearnOpenGL-高级OpenGL-9.几何着色器相关推荐
- 第二十二章 opengl之高级OpenGL(几何着色器)
OpenGL 使用几何着色器 用点造物体 爆破物体 法向量可视化 在顶点和片段着色器之间有一个可选的几何着色器(Geometry Shader), 几何着色器的输入是一个图元(如点或三角形)的一组顶点 ...
- OpenGL学习笔记(十)-几何着色器-实例化
参考网址:LearnOpenGL 中文版 4.7 几何着色器 4.7.1 基本概念 1.顶点和片段着色器之间有一个可选的几何着色器,几何着色器的输入是一个图元(如点或三角形)的一组顶点,顶点发送到下一 ...
- OpenGL Tessellation and Geometry Shaders镶嵌和几何着色器的实例
OpenGL 镶嵌和几何着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <sb7.h> class tessllatedgst ...
- OpenGL 几何着色器细分的实例
OpenGL 几何着色器细分 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <vmath.h> #include <cmath ...
- OpenGL 几何着色器剔除的实例
OpenGL 几何着色器剔除 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <vmath.h> #include <objec ...
- OpenGL几何着色器
OpenGL几何着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <stdio.h> #include "GL/glu ...
- OpenGL 几何着色器Geometry Shader
OpenGL几何着色器Geometry Shader 几何着色器Geometry Shader简介 使用几何着色器 造几个房子 爆破物体 法向量可视化 几何着色器Geometry Shader简介 在 ...
- opengl 纹理贴到对应的位置_一步步学OpenGL(27) -《公告牌技术与几何着色器》
教程 27 公告牌技术与几何着色器 原文: http://ogldev.atspace.co.uk/www/tutorial27/tutorial27.html CSDN完整版专栏: https:// ...
- OpenGL基础41:几何着色器
在顶点着色器之后,片段着色器之前,还有几何着色器,它是可选的,在<OpenGL基础3:渲染管线>这一章中就有提到了,有了几何着色器后可以做很多骚操作,更容易实现很多有意思的效果 一.最简单 ...
- [OpenGL] 几何着色器
reference:https://www.khronos.org/opengl/wiki/Geometry_Shader 几何着色器(GS)是一个使用GLSL编写的处理图元生成的shader程序,它 ...
最新文章
- 机器学习中算法的性能评估
- Linux网络包接收过程的监控与调优
- Django 入门项目案例开发(下)——创建项目应用及模型类
- Linux怎么查询全部容器时间,docker容器与Linux主机环境获取时间不一致
- ubuntu安装五笔输入法(ibus-table-wubi)
- Linux中查看日志文件的正确姿势,求你别tail走天下了!
- python开发自动化创建一个任务下发到手机_如何利用Fabric自动化你的任务
- 安卓系统应用启动流程分析
- Go 语言泛型,简明入门教程
- springBoot入门第一章springBoot第一个程序
- Spark算子与RDD基本转换
- XP系统访问win7共享文件夹教程和提示没有权限的解决办法
- SPSS统计检验中的边缘显著及其转化处理【SPSS 063期】
- 手把手·教你用 Echarts 画 ChinaMap
- 市县城镇开发边界内1:500地形图无人机航测实践(M300 RTK+P1+大疆智图+航天远景采集软件)
- pycharm windows 重置_pycharm重置设置,恢复默认设置
- linux搭建天地伟业easy7,天地伟业easy7客户端
- w ndows10卸载word,windows10系统下怎样卸载office 2003
- GIS招聘 | 云南省自然资源厅所属事业单位
- excel页码怎么设置从4开始?