LearnOpenGL-高级OpenGL-9.几何着色器

news2024/10/6 12:28:42

本人初学者,文中定有代码、术语等错误,欢迎指正

文章目录

  • 几何着色器
  • 使用几何着色器
    • 造几个房子
    • 爆破物体
    • 法向量可视化

几何着色器

  • 简介

    • 在顶点和片段着色器之间有一个可选的几何着色器
    • 几何着色器的输入是一个图元(如点或三角形)的一组顶点。
    • 几何着色器可以在顶点发送到下一着色器阶段之前对它们随意变换
  • 代码例子

    #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
  • 说明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个三角形

    • 图示

  • 从而推出房子需要的顶点,以及顺序

    顶点为:123456

    生成的三角形有:(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只有一个顶点,所以下标为0
    	fColor = 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-left
        0.5f,  0.5f, 0.0f, 1.0f, 0.0f, // top-right
        0.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会自动执行

      1. 透视除法到标准化设备坐标系

      2. 再经过视口变换到屏幕坐标

    • 所以

      在几何着色器的时候,顶点此时不在裁剪空间,而是在屏幕坐标系,从而绘制出来的法线不正确!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/606320.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【02】STM32·HAL库开发-Cortex-M系列介绍 | Cortex内核分类及特征 | Cortex-M3/M4/M7介绍

目录 1.ARM公司&#xff08;了解&#xff09;2.Cortex内核分类及特征&#xff08;了解&#xff09;3.Cortex-M3/4/7介绍&#xff08;了解&#xff09; 1.ARM公司&#xff08;了解&#xff09; ARM的R是RISC&#xff08;精简指令集计算机&#xff09;的缩写。ARM公司只做内核设计…

数学模型在水环境影响评价、防洪评价与排污口论证项目中的应用

数学模型在水环境评价、防洪评价和排污口论证等领域中的重要作用&#xff0c;随着人类活动的不断增加和环境问题的日益突出&#xff0c;对水资源和水环境的保护与管理变得至关重要。为了更好地理解和应对这些挑战&#xff0c;数学模型成为一种强大的工具&#xff0c;能够提供量…

ubuntu22.04使用kk安装kubernates1.20.4和kubesphere3.1.1

注意 存储空间不够可能安装失败 环境 master 192.168.1.108node1 192.168.1.106node2 192.168.1.102 root ssh登录 sudo passwd root sudo apt install openssh-server # 定位 /PermitRootLogin 添加 PermitRootLogin yes # 注释掉#PermitRootLogin prohibit-password #St…

【Jenkins+Ant+Jmeter】持续集成接口测试平台搭建

一、环境准备&#xff1a; 1、JDK&#xff1a;Java Downloads | Oracle 2、Jmeter&#xff1a;Apache JMeter - Download Apache JMeter 3、Ant&#xff1a;Apache Ant - Binary Distributions 4、Jenkins&#xff1a;Jenkins 二、Jemter脚本准备&#xff1a; 1、脚本目录&a…

PyTorch实验—回归任务

PyTorch回归任务 回归任务概述&#xff1a;通过pytorch搭建神经网络&#xff0c;进行气温的预测 回归任务可以看作 y kx b y为需要进行回归预测的值 下面对实验步骤进行整理 导入相关的库 import numpy as np import pandas as pd import matplotlib.pyplot as plt import…

张小飞的Java之路——第四十四章——其他流对象

写在前面&#xff1a; 视频是什么东西&#xff0c;有看文档精彩吗&#xff1f; 视频是什么东西&#xff0c;有看文档速度快吗&#xff1f; 视频是什么东西&#xff0c;有看文档效率高吗&#xff1f; 诸小亮&#xff1a;这一节&#xff0c;我们介绍一下其他不常用的流对象 …

SAP-MM-分割评估-评估类型-评估类别

同一物料的使用&#xff0c;既有“自制品”&#xff0c;又有“外购品”&#xff0c;并且其来源不同&#xff0c;如同一外购品由不同的供应商提供&#xff0c;价格也不相同。也就是说:同一物料有不同的价值指派&#xff0c;即在不同的条件下&#xff0c;同一物料可能有不同的价值…

智能型数字档案馆构建设想

档案作为企业正式权威的数据资源&#xff0c;具有其历史传承和凭证唯一性等特点&#xff0c;随着企业的数字化转型&#xff0c;档案工作更需要数字化转型&#xff0c;档案管理与利用急需借助信息技术手段来管理好和记录好&#xff0c;急需挖掘档案资源&#xff0c;发挥其价值&a…

01.硬盘启动盘,加载操作系统

硬盘启动盘&#xff0c;加载操作系统 模拟硬盘加载操作系统 环境&#xff1a; VMware16 Ubuntu16.04 qemu bochs 2.7 参考: 启动&#xff0c;BIOS&#xff0c;MBR 硬盘控制器主要端口寄存器 《操作系统真相还原》 1.系统开机流程 暂不构建中断向量表&#xff0c;直接加载MBR …

Knowledge Distillation: A Survey

本文是蒸馏学习综述系列的第一篇文章&#xff0c;主要是针对2021年 IJCV Knowledge Distillation: A Survey的一个翻译。 知识蒸馏&#xff1a;综述 摘要1 引言2 知识2.1 基于响应的知识2.2 基于特征的知识2.3 基于关系的知识 3 蒸馏方案3.1 离线蒸馏3.2 在线蒸馏3.3 自蒸馏 4…

你真的了解epoll吗?深入epoll的五个问题

由于epoll用的比较多&#xff0c;最近看到一些网友关于epoll的问答&#xff0c;所以我就想整理成一篇文章&#xff0c;这样看起来和理解起来都方便一些。 问题1&#xff1a;什么是epoll的ET/LT模式&#xff0c;select/poll支持吗&#xff1f; ET是edge trigger&#xff0c;也…

K8s in Action 阅读笔记——【9】Deployments: updating applications declaratively

K8s in Action 阅读笔记——【9】Deployments: updating applications declaratively 集群配置&#xff1a; 本章介绍如何更新运行在Kubernetes集群中的应用&#xff0c;以及Kubernetes如何帮助你实现真正的零停机更新过程。虽然这可以仅使用ReplicationControllers或ReplicaSe…

【Spring】javaBean、依赖注入、面向切面AOP、使用注解开发

文章目录 JavaBeanIoC理论基础使用IoC容器使用Spring 生命周期与继承生命周期继承 依赖注入 Dependency Injection基本类型注入非基本类型注入集合注入自动装配注入 面向切面AOP使用SpringAOP环绕方法 使用接口实现AOP 使用注解开发注解实现配置文件注解实现AOP操作其他注解配置…

MongoDB6.0.6 副本集搭建(CentOs8)

本文只说如何操作配置副本集&#xff0c;历程艰难&#xff0c;官网文档看了半天也只说了怎么添加单个&#xff0c;没有给出来一个完整的流程。 1.第一步安装&#xff0c;参考前一篇安装即可。 配置三台虚拟机&#xff1a; 192.168.182.142 192.168.182.143 192.168.182.14…

钉钉小程序页面之间传递数据如何传递对象

今天写代码的时候&#xff0c;发现了一个问题&#xff0c;在钉钉小程序页面之间传递对象数据的时候&#xff0c;如果直接传递一个对象那个的话&#xff0c;接收的那个页面得到的是一个【object Object】&#xff0c;而并非里面的数据&#xff0c;所以针对上述问题&#xff0c;下…

基于Spring Boot的学生志愿者管理系统的设计与实现

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对学生志愿者管理等问题&#xff0c;对学生…

项目计划软件 project安装包的下载和安装教程

目录 简介 安装配置过程 总结&#xff1a; 简介 Project是由微软公司开发的项目管理软件&#xff0c;旨在帮助个人和团队有效地管理项目进度、资源分配、协作和报告等工作&#xff0c;从而提高项目的质量和效率。Project维护项目的进程表、资源清单、成本预算、工作表和报告…

CSS-HTML知识点与高频考题解析

知识点梳理 选择器的权重和优先级 盒模型 盒子大小计算margin 的重叠计算 浮动 float浮动布局概念清理浮动 定位 position文档流概念定位分类fixed 定位特点绝对定位计算方式 flex布局 如何实现居中对齐&#xff1f; 理解语义化 CSS3 动画 重绘和回流 选择器的权重和优…

VTK 开发中遇到问题整理

1 Generic Warning VTK 开发 中是到 vtkOutputWindow 弹窗并提示Generic Warning&#xff1a;… vtkOutputWindow 弹窗 解决方法&#xff1a; 添加&#xff1a; #include <vtkOutputWindow.h> 在 main.cpp函数中添加&#xff1a; vtkOutputWindow::SetGlobalWarningD…

petalinux2022.2在ubantu20.04下的安装

1.Petalinux的下载路径 Downloads 这个是下载petalinux的官网路径。默认是2022.2版本&#xff0c;后期更新的均是以petalinux2022.2版本做的更新。 2.安装流程 在官网下载完成之后&#xff0c;会得到一个名为petalinux-v2022.2-10141622-installer.run的文件&#xff0c;这个文…