裁剪到底在裁什么
学 WebGL 3D 的时候,很多人都会反复看到一个词:
裁剪。
教程里经常会说:
- 顶点先进入裁剪空间
- GPU 会做裁剪
- 裁剪之后再做透视除法
但很多人其实并没有真正搞明白:
裁剪到底在裁什么?
常见的困惑通常有这些:
- 是不是把跑到屏幕外的点直接删掉
- 是不是只裁顶点,不裁线和三角形
- 为什么裁剪要发生在
clip space - 为什么不是到了 NDC 或屏幕坐标再裁
- 如果一个三角形只有一半跑出去了,GPU 会怎么处理
如果这一步理解不透,前面学过的这些概念就会一直像断开的:
gl_Position- clip space
w- 透视除法
- NDC
- viewport
这篇文章就专门把"裁剪"这一步单独讲清楚。
学完这一篇,你应该能回答这些问题:
- 裁剪发生在哪个阶段
- 裁剪到底裁的是点、线、三角形,还是别的东西
- 为什么裁剪不是简单"删掉看不见的点"
- 为什么裁剪必须发生在透视除法之前
- 为什么裁剪空间会叫"裁剪空间"
一、先给最短答案
如果你只想先记住最核心的一句话,可以先记:
裁剪裁掉的不是"某个随便越界的像素",而是那些落在可见体之外的图元部分。GPU 会在裁剪空间里判断图元哪些部分还能进入后续流程,哪些部分必须被丢掉或切掉。
再压缩成一句更好记的话,就是:
text
裁剪裁的是"图元中超出可见范围的部分"。
注意这里最关键的不是"顶点"两个字,而是:
text
图元
也就是:
- 点
- 线
- 三角形
这些真正参与光栅化的几何单元。
二、先把裁剪放回完整管线里
前面的专题里你已经反复看过这条链:(这篇文章)
text
相机坐标 -> 裁剪空间 -> 透视除法 -> NDC -> viewport -> 屏幕像素
现在把裁剪插进去,就会更完整一点:
text
相机坐标
-> 投影矩阵
-> 裁剪空间
-> 裁剪
-> 透视除法
-> NDC
-> viewport
-> 屏幕像素
这说明:
- 裁剪不是最后才发生的
- 它也不是屏幕阶段的小修小补
- 它发生在非常靠前的位置
而且这个位置不是随便选的,而是有严格原因。
三、为什么这一步叫"裁剪空间"
这个名字本身就已经在提示答案了。
clip space 之所以叫:
text
裁剪空间
不是因为它"名字听起来专业",而是因为:
GPU 就是在这一层空间里判断图元哪些部分还应该被保留。
换句话说:
- 顶点着色器输出
gl_Position gl_Position进入裁剪空间- 然后 GPU 开始做"可见性范围判断"
所以裁剪空间不是一个抽象中转站,它本身就是为裁剪这件事准备的标准空间。
四、裁剪到底在裁什么
这是整篇文章最核心的问题。
很多人会误以为:
"裁剪是不是就是把跑到屏幕外的顶点删掉?"
这个理解不完全对。
更准确地说:
裁剪裁的是图元中超出可见体的那部分几何。
什么叫图元
WebGL 最终不是直接拿"一个个孤立数字"画图,而是拿图元画图。
比如:
POINTSLINESTRIANGLES
这些才是 GPU 真正处理的绘制单位。
所以裁剪关注的不是"某个顶点单独死活",而是:
一个点、一道线、一个三角形,是否完全在可见范围内,或者只有一部分在可见范围内。
五、点、线、三角形在裁剪时有什么区别
这是很适合理解裁剪的一步。
1. 点
如果一个点完全在可见范围外,那它通常就直接被丢掉。
因为点这种图元非常简单,没有"被切开还剩下一半"的说法。
2. 线
如果一条线有一部分在可见范围内,一部分在外面,那 GPU 不会简单把整条线全删掉。
它通常会:
- 保留在可见范围里的那一段
- 把超出去的那部分切掉
也就是说,线是可以被"截断"的。
3. 三角形
如果一个三角形只有一部分在可见范围内,也不会直接整块消失。
GPU 会做更复杂的处理:
- 把超出视锥体的部分裁掉
- 保留仍在可见范围里的那部分区域
有时候一个原始三角形甚至会因为裁剪而变成新的多个三角形片段,再继续进入后面的光栅化流程。
所以:
裁剪不是只会"删东西",很多时候它也会"重建可见部分"。
六、为什么裁剪不是简单删除顶点
这点非常重要。
假设一个三角形有 3 个顶点:
- 2 个在可见范围内
- 1 个在可见范围外
如果 GPU 只是把那个外面的顶点简单删掉,会怎样?
答案是:
- 这个三角形的几何结构会直接坏掉
- 你没法正确得到剩余可见区域
所以 GPU 不能只是"删顶点",而必须:
按照几何边界重新求交,得到图元仍然可见的部分。
这就是为什么裁剪是一个几何过程,而不是一个简单的"数组过滤"。
七、可见范围到底是什么
说到这里,必须再把"裁剪到底以什么为边界"讲清楚。
在裁剪空间里,GPU 判断的不是:
- DOM 像素边界
- 浏览器窗口边界
- CSS 布局边界
而是:
视锥体对应的裁剪边界。
用最简化的直觉理解,就是:
- 能进入最终可见投影范围的部分保留
- 超出这个范围的部分裁掉
你可以把它想成一个 3D 摄像机前方的"可见盒子/可见体"。
只有落在这个合理范围里的图元部分,才值得继续进入后面的透视除法和屏幕映射。
八、为什么裁剪必须发生在透视除法之前
这是理解裁剪的第二个核心点。
很多人会自然问:
"反正后面都会到 NDC,那为什么不等除完以后再裁?"
原因有两个层面。
第一层:裁剪空间就是为这一步设计的
裁剪空间里的 gl_Position 是齐次结果:
text
(x_clip, y_clip, z_clip, w_clip)
GPU 可以在这一层用统一标准判断图元可见部分。
如果跳过这里,直接到 NDC 才处理,会把前面精心设计的裁剪语义绕掉。
第二层:透视除法会改变数值关系
透视除法会做:
text
x / w
y / w
z / w
这一步本身已经引入了深度相关的缩放。
所以从图形管线设计上,更合理的顺序是:
- 先在齐次裁剪空间里判断和切割图元
- 再把结果送去做透视除法
这样流程更稳定,几何意义也更一致。
九、为什么如果一个三角形只露出一角,它不会整个消失
这个现象特别适合帮助你理解裁剪。
假设一个大三角形有大半都跑出可见范围了,只剩一个角还在镜头里。
如果 GPU 的逻辑是:
- 只要有顶点越界就整块删掉
那这个三角形本来应该还能看见的一角,也会消失。
但真实情况不是这样。
GPU 会尽量保留:
仍然落在可见范围内的那部分几何。
于是你在屏幕上看到的,往往是:
- 三角形被"切掉了一部分"
- 而不是整个突然消失
这正是裁剪的意义所在。
十、为什么裁剪发生得这么早
你可以从效率角度再理解一次。
如果一个图元根本不可能出现在屏幕里,那 GPU 最理想的做法当然是:
- 尽早丢掉它
而不是等它继续经过:
- 透视除法
- 视口映射
- 光栅化
- 片元着色
以后再发现"哦,原来根本看不见"。
所以:
裁剪本质上也是一次非常重要的早期过滤。
它既保证几何正确,又避免后面为不可见内容浪费太多处理资源。
十一、裁剪和 culling 是一回事吗
这也是特别容易混的一个点。
很多人会把:
- 裁剪
- 剔除
混成一回事。
其实它们不完全一样。
裁剪 clipping
更强调:
- 超出可见体的图元部分被切掉
- 保留仍然在范围内的部分
剔除 culling
更强调:
- 某些图元直接整块不参与后续处理
比如:
- 背面剔除
- 视锥剔除
所以你可以简单理解为:
裁剪更像"切",剔除更像"整块不要"。
这两者相关,但不完全等价。
十二、把它放回 gl_Position 再理解一次
你现在再回头看:
glsl
gl_Position = ...
就应该知道:
- 这不是最终像素
- 这不是 NDC
- 这也不是"直接就拿来光栅化"的最终结果
它更像是:
一个等待被裁剪、等待被透视除法、等待继续进入后续管线的标准输入。
所以 gl_Position 之所以要先去裁剪空间,不只是为了名字完整,而是因为:
后面的裁剪逻辑正是围绕它展开的。
十三、一个特别适合理解的例子
假设你绘制一个非常大的三角形,它横跨整个相机可见范围,有一部分跑出去了。
这时候 GPU 不会做两件极端的事:
极端 1
直接整块删除。
这样会把本来应该看见的部分一起丢掉。
极端 2
直接完全不管,让它带着超范围部分进入后续所有流程。
这样会让后面处理变得不规范,也浪费资源。
它真正做的是:
正常做法
- 判断图元和可见体的关系
- 求出仍然在范围内的那一部分
- 只把那一部分继续送往后续阶段
这就是裁剪的几何本质。
十四、从职责边界再压一遍重点
如果你想彻底不混,可以按职责来记:
投影矩阵负责什么
- 把点送进裁剪空间
- 给 GPU 提供可裁剪的齐次结果
裁剪负责什么
- 判断图元哪些部分还能保留
- 切掉超出可见体的部分
透视除法负责什么
- 把裁剪空间结果变成 NDC
viewport 负责什么
- 把 NDC 映射到真实屏幕像素区域
这样一来你就会发现:
裁剪正好卡在投影之后、透视除法之前。
这个位置一点都不随意。