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

发布于:2025-05-19 ⋅ 阅读:(22) ⋅ 点赞:(0)

几何着色器

以下是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);

网站公告

今日签到

点亮在社区的每一天
去签到