OpenGL蓝宝书第八章学习笔记:基元处理之几何着色器

news/2024/10/22 4:45:29/
前言
本篇在讲什么

OpenGL蓝宝书第八章学习笔记之几何着色器
本篇适合什么

适合初学OpenGL的小白
本篇需要什么

C++语法有简单认知
OpenGL有简单认知
最好是有OpenGL超级宝典蓝宝书
依赖Visual Studio编辑器

本篇的特色

具有全流程的图文教学
重实践,轻理论,快速上手
提供全流程的源码内容


★提高阅读体验★

👉 ♠ 一级标题 👈

👉 ♥ 二级标题 👈

👉 ♣ 三级标题 👈

👉 ♦ 四级标题 👈


目录

  • ♠ 几何着色器
    • ♥ 传递几何着色器
    • ♥ 在应用程序中使用几何着色器
    • ♥ 删除几何着色器中的几何
    • ♥ 在几何着色器中生成几何体
    • ♥ 修改几何着色器中的基元类型
    • ♥ 多视口转换
  • ♠ 推送
  • ♠ 结语


♠ 几何着色器

几何着色器作为可选择的可编程部分,其位于曲面细分和片段着色器之间,如果不设置几何着色器,则曲面细分的输出直接到片段着色器,以下阐述有关几何着色器的一些特点

  • 可以一次性处理整个基元(三角形、线条或点)

  • 可以通过编程方式实际改变OpenGL管线中的数据量

  • 可以访问基元中的所有顶点,可更改基元类型,甚至可以创建和销毁基元


♥ 传递几何着色器

下面是一个最简单的一个着色器例子,这种传递几何着色器将其输入直接发送给输出而不经任何修改

#version 410 core                                                                  layout (triangles) in;        
layout (triangle_strip) out;
layout (max_vertices = 3) out;                                           void main(void)                                                                    
{                                                                                  int i;                                                                         for (i = 0; i < gl_in.length(); i++)                                           {                                                                              gl_Position = gl_in[i].gl_Position;                                        EmitVertex();                                                              }   EndPrimitive();                                                                           
}      

我们简单分析一下这部分着色器代码,首先第一部分我们使用布局限定符设置输入输出类型和最大顶点数量

  • 使用布局限定符triangles作为输入
  • 使用布局限定符triangle_strip作为输出
  • 使用布局限定符max_vertices=3指定着色器应生成的最大顶点数量为三

接下来是main()函数

该着色器含一个循环,且该循环根据内置数组gl_in的长度运行很多次
gl_in是几何着色器的另一个特有变量,包含顶点着色器入的所有内置变量
gl_in[]数组的长度由输入基元模式确定。在该着色器中,三角形为输入基元模式,因此gl_in[]的尺寸为3
Emitvertex()该内置函数专对几何着色器,告知着色器我们已经完成对该顶点的操作
EndPrimitive()该内置函数告知着色器我们已经生成当前基元的各个顶点,应继续下一个基元的操作


♥ 在应用程序中使用几何着色器

下面代码创建了一个几何着色器

glCreateShader(GL_GEOMETRY_SHADER);

与其他着色器类型相同,通过调用glcreateShader()函数创建几何着色器,并使用GL_GEOMETRY_SHADER作为着色器类型

和其他着色器相同,通过调用glCompileshader()函数编译着色器,并通过调用glAttachshader()函数将其附加到程序对象。然后使用glLnkProgram()函数将程序正常链接

几何着色器接收的基元必须与其自身所预期的输入基元模式匹配

当曲面细分未激活时,绘图命令中使用的基元模式必须与几何着色器输入基元模式匹配。例如,如果几何着色器的输入基元模式为点,则可在调用glDrawArrays()时只使用GL POINTS。如果几何着色器的输入基元模式为三角形,则可在调用glDrawArrays()时使用GL_TRIANGLES、GL_TRIANGLE_STRIP或GL_TRIANGLE_FAN

几何着色器输入基元模式和可用几何类型如下表所示

几何着色器输入模式可用绘图模式
pointsGL_POINTS
linesGL_LINES,GL_LINE_LOOP,GL_LINE_STRIP
trianglesGL_TRIANGLES,GL_TRIANGLE_FAN,GL_TRIANGLE_STRIP
lines_adjacencyGL_LINES_ADJACENCY, GL_LINE_STRIP_ADJACENCY
triangles_adjacencyGL_TRIANGLES_ADJACENCY,GL_TRIANGLE_STRIP_ADJACENCY

当曲面细分被激活时,绘图命令中使用的模式应始终为GL_PATCHES,几何着色器的输入基元模式应与曲面细分基元模式匹配。输入基元类型使用布局限定符在几何着色器主体内指定。输入布局限定符的一般形式为

layout (primitive_type) in;

几何着色器预定义输入存储在名为gL_in[]的内置数组中,其结构如下所示

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

几何着色器的输入数组长度取决于所处理的基元类型,上文我们已知三角形的长度是3,下表列出了对应基元类型输入数组大小

输入基元类型输入数组大小
points1
lines2
triangles3
lines_adjacency4
triangles_adjacency6

♥ 删除几何着色器中的几何

如果正在运行几何着色器,但从未针对该特定基元调EmitVertex(),则不会绘制任何东西。因此,我们可以实现一种剔除几何图形的自定义背面剔除程序,在蓝宝书中提供了详细示例gsculling以供参考,我们这里截取示例中的几何着色器的main代码

void main(void)                                                         
{                                                                       int n;                                                              vec3 ab = gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz;      vec3 ac = gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz;      vec3 normal = normalize(cross(ab, ac));                             vec3 transformed_normal = (mat3(mvMatrix) * normal);                vec4 worldspace = /* mvMatrix * */ gl_in[0].gl_Position;            vec3 vt = normalize(viewpoint - worldspace.xyz);                    if (dot(normal, vt) > 0.0) {                                        for (n = 0; n < 3; n++) {                                       gl_Position = mvpMatrix * gl_in[n].gl_Position;             color = vertex[n].color;                                    EmitVertex();                                               }                                                               EndPrimitive();                                                 }                                                                   
}                                                                       

注意在满足(dot(normal, vt) > 0.0)条件下EmitVertex()EndPrimitive()才会被调用,运行效果如下图所示

在这里插入图片描述

如果去除掉限制条件,则不会有剔除效果,运行效果如下图所示

在这里插入图片描述


♥ 在几何着色器中生成几何体

可以根据需要多次调用 EmitVertex()和 EndPrimitive()以生成新几何体,以下代码摘自蓝宝书示例gstessellate

#version 410 core                                                             layout (triangles) in;                                                        
layout (triangle_strip, max_vertices = 12) out;                                uniform float stretch = 0.7;                                                  flat out vec4 color;                                                          uniform mat4 mvpMatrix;                                                       
uniform mat4 mvMatrix;                                                        void make_face(vec3 a, vec3 b, vec3 c)                                        
{                                                                             vec3 face_normal = normalize(cross(c - a, c - b));                        vec4 face_color = vec4(1.0, 0.4, 0.7, 1.0) * (mat3(mvMatrix) * face_normal).z;  gl_Position = mvpMatrix * vec4(a, 1.0);                                   color = face_color;                                                       EmitVertex();                                                             gl_Position = mvpMatrix * vec4(b, 1.0);                                   color = face_color;                                                       EmitVertex();                                                             gl_Position = mvpMatrix * vec4(c, 1.0);                                   color = face_color;                                                       EmitVertex();                                                             EndPrimitive();                                                           
}                                                                             void main(void)                                                               
{                                                                             int n;                                                                    vec3 a = gl_in[0].gl_Position.xyz;                                        vec3 b = gl_in[1].gl_Position.xyz;                                        vec3 c = gl_in[2].gl_Position.xyz;                                        vec3 d = (a + b) * stretch;                                               vec3 e = (b + c) * stretch;                                               vec3 f = (c + a) * stretch;                                               a *= (2.0 - stretch);                                                     b *= (2.0 - stretch);                                                     c *= (2.0 - stretch);                                                     make_face(a, d, f);                                                       make_face(d, b, e);                                                       make_face(e, c, f);                                                       make_face(d, e, f);                                                       EndPrimitive();                                                           
}     

我们可以从中看出我们通过一个矩阵变换mvpMatrix重新设置了每个顶点的xyz,还给每个顶点重新设置了颜色face_color,执行效果如下图

在这里插入图片描述


♥ 修改几何着色器中的基元类型

下面示例中我们将把几何类型从三角形改为线,该示例节选字蓝宝书示例normalviewer

static const char * gs_source[] =
{"#version 410 core                                                      \n""                                                                       \n""layout (triangles) in;                                                 \n""layout (line_strip, max_vertices = 4) out;                             \n""                                                                       \n""uniform mat4 mv_matrix;                                                \n""uniform mat4 proj_matrix;                                              \n""                                                                       \n""in VS_OUT                                                              \n""{                                                                      \n""    vec3 normal;                                                       \n""    vec4 color;                                                        \n""} gs_in[];                                                             \n""                                                                       \n""out GS_OUT                                                             \n""{                                                                      \n""    vec3 normal;                                                       \n""    vec4 color;                                                        \n""} gs_out;                                                              \n""                                                                       \n""uniform float normal_length = 0.2;                                     \n""                                                                       \n""void main(void)                                                        \n""{                                                                      \n""    mat4 mvp = proj_matrix * mv_matrix;                                \n""    vec3 ab = gl_in[1].gl_Position.xyz - gl_in[0].gl_Position.xyz;     \n""    vec3 ac = gl_in[2].gl_Position.xyz - gl_in[0].gl_Position.xyz;     \n""    vec3 face_normal = normalize(cross(ab, ac));                      \n""                                                                       \n""    vec4 tri_centroid = (gl_in[0].gl_Position +                        \n""                         gl_in[1].gl_Position +                        \n""                         gl_in[2].gl_Position) / 3.0;                  \n""                                                                       \n""    gl_Position = mvp * tri_centroid;                                  \n""    gs_out.normal = gs_in[0].normal;                                   \n""    gs_out.color = gs_in[0].color;                                     \n""    EmitVertex();                                                      \n""                                                                       \n""    gl_Position = mvp * (tri_centroid +                                \n""                         vec4(face_normal * normal_length, 0.0));      \n""    gs_out.normal = gs_in[0].normal;                                   \n""    gs_out.color = gs_in[0].color;                                     \n""    EmitVertex();                                                      \n""    EndPrimitive();                                                    \n""                                                                       \n""    gl_Position = mvp * gl_in[0].gl_Position;                          \n""    gs_out.normal = gs_in[0].normal;                                   \n""    gs_out.color = gs_in[0].color;                                     \n""    EmitVertex();                                                      \n""                                                                       \n""    gl_Position = mvp * (gl_in[0].gl_Position +                        \n""                         vec4(gs_in[0].normal * normal_length, 0.0));  \n""    gs_out.normal = gs_in[0].normal;                                   \n""    gs_out.color = gs_in[0].color;                                     \n""    EmitVertex();                                                      \n""    EndPrimitive();                                                    \n""}                                                                      \n"
};

最终展示效果如下图所示

在这里插入图片描述


♥ 多视口转换

OpenGL有助于你同时使用多个视口,这是一种称为视口数组的功能

为使用视口数组,首先需要指定想要使用的视口边界。为此,可以调用glviewportIndexedf()glViewportIndexedfv()

void glviewportIndexedf(GLuint index, GLfloat x, GLfloat y, GLfloat w, GLfloat h);
void glviewportIndexedfv(GLuint index, const GLfloatW * v);void glDepthRangeIndexed(GLuint index, GLdouble n, GLdouble f);

index是要修改的视口索引。注意,有索引的视口命令的视口参数为浮点值,而不是 glviewport()所用的整数
OpenGL至少支持 16 个视口,因此 index 的范围为0~15
每个视口都有自己的深度范围,可通过调用 glDepthRangeIndexed()指定,index的值可能为0~15

如果要一次性设置多个视口,则可以考虑使用glviewportArrayv()和gDepthRangeArrayv()

void glViewportArrayv(GLuint first, GLsizei count, const GLfloat * v);
void glDepthRangeArrayv(GLuint first, GLsizei count, const GLdouble * v);

一旦指定视口,你需要将几何图形定向到这些视口。这可以通过使用几何着色器完成。写入内置变量gl_viewportIndex 选择要染到哪个视口


♠ 推送

  • Github
https://github.com/KingSun5

♠ 结语

若是觉得博主的文章写的不错,不妨关注一下博主,点赞一下博文,另博主能力有限,若文中有出现什么错误的地方,欢迎各位评论指摘。

👉 本文属于原创文章,转载请评论留言,并在转载文章头部著名作者出处👈

http://www.ppmy.cn/news/152377.html

相关文章

关于vivo手机安装失败的解决办法

真的不想吐槽vivo手机了 连接真机测试安装APP失败报如下错误 Installation did not succeed. The application could not be installed: INSTALL_FAILED_TEST_ONLY Installation failed due to: null Retry 解决办法如下 打开gradle.properties这个文件&#xff0c;添加如下…

vivoy85a参数配置_vivo Y85手机详细配置参数和图鉴

最新的vivo新品发布会上了,最新发布了一款千元手机vivo y58,这款手机怎么样呢?下面就让小编来大家看下这款手机的详细配置参数和图赏吧。 vivo Y85 废话不多说,直接上vivo Y85配置参数,包含了价格与各硬件,如下参数表所示。 vivo Y85配置参数 屏幕规格 6.26英寸2280x1080…

vivo手机刷鸿蒙教程,vivo手机怎么刷机_vivo手机刷机全教程

现在全中国的人用vivo手机的应该有一百个人里面有十几个,那么vivo手机怎么进行刷机呢,这样就可以安装自己喜欢的系统,或者自己的手机密码忘了还可以通过刷机打开。 1.最直接简单的办法 手机关机,按关机键和音量上键,双清手机 去vivo官网下载对应机型的安装包,存在手机根目…

vivo手机哪款性价比高质量好 2022

iQOOZ5是我用过的体验中最舒服的千元机&#xff0c;绝对值得爆炸的神机。首先Z5采用骁龙778G处理器&#xff0c;778G处理器不仅性能强&#xff0c;而且耗电也非常无敌。所有778G模型的续航表现都很好&#xff0c;例如小米Civi、荣光50、GT母版等&#xff0c;iQOZ5的电池容量达到…

什么是 TypeScript ? 快速上手 TypeScript

什么是 TypeScript &#xff1f; TypeScript是一种由微软开发的开源编程语言&#xff0c;它是JavaScript语言的一个超集&#xff0c;支持JavaScript中的所有语法和特性&#xff0c;并在此基础上添加了一些新的特性。 TypeScript与JavaScript最大的不同是它引入了静态类型检查机…

vivo手机计算机恢复出厂设置,vivo手机如何恢复出厂设置

使用智能手机的用户都知道&#xff0c;就像电脑一样&#xff0c;手机使用一段时间后&#xff0c;就会不如一开始那么流畅好用&#xff0c;打开程序也有点卡&#xff0c;而解决诸如此类问题的办法呢&#xff0c;在手机上就是恢复出厂设置。 每个手机都自带有回复出厂设置的功能&…

「JavaScript DOM编程必备」掌握前端开发交互技能的必备秘籍

什么是 DOM 事件? 在 HTML 或 XML 文档中,当某个操作被触发时所产生的事件称为 DOM 事件。这些事件可以来自用户的交互操作,比如鼠标点击、滚动、键盘输入等;也可以来自浏览器的内部操作,比如页面加载、DOM 结构变化等。 如何监听 DOM 事件? 可以使用 JavaScript 中的 a…

DES,RAS,HASH

是猫咪&#xff0c;我加入了一些猫咪 1.DES Data Encryption Standard&#xff0c;即数据加密标准&#xff0c;是一种使用密钥加密的块算法。设计中使用了分组密码设计的两个原则&#xff1a;混淆&#xff08;confusion&#xff09;和扩散(diffusion)。DES加密算法原理简析_51…