当前位置: 首页 > news >正文

计算机图形学编程(使用OpenGL和C++)(第2版)学习笔记 13.几何着色器(一)修改顶点

几何着色器

以下是OpenGL图像管线的主要阶段:

几何着色器(Geometry Shader)

几何着色器是OpenGL管线中的一个可选阶段,位于顶点着色器和片段着色器之间。它能够动态地生成或修改图元(primitives)。

主要特点

  1. 图元操作能力

    • 可以创建新的顶点
    • 可以修改现有顶点
    • 可以删除顶点
    • 可以改变图元类型
  2. 输入/输出图元类型

    • 输入:points、lines、triangles等
    • 输出:points、line_strip、triangle_strip等

基本语法

#version 430// 定义输入图元类型
layout (triangles) in;// 定义输出图元类型和最大顶点数
layout (triangle_strip, max_vertices = 3) out;// 输入变量(必须声明为数组)
in vec3 varyingNormal[];    
in vec3 varyingColor[];     // 输出变量
out vec3 gNormal;
out vec3 gColor;void main() {// 处理每个顶点for(int i = 0; i < 3; i++) {// 发射一个顶点gl_Position = gl_in[i].gl_Position;gNormal = varyingNormal[i];gColor = varyingColor[i];EmitVertex();}// 结束图元EndPrimitive();
}

常见应用

  1. 几何细分

    • 增加模型细节
    • 动态LOD(Level of Detail)
    • 曲面细分
  2. 粒子效果

    • 粒子系统
    • 特效生成
    • 动态粒子发射
  3. 阴影体生成

    • 体积阴影
    • 轮廓线提取
    • 阴影体挤出
  4. 实例化渲染

    • 批量复制几何体
    • 程序化生成几何体
    • 植被渲染

性能考虑

  • 几何着色器会增加GPU负载
  • 应该限制输出顶点的数量
  • 复杂的几何操作可能影响性能
  • 建议在必要时才使用几何着色器

修改顶点

这是我们在前面的例子中渲染出来的图形

如果我们想要将其充气膨胀,我们可以使用几何着色器来修改顶点。
即将顶点沿法线方向移动一定的距离。这样外面的顶点向外膨胀,里面的顶点向内收缩。


上图是做出来的效果,有些细节没有处理好,但是基本效果已经出来了。

几何着色器中图元输入类型的选项有:

输入布局限定符描述顶点数典型用途
points点图元1粒子效果
lines独立线段2线条渲染
lines_adjacency带邻接信息的线段4轮廓检测
line_strip线带2连续线段
line_strip_adjacency带邻接信息的线带4曲线平滑
triangles独立三角形3基本3D渲染
triangles_adjacency带邻接信息的三角形6边缘检测
triangle_strip三角形带3连续曲面
triangle_strip_adjacency带邻接信息的三角形带6细分曲面

重点

  1. 几何着色器中,输入图元类型和输出图元类型必须相同
  2. 需要使用layout限定符来指定输入和输出图元类型
  3. 需要使用EmitVertex()函数来发射顶点
  4. 需要使用EndPrimitive()函数来结束图元
  5. 从顶点着色器接收的输入变量必须声明为数组
  6. 从顶点着色器中输出的gl_Position值不能有投影矩阵,投影矩阵会在几何着色器中应用

gl_in 内置变量说明

gl_in是几何着色器中的一个内置变量数组,用于访问来自顶点着色器的内置变量值。

主要特点
  1. 数据结构
in gl_PerVertex {vec4 gl_Position;float gl_PointSize;float gl_ClipDistance[];
} gl_in[];
  1. 数组大小
  • 数组大小取决于输入图元类型:
    • points: 1个顶点
    • lines: 2个顶点
    • triangles: 3个顶点
    • lines_adjacency: 4个顶点
    • triangles_adjacency: 6个顶点
  1. 常用成员
  • gl_in[i].gl_Position: 顶点位置
  • gl_in[i].gl_PointSize: 点精灵大小(仅用于点图元)
  • gl_in[i].gl_ClipDistance[]: 裁剪距离数组
使用示例
// 在几何着色器中访问顶点位置
void main() {for(int i = 0; i < 3; i++) {// 获取顶点位置vec4 position = gl_in[i].gl_Position;// 对位置进行处理position = position + vec4(offset, 0.0);// 设置新的顶点位置gl_Position = position;EmitVertex();}EndPrimitive();
}
注意事项
  1. gl_in数组的大小是固定的,由输入图元类型决定
  2. 只能在几何着色器中访问
  3. 是只读变量,不能修改其值

修改顶点的几何着色器代码

#version 430// 定义输入图元类型为三角形
layout (triangles) in;// 从顶点着色器接收的输入变量(必须声明为数组)
in vec3 varyingNormal[];    // 法线向量数组
in vec3 varyingLightDir[];  // 光照方向数组
in vec3 varyingVertPos[];   // 顶点位置数组// 传递给片段着色器的输出变量
out vec3 gNormal;    // 法线向量
out vec3 gLightDir;  // 光照方向
out vec3 gVertPos;   // 顶点位置// uniform变量声明
uniform mat4 proj_matrix;  // 投影矩阵
uniform mat4 norm_matrix;  // 法线变换矩阵// 定义输出图元类型为三角形带,每个图元最多输出3个顶点
layout (triangle_strip, max_vertices = 3) out;void main(void)
{// 处理三角形的每个顶点for (int i = 0; i < 3; i++){// 计算膨胀效果vec3 normal = normalize(varyingNormal[i]);  // 归一化法线向量// 将顶点沿法线方向移动(膨胀效果)// 注释掉的是带投影矩阵的版本//gl_Position = proj_matrix * gl_in[i].gl_Position + normalize(vec4(normal, 1.0));gl_Position = gl_in[i].gl_Position + normalize(vec4(normal, 1.0)) * 0.5;// 将变量传递给片段着色器gNormal = varyingNormal[i];      // 传递法线gLightDir = varyingLightDir[i];  // 传递光照方向gVertPos = varyingVertPos[i];    // 传递顶点位置// 发射顶点EmitVertex();}// 结束当前图元的构建EndPrimitive();
}

上述代码中,几何着色器将每个顶点沿法线方向移动0.5个单位,从而实现膨胀效果。
但该代码有一个问题,就是如果gl_Position已经是在投影空间后,是不能直接去进行修改顶点的,我们需要在摄像机空间中来修改顶点,然后再使用投影矩阵
以下是修改后的代码的效果图:

代码修改

顶点着色器中修改

顶点着色器中,我们使用模型视图矩阵来计算顶点位置

    // 计算裁剪空间中的顶点位置 ,由于要在几何着色器中进行膨胀处理,所以这里不使用投影矩阵//gl_Position = proj_matrix * mv_matrix * vec4(vertPos,1.0);gl_Position =  mv_matrix * vec4(vertPos,1.0);
几何着色器中修改

最后,我们需要在几何着色器中修改gl_Position,将顶点位置乘以投影矩阵,

    // 计算膨胀效果gl_Position =proj_matrix*( gl_in[i].gl_Position + normalize(vec4(normal, 1.0)) * 0.5);

相关文章:

  • React Flow 边事件处理实战:鼠标事件、键盘操作及连接规则设置(附完整代码)
  • 学习黑客HTTP(HyperText Transfer Protoco)
  • 从代码学习深度学习 - 近似训练 PyTorch版
  • [强化学习的数学原理—赵世钰老师]学习笔记02-贝尔曼方程-下
  • 【AWS】从 0 基础直觉性地理解 IAM(Identity and Access Management)
  • CudaMemCpy returns cudaErrorInvalidValue
  • 《Vite 报错》ReferenceError: module is not defined in ES module scope
  • 学习黑客Active Directory入门
  • 重读《人件》Peopleware -(10-2)Ⅱ 办公环境 Ⅲ 节省办公空间的费用(下)
  • 多头自注意力机制—Transformer模型的并行特征捕获引擎
  • 打卡Day29
  • Vue百日学习计划Day24-28天详细计划-Gemini版
  • C++中的容器
  • Spring Boot JWT认证示例项目
  • 怎样免费开发部署自己的网站?
  • react深入2 - react-redux
  • MySQL——6、内置函数
  • 2025年- H31-Lc139- 242.回文链表(快慢指针)---java版--需2刷
  • c++编写中遇见的错误
  • 如何利用DeepSeek提升工作效率
  • 中国证监会副主席李明:目前A股估值水平仍处于相对低位
  • 爬坡难下坡险,居民出行难题如何解?
  • 南昌上饶领导干部任前公示:2人拟提名为县(市、区)长候选人
  • 上海率先推进生物制品分段生产试点,这款国产1类创新药获批上市
  • 《上海市建筑信息模型技术应用指南(2025版)》发布
  • 严打金融黑灰产,今年来上海警方破获各类经济犯罪案件690余起