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.几何着色器
本人初学者,文中定有代码、术语等错误,欢迎指正 文章目录 几何着色器使用几何着色器造几个房子爆破物体法向量可视化 几何着色器 简介 在顶点和片段着色器之间有一个可选的几何着色器几何着色器的输入是一个图元(如点或三角形)的一…...
8.视图和用户管理
目录 视图 基本使用 用户管理 用户 用户信息 创建用户 删除用户...
bootstrapvue上传文件并存储到服务器指定路径及从服务器某路径下载文件
前记 第一次接触上传及下载文件,做个总结。 从浏览器上传本地文件 前端 本处直接将input上传放在了button内实现。主要利用了input的type“file” 实现上传框。其中accept可以限制弹出框可选择的文件类型。可限制多种: :accept"[doc, docx]&qu…...
 
Qt OpenGL(四十二)——Qt OpenGL 核心模式-GLSL(二)
提示:本系列文章的索引目录在下面文章的链接里(点击下面可以跳转查看): Qt OpenGL 核心模式版本文章目录 Qt OpenGL(四十二)——Qt OpenGL 核心模式-GLSL(二) 冯一川注:GLSL其实也是不断迭代的,比如像3.3版本中,基本数据类型浮点型只支持float型,而GLSL4.0版本开始就…...
C++基础讲解第八期(智能指针、函数模板、类模板)
C基础讲解第八期 代码中也有对应知识注释,别忘看,一起学习! 一、智能指针二、模板1. 概念2.函数模板1. 函数模板和普通函数 3. 类模板1.类模板的定义2.举个例子3.举例 一、智能指针 举个栗子: 看下面代码, 当我们直接new一个指针时, 忘记dele…...
 
JMeter 测试 ActiveMq
JMeter 测试 ActiveMq 的资料非常少, 我花了大量的时间才研究出来 关于ActiveMq 的文章请参考我另外的文章。 版本号: ActiveMq 版本号: 5.91 Jmeter 版本号: 1.13 添加ActiveMq 的jar包 将 ActiveMq 下的 "activemq-all-5.9.1.jar" 复制…...
 
2023年4月和5月随笔
1. 回头看 为了不耽误学系列更新,4月随笔合并到5月。 日更坚持了151天,精读完《SQL进阶教程》,学系统集成项目管理工程师(中项)系列更新完成。 4月和5月两月码字114991字,日均码字数1885字,累…...
 
新Linux服务器安装Java环境[JDK、Tomcat、MySQL、Nacos、Redis、Nginx]
文章目录 JDK服务Tomcat服务MySQL服务Nacos服务Redis服务Nginx服务 说明:本文不使用宝塔安装 温馨提示宝塔安装命令:yum install -y wget && wget -O install.sh http://download.bt.cn/install/install_6.0.sh && sh install.sh JDK服务…...
 
精简总结:一文说明软件测试基础概念
基础概念-1 基础概念-2 目录 一、什么是软件测试? 二、软件测试的特点 三、软件测试和开发的区别 1、内容: 2、技能区别 3、工作环境 4、薪水 5、发展前景 6、繁忙程度 7、技能要求 四、软件测试与调试的区别 1、角色 2、目的 3、执行的阶…...
 
通过 Gorilla 入门机器学习
机器学习是一种人工智能领域的技术和方法,旨在让计算机系统能够从数据中学习和改进,而无需显式地进行编程。它涉及构建和训练模型,使其能够自动从数据中提取规律、进行预测或做出决策。 我对于机器学习这方面的了解可以说是一片空白…...
【二叉树】298. 二叉树最长连续序列
文章目录 一、题目1、题目描述2、基础框架3、原题链接 二、解题报告1、思路分析2、时间复杂度3、代码详解 三、本题小知识 一、题目 1、题目描述 给你一棵指定的二叉树的根节点 root ,请你计算其中 最长连续序列路径 的长度。 最长连续序列路径 是依次递增 1 的路…...
 
Matlab论文插图绘制模板第100期—紧凑排列多子图(Tiledlayout)
不知不觉,《Matlab论文插图绘制模板》系列来到了第100期。 在此之前,其实我也没想到会有这么多种数据可视化表达方式,论文里不是折线图就是柱状图,单调的很。 假如研究生那会要是能遇到现在的自己(分享的内容&#x…...
 
[2.0快速体验]Apache Doris 2.0 日志分析快速体验
1. 概述 应用程序、服务器、云基础设施、IoT 和移动设备、DevOps、微服务架构—最重要的业务和 IT 发展趋势帮助我们以前所未有的方式优化运维和客户体验。但这些趋势也导致由机器生成的数据出现爆炸式成长,其中包括日志和指标等,例如,用户交…...
 
MySQL学习-数据库创建-数据库增删改查语句-事务-索引
MySQL学习 前言 SQL是结构化查询语言的缩写,用于管理关系数据库(RDBMS)中的数据。SQL语言由IBM公司的Donald Chamberlin和Raymond Boyce于20世纪70年代开发而来,是关系型数据库最常用的管理语言。 使用SQL语言可以实现关系型数据库中的数据处理、数据…...
浏览器渗透攻击-渗透测试模拟环境(9)
介绍了浏览器供给面和堆喷射技术。 “客户端最流行的应用软件是什么,大家知道吗?” 这个简单的问题,你当然不会放过:“当然是浏览器,国内用得最多的估计还是 IE 浏览器,其实 360安全浏览器遨游啥的也都是基于IE内核的。” “OK,浏览器是客户端渗透攻击的首要目标,目前IE…...
 
MySQL数据库基础(基础命令详解)
1、数据库操作 1.1、显示当前的数据库 SHOW DATABASES; 1.2、创建数据库 CREATE DATABASE IF NOT EXISTS 库名; 1.3、使用数据库 USE 库名; 1.4、删除数据库 DROP DATABASE IF EXISTS 库名; 说明:数据库删除之后,内部看不到对应…...
 
企业培训直播场景下嘉宾连线到底是如何实现的?
企业培训直播场景下,进行音视频连线的嘉宾,都拥有面向学员教学的权限,支持多位老师/专家异地同堂授课,那么,这种嘉宾连线到底是如何实现的? 企业培训,如何做到不受时间和地点限制,实…...
 
五、JSP05 分页查询及文件上传
五、JSP 分页查询及文件上传 5.1 使用分页显示数据 通过网络搜索数据时最常用的操作,但当数据量很大时,页面就会变得冗长,用户必须拖动才能浏览更多的数据 分页是把数据库中需要展示的数据逐页分步展示给用户 以分页的形式显示数据ÿ…...
 
一起看 I/O | 借助 Google Play 管理中心价格实验,优化定价策略
作者 / Google Play 产品经理 Phalene Gowling 今年 Google I/O 大会上的 "通过 Google Play Commerce 提升收益" 演讲重点为您介绍了深度集成至 Google Play 的最新创收工具。此工具专注于帮您优化定价策略。为您的产品或内容确定合适的价格是实现更出色的用户生命周…...
hexview 命令行操作使用说明
hexview 命令行操作使用说明 命令行操作基础格式 hexview.exe infile [option] -o outfile提取部分内容 hexview.exe app.hex /AR:0X200000-0X303404 /s /XI -o app1.hex/AR:指定提取的范围。(也可以使用/CR,它可以指定多个范围࿰…...
Python|GIF 解析与构建(5):手搓截屏和帧率控制
目录 Python|GIF 解析与构建(5):手搓截屏和帧率控制 一、引言 二、技术实现:手搓截屏模块 2.1 核心原理 2.2 代码解析:ScreenshotData类 2.2.1 截图函数:capture_screen 三、技术实现&…...
 
超短脉冲激光自聚焦效应
前言与目录 强激光引起自聚焦效应机理 超短脉冲激光在脆性材料内部加工时引起的自聚焦效应,这是一种非线性光学现象,主要涉及光学克尔效应和材料的非线性光学特性。 自聚焦效应可以产生局部的强光场,对材料产生非线性响应,可能…...
 
【OSG学习笔记】Day 18: 碰撞检测与物理交互
物理引擎(Physics Engine) 物理引擎 是一种通过计算机模拟物理规律(如力学、碰撞、重力、流体动力学等)的软件工具或库。 它的核心目标是在虚拟环境中逼真地模拟物体的运动和交互,广泛应用于 游戏开发、动画制作、虚…...
 
STM32标准库-DMA直接存储器存取
文章目录 一、DMA1.1简介1.2存储器映像1.3DMA框图1.4DMA基本结构1.5DMA请求1.6数据宽度与对齐1.7数据转运DMA1.8ADC扫描模式DMA 二、数据转运DMA2.1接线图2.2代码2.3相关API 一、DMA 1.1简介 DMA(Direct Memory Access)直接存储器存取 DMA可以提供外设…...
 
Vue2 第一节_Vue2上手_插值表达式{{}}_访问数据和修改数据_Vue开发者工具
文章目录 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染2. 插值表达式{{}}3. 访问数据和修改数据4. vue响应式5. Vue开发者工具--方便调试 1.Vue2上手-如何创建一个Vue实例,进行初始化渲染 准备容器引包创建Vue实例 new Vue()指定配置项 ->渲染数据 准备一个容器,例如: …...
【C语言练习】080. 使用C语言实现简单的数据库操作
080. 使用C语言实现简单的数据库操作 080. 使用C语言实现简单的数据库操作使用原生APIODBC接口第三方库ORM框架文件模拟1. 安装SQLite2. 示例代码:使用SQLite创建数据库、表和插入数据3. 编译和运行4. 示例运行输出:5. 注意事项6. 总结080. 使用C语言实现简单的数据库操作 在…...
 
【C++进阶篇】智能指针
C内存管理终极指南:智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
 
Linux nano命令的基本使用
参考资料 GNU nanoを使いこなすnano基础 目录 一. 简介二. 文件打开2.1 普通方式打开文件2.2 只读方式打开文件 三. 文件查看3.1 打开文件时,显示行号3.2 翻页查看 四. 文件编辑4.1 Ctrl K 复制 和 Ctrl U 粘贴4.2 Alt/Esc U 撤回 五. 文件保存与退出5.1 Ctrl …...
提升移动端网页调试效率:WebDebugX 与常见工具组合实践
在日常移动端开发中,网页调试始终是一个高频但又极具挑战的环节。尤其在面对 iOS 与 Android 的混合技术栈、各种设备差异化行为时,开发者迫切需要一套高效、可靠且跨平台的调试方案。过去,我们或多或少使用过 Chrome DevTools、Remote Debug…...
