flowchart LR
classDef importentNode fill:#e81224,stroke:#999,stroke-width:1px,color:#eff1f5;
subgraph A[几何处理]
direction TD
A1[输入装配]
A2[顶点着色器]:::importentNode
A4[<图元装配><br>顶点组成:点线,三角形]
A5[剪裁]
A6[<透视除法><br>也就是:xyz ÷ w 得到 NDC ]
A7[<视口变换><br>将 NDC 坐标映射到屏幕]
A8[正反面判定与剔除]
end
subgraph A3[可选阶段]
direction TD
A3_1[几何着色器]
A3_2[曲面细分]
A3_3[流式输出]
end
A1-->A2-->A3-->A4-->A5-->A6-->A7-->A8
A2-->|可以不选|A4
🔴3D 模型文件(GLB/FBX/OBJ 这类)里面存啥
- 可以先了解模型文件(fbx)存了哪些信息,因为顶点着色器就是要处理这些信息
- 文件里最重要的部分,也是最基础的信息就是网格体信息,其中包括:
- 顶点:就是以锚点为中心排布的顶点的位置(x,y,z)
- 面: 告诉GPU 哪些顶点连成一个三角形
- 顶点法线:每个顶点朝向哪里
- UV:每个顶点的 2D 贴图坐标
[!tip] obj 格式是模型文件最基础的格式,存的就是上面那些
- 材质 + 贴图
- 材质参数:金属度、粗糙度、基础色、自发光、透明度等。
- 贴图引用:法线贴图、金属度贴图等。
[!tip] GLB 文件能直接把图片打包到一个文件里,OBJ 会以外附件的形式跟在同级目录下
- 骨骼信息 + 蒙皮 + 权重 + 动画信息
- 骨骼:每根骨骼的名字、骨骼本地初始 T-pose 的位置 / 旋转 / 缩放、
- 骨骼之间父子关系
- 骨骼的逆绑定矩阵(Inverse Bind Matrix) 骨头回到 Tpose 的 “还原矩阵”。运行时计算顶点的时候要用,把顶点从原始模型空间转到骨骼局部空间。
- 蒙皮:每个顶点受哪几根骨头影响
- 权重:每根骨骼对这个顶点的权重值(0~1),所有骨骼的权重加起来 = 1。
- 骨骼动画信息:
- 每根骨骼在该时间轴上的:本地位移、旋转、缩放
- 插值方式:两个关键帧之间用线性 / 四元数插值平滑过渡。
- 变形动画信息:
- 和骨骼动画完全不一样:直接存顶点的偏移量,不是骨头。
- 基准模型(默认 pose ):原始顶点
- 多个形态目标(每个形态就是一个变形版本)
- 形态权重(混合系数)
- 0~1 之间的数值。0 = 完全不启用这个形态;1 = 完全应用这个顶点偏移。多个形态可以叠加混合:同时张嘴 + 挑眉。
- 元数据:形态名字(张嘴、闭眼、微笑),有的文件存最小最大权重。
[!note] 骨骼动画 和 变形动画 的区别
变形动画:骨头不动,直接修改顶点位置。适合面部表情这类精细、局部形变,骨骼很难做到。
骨骼动画:动画只存骨头怎么动;顶点靠权重跟着骨头被拽动。顶点本身原始坐标不会变,只是被骨骼矩阵带动。
-
场景层级(节点树)
- 一个文件可以装多个模型,记录父子关系(并且记下导出时每个物体相对于父物体的初始摆放)
-
杂项元数据
- 单位(米 / 厘米)、坐标轴方向(Y 向上还是 Z 向上)、LOD 信息、自定义属性
🟠GPU 中的顶点缓冲区
[!error] 顶点缓冲区,简称 VBO,就是GPU 显存里的一块内存区域,CPU 会把所有顶点打包成一大串数组,送到 GPU 显存,专门用来存放一堆顶点数据。
- 顶点缓冲区会存什么?
- 每个顶点 一整套信息:
模型空间坐标(x,y,z)、UV、法线、切线、骨骼索引、骨骼权重值 等
- 每个顶点 一整套信息:
🔴顶点着色器 可以从哪里拿数据?
-
来自顶点缓冲区 VBO、获取 每个顶点的属性:如
模型空间坐标(x,y,z)、UV、法线、切线、骨骼索引、等 -
来自 Uniform 全局数据
- (同一个 DrawCall 里,所有顶点共用一份):不从顶点缓冲区拿 是 CPU 每帧打包传给 GPU 的全局变量,所有顶点都读到一模一样的值。
- 例如:模型矩阵、视图矩阵、投影矩阵(MVP)
- 骨骼矩阵数组(GPU 蒙皮用)
- 时间、光照参数、材质颜色
- (同一个 DrawCall 里,所有顶点共用一份):不从顶点缓冲区拿 是 CPU 每帧打包传给 GPU 的全局变量,所有顶点都读到一模一样的值。
-
可以来自 GPU 提供的 输入内置变量
- 输入内置变量是GPU 硬件 / 着色器语言规范自带,不用你在 VBO 或者 Uniform 里手动上传,GPU 自动生成。比如
gl_VertexID:当前正在处理的顶点序号(从 0 开始)gl_InstanceID:实例 ID,实例渲染 (Instancing) 才有用。画大量一模一样物体(树、草),同一个 DrawCall 渲染上千棵树,这个数字代表当前是第几棵实例。- 只在本次顶点着色器的计算过程里面有效。不会自动传递。
[!important] 还有一个是:输出内置变量
输出内置变量(你写代码赋值给它),不是读取输入:gl_Position:顶点着色器必须输出的裁剪坐标,是你写给 GPU,不是读进来的。
- 可以来自 纹理采样器
- 纹理数据存在 GPU 纹理显存(Texture Resource)
- 是一个
sampler采样器,属于 Uniform 类型 ,整个 DrawCall 全局共用。 - CPU 上传图片到 GPU 显存,然后把采样器作为 Uniform 传给着色器。
[!note] 总结
数据类型 来源 特点 顶点属性(position/uv/ 权重) VBO 顶点缓冲区 每个顶点不一样 Uniform(矩阵、骨骼数组、sampler 采样器) CPU 每帧上传到 GPU 同一次 DrawCall,全部顶点共用 内置输入 gl_VertexID /gl_InstanceID GPU 硬件自动生成 不需要 CPU 上传任何数据 纹理图片像素 GPU 纹理显存,通过 sampler 读取 通过 Uniform 采样器访问
🔴顶点着色器干些什么??
顶点着色器其实就是 GPU 上用于处理顶点的小程序
- 顶点着色器会做这几件事:
- 从顶点缓冲区 拿顶点属性数据
- 🔵 计算顶点最终位置
- 纯静态模型:会将顶点从 ==模型空间== 经过
世界变换-->观察变换-->投影变换最终转换到 ==剪裁空间==- 本质就是将 模型空间的顶点坐标 × 模型矩阵 × 视图矩阵 × 投影矩阵 最后输出该顶点在裁剪空间上的坐标
- 带骨骼蒙皮的模型:会去拿顶点的权重 + 骨骼索引,读取传进来的骨骼矩阵数组、混合多根骨骼的矩阵然后算出这个顶点被骨头拉动后的变形后模型空间位置
- 经过骨骼变换矩阵的运算后就换继续走
世界变换-->观察变换-->投影变换这套算法 输出该顶点在裁剪空间上的坐标
- 经过骨骼变换矩阵的运算后就换继续走
- 有变形动画的模型 拿到各个形态目标的偏移,按权重叠加到原始顶点位置上,再继续矩阵变换。
- 纯静态模型:会将顶点从 ==模型空间== 经过
- 🔵 输出数据:
- 必须输出:输出 裁剪空间顶点坐标
gl_Position比如,该顶点的检查空间坐标会给光栅化程序。 - 附属输出:输出 UV、法线、切线、顶点色这类顶点属性数据,输入内置变量数据、和采样器数据 不会自动输出。
- 必须输出:输出 裁剪空间顶点坐标
- 🔴 简单顶点动画(海浪、草、旗帜摆动)
- 可以有程序员 直接在顶点着色器里修改顶点位置: 比如根据时间,给顶点 Z 轴加正弦波动,实现风吹草动。
- 优点:不用 CPU 每帧更新顶点,GPU 直接算,性能高。
[!note] 什么是模型空间、什么是世界空间、什么是观察空间?
模型空间: 就是在 fbx、obj 中定义的以锚点(0,0,0)为模型中心,定义的空间。顶点坐标都是相对于 模型自身 的,跟放在场景哪没关系。
世界空间:所有模型在世界中都有一个统一的原点(0,0,0)
观察空间:以相机为原点,的空间。所有物体都围绕相加转。
剪裁空间:又叫投影空间,把相机 “看到的一整块视野盒子”,压缩成一个标准小方块(裁剪盒)
[!error] 模型顶点的坐标转换过程
flowchart LR classDef importentNode fill:#ec1e23,stroke:#999,stroke-width:1px,color:#eff1f5; subgraph A[变换空间] direction LR A1[模型空间] A2[世界空间] A3[观察空间] A4[剪裁空间]:::importentNode end A1-->|世界变换|A2-->|观察变换|A3-->|投影变换|A4变换的本质就是坐标向量和变换矩阵做乘法:04_几何处理阶段_00_在GLSL_HLSL中如何实现从模型空间到剪裁空间的变换
[!tip] 在 maya、blender这类软件中也有这套坐标转换流程,如预览视口中的实时预览管线
✔️ 我们可以用顶点着色器做什么?
-
坐标变换(最基础,必写)
- 把模型空间顶点,乘变换矩阵,一路转到裁剪空间,输出
gl_Position。
- 把模型空间顶点,乘变换矩阵,一路转到裁剪空间,输出
-
GPU 蒙皮动画
- 从 VBO 拿顶点的骨骼索引 + 权重;从 Uniform 读取骨骼矩阵数组。 在顶点着色器里混合骨骼矩阵,拉动顶点,实现人物走路、抬手。
-
形态形变
- 读取各个形态目标的顶点偏移,按权重叠加到原始顶点位置。
-
顶点偏移动画
- 利用时间 Uniform,直接修改顶点坐标。 例如:风吹草摆动、旗帜飘动、海面起伏、热浪扭曲、旗子、藤蔓晃动。
-
顶点级别的数据计算,输出给片元着色器
- 在顶点着色器预先算好一些值,输出,硬件自动插值送到片元着色器。
- 预先算光照方向、世界空间位置
- 预先算顶点颜色
- 在顶点着色器预先算好一些值,输出,硬件自动插值送到片元着色器。
-
顶点着色器内采样纹理(现代 GPU)
- 拿高度图 / 位移图,采样纹理,根据纹理值抬高 / 压低顶点。 典型:地形起伏、山体、简单置换。
⚠️ 注意:这是修改顶点位置,不是给像素上色。
- 实例渲染(Instancing)配合 gl_InstanceID
- 同一个网格,一次性画成千上万份(树、石头、粒子)。 用
gl_InstanceID读取当前实例编号,拿到这个实例的变换矩阵,移动顶点。
- 同一个网格,一次性画成千上万份(树、石头、粒子)。 用
❌ 顶点着色器做不到的?
- 不能增加或者删掉顶点,顶点数量由模型网格 / 索引缓冲固定死
- 不能读取其他顶点的数据,每个顶点单独跑
- 不能直接输出像素颜色,颜色是片元着色器负责
- 旧版 GLSL 不能访问贴图去采样颜色(现代可以采样,但一般不在这里采样贴图算颜色)
评论区