OpenGL基础41:几何着色器
在顶点着色器之后,片段着色器之前,还有几何着色器,它是可选的,在《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:几何着色器相关推荐
- OpenGL基础6:着色器
如果想要完整的代码,可以用上一章最下面那份代码,然后进行局部替换就OK了 一.着色器结构 一个简单的着色器如下: #version 3.4 //版本号in type name1 //输入数据 out ...
- OpenGL Tessellation and Geometry Shaders镶嵌和几何着色器的实例
OpenGL 镶嵌和几何着色器 先上图,再解答. 完整主要的源代码 源代码剖析 先上图,再解答. 完整主要的源代码 #include <sb7.h> class tessllatedgst ...
- [OpenGL] 几何着色器
reference:https://www.khronos.org/opengl/wiki/Geometry_Shader 几何着色器(GS)是一个使用GLSL编写的处理图元生成的shader程序,它 ...
- OpenGL学习笔记(十)-几何着色器-实例化
参考网址:LearnOpenGL 中文版 4.7 几何着色器 4.7.1 基本概念 1.顶点和片段着色器之间有一个可选的几何着色器,几何着色器的输入是一个图元(如点或三角形)的一组顶点,顶点发送到下一 ...
- 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:// ...
最新文章
- 嵌入式程序员应知道的0x10个基本问题
- 【最短路】hxk化学课
- 一个最简单的UDP通信
- 排序(基本概念及分类,直接插入排序和希尔排序)
- mysql帐号不允许从远程登陆
- 电子计算机说明文作文,关于电脑说明文作文(精选3篇)
- Unix环境高级编程笔记:12、高级IO
- 【贪心 和 DP + 卖股票】LeetCode 122. Best Time to Buy and Sell Stock II
- 反思风险管理的五项核心风险。
- Typora保留文本格式
- ReactNative Ios打包流程
- fedora 11源码安装设置fcitx3.6输入法
- delphi SysErrorMessage 函数和系统错误信息表 good
- 5种经典的数据分析思维和方法
- 计算机上如何保存ico格式,PS怎么保存ico格式
- Android商城开发(一)——一次活动页需求引发的危机感
- How to edit registry via CMD command
- 在软件工程领域,搞科研的这十年!
- 苹果平板可以用html么,苹果iPad怎么用?iPad新手必备十个使用心得分享(必看)...
- win7打开远程计算机,win7系统开启远程桌面实现远程连接图文教程
热门文章
- python入门指南txt-十分钟搞定 C/C++ 项目自动化构建 —— Xmake 入门指南
- 如何系统的自学python-自学Python应该如何正确系统学习,避免少走弯路
- python工资高还是java-Python工资高还是Java?
- python软件下载安装-python安装最新下载_python安装绿色版 - 软件帝
- python工资一般多少p-Python是什么?简单了解pythonp-入门
- python有道-Python爬取有道词典
- android 4实例分析,OpenGL Shader实例分析(4)闪光效果
- php获取用户当前坐标,web端定位:获取当前地理位置
- boost.asio mysql_boost asio学习笔记
- 保留五天的日志 php,怎样让日志在归档目录保留5天?