在顶点着色器之后,片段着色器之前,还有几何着色器,它是可选的,在《OpenGL基础3:渲染管线》这一章中就有提到了,有了几何着色器后可以做很多骚操作,更容易实现很多有意思的效果

一、最简单的几何着色器(Geometry Shader)

几何着色器的输入:一个或多个表示单独基本图形(primitive)的顶点,这个基本图形可以是点、线或者三角形

输出:处理过后的基本图形顶点,这些顶点无论是大小还是位置,甚至是数量、基本图形单元都可以被改变、拼装

最“简单”的几何着色器如下:

#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
in VS_OUT
{vec2 texIn;vec3 normalIn;vec3 fragPosIn;
}vs_in[];
out vec2 texIn;
out vec3 normalIn;
out vec3 fragPosIn;
void main()
{for (int i = 0; i <= 2; i++){gl_Position = gl_in[i].gl_Position;texIn = vs_in[i].texIn;normalIn = vs_in[i].normalIn;fragPosIn = vs_in[i].fragPosIn;EmitVertex();}EndPrimitive();
}

其实可以看出来,它也不是最简单的几何着色器,因为它接受和输出的顶点都是三角形,而不是点,并且对每个顶点都进行了传输,甚至包括了顶点着色器要传递的值。那之所以拿这个来举例子,是因为它可以应用于链接之前物体的顶点着色器和片段着色器,并且不会对结果进行任何改变

了解了这个几何着色器,就可以很轻松的在这基础上做一些效果了

几何着色器中的 layout 修饰符:

前面两个 layout 标识符是不能多也不能少的,分别声明了输入的基本图元类型,以及要输出的基本图元类型

输入的基本图元类型可以是以下几种:

  • points:绘制 GL_POINTS 基本图形的时候
  • lines:绘制 GL_LINES 或 GL_LINE_STRIP 基本图形的时候
  • lines_adjacency:同上,GL_LINES_ADJACENCY 或 GL_LINE_STRIP_ADJACENCY
  • triangles:GL_TRIANGLES、GL_TRIANGLE_STRIP 或 GL_TRIANGLE_FAN
  • triangles_adjacency:GL_TRIANGLES_ADJACENCY 或 GL_TRIANGLE_STRIP_ADJACENCY

具体哪一个要和绘制时的 glDrawElements 方法对应

输出的基本图元类型就是以下3种:

  • points:点
  • line_strip:线
  • triangle_strip:三角形

对于上面的样例,就可以理解为输入为最基础的三角形图元,并原样输出

可以看到,第二个layout中还设置了 max_vertices = 3,它限制了输出的顶点数量的最大值,如果超出了这个数值,OpenGL就会忽略剩下的顶点

上面描述的所有东西,都可以用一张图来举例:

已知绘制时填入的基本图形是GL_POINTS,并且顶点着色器会正确的输出上图中5个顶点的位置,那么如何通过几何着色器将这5个顶点按照先后的顺序依次相连成线并输出至片段着色器呢?

那么显然,此时输入的基本图元类型就要定义为points,输出图元类型要定义为line_strip,并且设置最大顶点数量max_vertices = 5,假设错误的设置了max_vertices为2,那么就只会绘制第一段线段,如果错误的设置了输出图元类型或者输入图元类型,那么就会直接报错或得到非常离谱的渲染结果

二、内建变量gl_in[]

这是一个非常重要的内建变量,几何着色器正是从中获得顶点着色器传来的顶点信息的,它也是一个默认的接口块,内容如下:

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

这3个变量也都是顶点着色器的内建变量,前2个在《OpenGL基础39:GLSL内建变量与接口块》这一章有详细介绍,其中gl_Position就是位置信息,大部分情况下也只需要用到它(第三个参数先不管)

它被声明为一个数组,因为大多数渲染基本图形由一个以上顶点组成,几何着色器接收一个基本图形的所有顶点作为它的输入,如果你接受的基本图形是三角形,那么这个数组大小就是3

着色器之间传数据通过 in 和 out 关键字,只需要在当前着色器中声明一个输出,在下一个着色器中声明一个同类型同名字的输入就可以,当然,现在在中间多了一个几何着色器,那么顶点着色器中的数据就不会直接传入片段着色器,而是会被传入几何着色器,那么:

  • 在这里几何着色器是中介,要把顶点着色器传入的信息再传入片段着色器
  • 有了顶点着色器的这些信息,当然也可以拿来进行数据的处理

正如上面的第一份代码,里面就将顶点着色器的三个数据(接口块),拆掉并传入片段着色器

下面这份代码,就可以将接受的4个顶点,输出为4条线段,每条线段长度为0.2:

glDrawArrays(GL_POINTS, 0, 4);
#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();
}

三、绘制规则

到这里,几何着色器还有两个非常重要的函数 EmitVertex() 和 EndPrimitive():

  • EmitVertex():每当调用一次,当前设置到gl_Position的向量就会被添加到基本图形上,可以理解为输出一个顶点
  • EndPrimitive():结束当次图元输出

对于左图,顶点①⑤之间并没有连线,对于右图,顶点①②④并没有连成三角形,这也说明了一点,生成线段和三角形的顺序必须要保证正确

  • 对于线段模式:每次EmitVertex(),都会连接当前顶点和上一个顶点
  • 对于三角形模式:每次EmitVertex(),都会生成贴着前一个三角形的新三角形,也就是连接当前顶点和上两个顶点
  • 只要EndPrimitive(),就会“另起一段”开启下一次绘制,也就是前面说的“上x个顶点”就不存在了,下一个顶点就会是“第一个”顶点,重新开始

也就是说,对于右图顶点的顺序正是数字顺序

四、一个例子

尝试修改下最上面的片段着色器,看下能有什么好玩的效果:

①例如将所有的三角形片元都沿着它的法向量方向移动一段距离:

#version 330 core
layout (triangles) in;
layout (triangle_strip, max_vertices = 3) out;
in VS_OUT
{vec2 texIn;vec3 normalIn;vec3 fragPosIn;
}vs_in[];
out vec2 texIn;
out vec3 normalIn;
out vec3 fragPosIn;
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()
{for (int i = 0; i <= 2; i++){gl_Position = gl_in[i].gl_Position + vec4(GetNormal(), 1.0) * 0.1f;texIn = vs_in[i].texIn;normalIn = vs_in[i].normalIn;fragPosIn = vs_in[i].fragPosIn;EmitVertex();}EndPrimitive();
}

②又或者新增一套着色器,来实现物体的法向量可视化:

#version 420 core
layout (location = 0) in vec3 position;
layout (location = 1) in vec3 normal;
out VS_OUT
{vec3 normalIn;
}vs_out;
uniform mat4 model;             //模型矩阵
layout (std140, binding = 0) uniform Matrices
{mat4 view;                  //观察矩阵mat4 projection;            //投影矩阵
};
void main()
{gl_Position = projection * view * model * vec4(position, 1.0);vs_out.normalIn = mat3(transpose(inverse(model))) * normal;
}//#version 330 core
layout (triangles) in;
layout (line_strip, max_vertices = 6) out;
in VS_OUT
{vec3 normalIn;
}vs_in[];
void main()
{for (int i = 0; i <= 2; i++){gl_Position = gl_in[i].gl_Position;EmitVertex();gl_Position = gl_in[i].gl_Position + vec4(normalize(vs_in[i].normalIn), 0.0f) * 0.2f;EmitVertex();EndPrimitive();}
}//#version 330 core
out vec4 color;
void main()
{color = vec4(1.0f, 0.5f, 0.2f, 1.0f);
}

最后:要改写之前的Shader.h:

#ifndef SHADER_H
#define SHADER_H
#include<string>
#include<fstream>
#include<sstream>
#include<iostream>
#include<opengl/glew.h>class Shader
{public:GLuint Program;Shader(const GLchar* vertexPath, const GLchar* fragmentPath, const GLchar* geometryPath = ""){std::string vertexCode;std::string geometryCode;std::string fragmentCode;std::ifstream vShaderFile;std::ifstream gShaderFile;std::ifstream fShaderFile;vShaderFile.exceptions(std::ifstream::badbit);gShaderFile.exceptions(std::ifstream::badbit);fShaderFile.exceptions(std::ifstream::badbit);try{vShaderFile.open("Shader/" + (std::string)vertexPath);fShaderFile.open("Shader/" + (std::string)fragmentPath);std::stringstream vShaderStream, fShaderStream;vShaderStream << vShaderFile.rdbuf();fShaderStream << fShaderFile.rdbuf();vShaderFile.close();fShaderFile.close();vertexCode = vShaderStream.str();fragmentCode = fShaderStream.str();if ((int)((std::string)geometryPath).length() >= 3){std::stringstream gShaderStream;gShaderFile.open("Shader/" + (std::string)geometryPath);gShaderStream << gShaderFile.rdbuf();gShaderFile.close();geometryCode = gShaderStream.str();}}catch (std::ifstream::failure e){std::cout << "ERROR::SHADER::FILE_NOT_SUCCESFULLY_READ" << std::endl;}const GLchar* vShaderCode = vertexCode.c_str();const GLchar* fShaderCode = fragmentCode.c_str();GLuint vertex, geometry, fragment;GLint success;GLchar infoLog[512];vertex = glCreateShader(GL_VERTEX_SHADER);glShaderSource(vertex, 1, &vShaderCode, NULL);glCompileShader(vertex);glGetShaderiv(vertex, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(vertex, 512, NULL, infoLog);std::cout << "ERROR::SHADER::VERTEX::COMPILATION_FAILED\n" << infoLog << std::endl;}geometry = -1;if (geometryCode.size() != 0){const GLchar* gShaderCode = geometryCode.c_str();geometry = glCreateShader(GL_GEOMETRY_SHADER);glShaderSource(geometry, 1, &gShaderCode, NULL);glCompileShader(geometry);glGetShaderiv(geometry, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(geometry, 512, NULL, infoLog);std::cout << "ERROR::SHADER::GEOMETRY::COMPILATION_FAILED\n" << infoLog << std::endl;}}fragment = glCreateShader(GL_FRAGMENT_SHADER);glShaderSource(fragment, 1, &fShaderCode, NULL);glCompileShader(fragment);glGetShaderiv(fragment, GL_COMPILE_STATUS, &success);if (!success){glGetShaderInfoLog(fragment, 512, NULL, infoLog);std::cout << "ERROR::SHADER::FRAGMENT::COMPILATION_FAILED\n" << infoLog << std::endl;}this->Program = glCreateProgram();glAttachShader(this->Program, vertex);glAttachShader(this->Program, fragment);if (geometry != -1)glAttachShader(this->Program, geometry);glLinkProgram(this->Program);glGetProgramiv(this->Program, GL_LINK_STATUS, &success);if (!success){glGetProgramInfoLog(this->Program, 512, NULL, infoLog);std::cout << "ERROR::SHADER::PROGRAM::LINKING_FAILED\n" << infoLog << std::endl;}glDeleteShader(vertex);glDeleteShader(fragment);if (geometry != -1)glDeleteShader(geometry);}void Use(){glUseProgram(this->Program);}
};
#endif

OpenGL基础41:几何着色器相关推荐

  1. OpenGL基础6:着色器

    如果想要完整的代码,可以用上一章最下面那份代码,然后进行局部替换就OK了 一.着色器结构 一个简单的着色器如下: #version 3.4 //版本号in type name1 //输入数据 out ...

  2. OpenGL Tessellation and Geometry Shaders镶嵌和几何着色器的实例

    OpenGL 镶嵌和几何着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <sb7.h> class tessllatedgst ...

  3. [OpenGL] 几何着色器

    reference:https://www.khronos.org/opengl/wiki/Geometry_Shader 几何着色器(GS)是一个使用GLSL编写的处理图元生成的shader程序,它 ...

  4. OpenGL学习笔记(十)-几何着色器-实例化

    参考网址:LearnOpenGL 中文版 4.7 几何着色器 4.7.1 基本概念 1.顶点和片段着色器之间有一个可选的几何着色器,几何着色器的输入是一个图元(如点或三角形)的一组顶点,顶点发送到下一 ...

  5. OpenGL 几何着色器细分的实例

    OpenGL 几何着色器细分 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <vmath.h> #include <cmath ...

  6. OpenGL 几何着色器剔除的实例

    OpenGL 几何着色器剔除 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <vmath.h> #include <objec ...

  7. OpenGL几何着色器

    OpenGL几何着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <stdio.h> #include "GL/glu ...

  8. OpenGL 几何着色器Geometry Shader

    OpenGL几何着色器Geometry Shader 几何着色器Geometry Shader简介 使用几何着色器 造几个房子 爆破物体 法向量可视化 几何着色器Geometry Shader简介 在 ...

  9. opengl 纹理贴到对应的位置_一步步学OpenGL(27) -《公告牌技术与几何着色器》

    教程 27 公告牌技术与几何着色器 原文: http://ogldev.atspace.co.uk/www/tutorial27/tutorial27.html CSDN完整版专栏: https:// ...

最新文章

  1. 嵌入式程序员应知道的0x10个基本问题
  2. 【最短路】hxk化学课
  3. 一个最简单的UDP通信
  4. 排序(基本概念及分类,直接插入排序和希尔排序)
  5. mysql帐号不允许从远程登陆
  6. 电子计算机说明文作文,关于电脑说明文作文(精选3篇)
  7. Unix环境高级编程笔记:12、高级IO
  8. 【贪心 和 DP + 卖股票】LeetCode 122. Best Time to Buy and Sell Stock II
  9. 反思风险管理的五项核心风险。
  10. Typora保留文本格式
  11. ReactNative Ios打包流程
  12. fedora 11源码安装设置fcitx3.6输入法
  13. delphi SysErrorMessage 函数和系统错误信息表 good
  14. 5种经典的数据分析思维和方法
  15. 计算机上如何保存ico格式,PS怎么保存ico格式
  16. Android商城开发(一)——一次活动页需求引发的危机感
  17. How to edit registry via CMD command
  18. 在软件工程领域,搞科研的这十年!
  19. 苹果平板可以用html么,苹果iPad怎么用?iPad新手必备十个使用心得分享(必看)...
  20. win7打开远程计算机,win7系统开启远程桌面实现远程连接图文教程

热门文章

  1. python入门指南txt-十分钟搞定 C/C++ 项目自动化构建 —— Xmake 入门指南
  2. 如何系统的自学python-自学Python应该如何正确系统学习,避免少走弯路
  3. python工资高还是java-Python工资高还是Java?
  4. python软件下载安装-python安装最新下载_python安装绿色版 - 软件帝
  5. python工资一般多少p-Python是什么?简单了解pythonp-入门
  6. python有道-Python爬取有道词典
  7. android 4实例分析,OpenGL Shader实例分析(4)闪光效果
  8. php获取用户当前坐标,web端定位:获取当前地理位置
  9. boost.asio mysql_boost asio学习笔记
  10. 保留五天的日志 php,怎样让日志在归档目录保留5天?