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

前言
本篇在讲什么

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

♠ 结语

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

👉 本文属于原创文章,转载请评论留言,并在转载文章头部著名作者出处👈
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: OpenGL蓝宝书PDF是指《OpenGL超级宝典》一书的电子版本,主要介绍了OpenGL图形学编程的基础知识和开发技巧。 OpenGL是一种跨平台的图形库,可以用于开发高性能的2D和3D图形应用程序。《OpenGL超级宝典》是一本经典的OpenGL教材,适合初学者和有一定OpenGL基础的开发者学习和参考。 这本书的PDF版本提供了一种便捷的阅读方式,可以随时随地通过电脑、平板或手机进行学习。相比于传统的纸质书籍,PDF版本的《OpenGL超级宝典》具有以下优势: 1. 可随时复制和搜索:PDF格式的书籍可以方便地进行复制和搜索,使得我们可以快速找到我们需要的内容,并方便地进行引用和参考。 2. 纸质书籍的替代品:PDF版本的书籍不占用实体空间,而且可以通过电子设备随时携带,方便在任何时间、任何地点进行学习和阅读。 3. 交互性强:PDF格式的书籍还可以添加书签、注释和标记,方便读者进行个性化的标记和笔记,更好地帮助记忆和理解。 总之,《OpenGL超级宝典》PDF版本是一种便捷、高效的学习OpenGL图形学编程的工具,通过这本书,读者可以系统地学习OpenGL的基础知识和开发技巧,从而提高自己在图形学编程领域的能力。 ### 回答2: OpenGL蓝宝书是一本关于OpenGL编程的经典教材,适合初学者和有一定编程基础的开发者。本书全面介绍了OpenGL的基础知识和常用编程技巧,并提供了大量的代码示例和实践项目。 这本书的PDF版本提供了方便的电子阅读方式,读者可以随时随地学习和实践OpenGL编程。使用PDF格式的优点是可以根据需要进行搜索、标注、复制和打印,在学习过程中方便查阅和注释,提高学习效率。 《OpenGL蓝宝书》的内容涵盖了OpenGL的基础知识,包括顶点和片元着色器、图元绘制、纹理映射、新的OpenGL特性等。此外,该书还介绍了OpenGL的高级技术,如光照、阴影、透明度、几何着色器等,帮助读者掌握更复杂的图形渲染技术。 这本书对于学习OpenGL编程的人来说是一部非常有价值的参考资料。它通过清晰的逻辑结构和易于理解的语言,帮助读者理解和掌握OpenGL的核心概念和编程技巧。同时,书中提供的示例代码和实践项目可以帮助读者巩固所学知识,并进一步探索OpenGL的应用。 总的来说,《OpenGL蓝宝书》的PDF版本是一种方便快捷的学习OpenGL编程的方式,可以满足读者在不同场合和需求下的学习和实践需求。无论是初学者还是有一定经验的开发者,都可以从中获得宝贵的知识和经验。 ### 回答3: OpenGL蓝宝书是一本经典的OpenGL编程指南,全书详尽地介绍了OpenGL的基础知识和编程技巧。这本书的完整版可以在网上找到PDF格式的电子书。通过阅读OpenGL蓝宝书,我们可以了解到OpenGL的底层原理、渲染管线以及各种常用的绘图和渲染技术。 OpenGL是一种跨平台的图形编程接口,它可以用于开发2D和3D图形应用程序。蓝宝书从基础概念开始,逐步介绍OpenGL的各个方面,包括顶点缓冲对象、着色器、纹理映射、光照等。通过例子和代码实践,读者可以深入理解OpenGL的工作原理,并学会如何使用OpenGL进行图形渲染。 OpenGL蓝宝书的PDF版本提供了便捷的学习方式。电子版本便于阅读和搜索,可以随时随地进行学习。同时,通过电子书的书签和目录功能,读者可以方便地定位到自己感兴趣的章节和内容。此外,电子书的PDF格式可以在多个设备上使用,如电脑、平板电脑和手机等。 总之,OpenGL蓝宝书PDF是一本非常有价值的OpenGL学习资料,它为初学者提供了一个系统而又详细的学习路径,帮助读者深入理解OpenGL的基础知识和编程技巧。无论是对于想要学习图形编程的人来说,还是对于已经对OpenGL有一定了解的开发者来说,这本书都是一本非常值得阅读的指南。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值