news 2026/5/26 8:36:25

3D渲染中的六个裁剪平面:摄像机视野的秘密

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
3D渲染中的六个裁剪平面:摄像机视野的秘密

先把核心结论用最直白的话说出来:

裁剪的六个平面,其实就是:
给摄像机前面那一块“能看到的空间”做了一个看不见的长方体外壳,
这个壳有六个面:左、右、上、下、近、远。
凡是跑到这六个面外面的东西——统统不画;
被压在这六个面之间的那一块空间,就是 GPU 真正要渲染的“视野空间”。

这六个平面就是:

  • 左平面(Left Plane)
  • 右平面(Right Plane)
  • 上平面(Top Plane)
  • 下平面(Bottom Plane)
  • 近平面(Near Plane)
  • 远平面(Far Plane)

它们一起围出一个“空间盒子”:
在透视投影时是个截掉尖端的锥体(视锥体)
在投影/变换之后,经常会变成一个规整的盒子(剪裁空间/NDC 里的立方体)。

这篇我们用非常大白话、带点数学但不吓人的方式,
把这六个平面讲清楚,目标是:

  • 你脑子里能有一个立体画面:摄像机 + 六个平面 + 视锥体;
  • 知道为什么必须有这六个平面,而不是三五个;
  • 知道这些平面跟FOV / near / far / aspect / 投影矩阵 / Clip Space / NDC有什么关系;
  • 大致明白“一个点/一个三角形是怎么被判定在不在视野里的”;

尽量用生活比喻说明白,公式只在需要时上,保证能看懂。


一、六个平面到底是“啥”?先把画面感建立起来

1.1 想象你拿着一个手电筒

手电筒前面发出一束锥形的光:
中间亮、两边越来越暗,边缘之外是黑的。

我们只关心“亮光照到的那一块空间”,
这块空间大致像一个锥体:

  • 离你太近:光没发散开,甚至还没出筒口,可以看作是“不可见”;
  • 离你太远:光也照不到;
  • 左右、上下都有一个“边界角度”,再往外就出光束了。

如果我们用六块透明玻璃,把这束光“包起来”,
就得到了六个平面:

  • 左边一块玻璃:左边界;
  • 右边一块:右边界;
  • 上下各一块:上边界、下边界;
  • 离手电筒很近的那块:近平面;
  • 离手电筒比较远的那块:远平面。

玻璃外面的世界统统不管,
玻璃里面那一块,就是“视野空间”。

1.2 摄像机视锥体 = 手电筒光束 + 六块玻璃

在 3D 渲染里:

  • 手电筒换成“摄像机”;
  • 光束换成“摄像机能看到的那一块空间”(视锥体);
  • 六块玻璃换成“六个裁剪平面”。

这六个平面一起围出摄像机的视锥体(View Frustum)

  • 左、右、上、下:由视野角(FOV)和宽高比(aspect)决定的斜面;
  • 近、远:与相机平行的两个平面,决定能看到多近、多远。

视锥体里——才是 GPU 要认真算的那一段世界


二、为什么不是“一个大球”而是“六个平面”?

你可能会问:

反正就是决定“看到哪儿、不看到哪儿”呗,
为啥非要搞“六个平面”,不能说“一个球半径多大”之类的?

原因有两个大方面:符合透视 + 数学简单

2.1 透视的视野天生是“锥形”的,而不是球

摄像机的视野是一个角度,不是一个固定半径的球:

  • 左右有一个水平方向的 FOV(比如 90°);
  • 上下也有一个垂直方向的 FOV(比如 60°);
  • 近/远各有一个距离(比如 0.1 到 100 单位)。

所以你能看到的是“从相机出发的一个角度范围内的空间段”,
这东西最像的形状就是:截头锥(Truncated Frustum)

要用平面来描述“角度 + 距离”的边界,刚好就是六个:

  • 左边界:在空间中对应一块“向内倾斜”的平面;
  • 右边界:同理;
  • 上、下两块;
  • 再加前后(近、远)两块与相机平行的平面。

2.2 用平面来定义边界,数学和实现都特别好搞

一个平面可以写成很简单的方程:

ax + by + cz + d = 0

或者用向量形式:

N · P + d = 0 // N 是法线,P 是点 (x,y,z)

判断一个点在平面的哪一侧,只要看:

v = N · P + d v > 0 → 在平面某一侧(比如“内侧”) v < 0 → 在另一侧(比如“外侧”) v = 0 → 正好在平面上

把六个平面都写成这种形式:

  • 左平面:点在内侧 → 保留;
  • 右平面:内侧 → 保留;
  • ……六个都通过 → 在视锥体里。

对 GPU 来说:

判断一个点/三角形是否在视锥体里,就是做六次“点乘 + 加法 + 比较”的事,简单到爆,高度可并行。

而且:

  • 裁剪三角形与平面的交点,在线性代数里也是基础操作;
  • 比起球、复杂曲面,平面是最适合硬件大规模处理的。

三、先在“摄像机空间”里看这六个平面:最直观

为了直觉清楚,我们先在摄像机空间(View / Camera Space)下看这六个平面。

假设约定:

  • 摄像机位置就是原点 (0,0,0);
  • 摄像机看向 -Z 方向(OpenGL 常用约定);
  • +X 往右,+Y 往上。

这样,视锥体大概长这样(侧视 & 俯视综合想象):

上平面 /| / | / | / | 远平面 相机/____| \ | \ | 近平面 \ | \ | \| 下平面

从这个角度看:

  • 近平面:z = -near(离相机最近的一块“可见墙”);
  • 远平面:z = -far(最远能看到的那块“可见墙”);
  • 左右上下平面:都是通过相机原点的一组斜面,
    由 FOV 和宽高比决定它们的倾斜程度。

我们挨个用大白话解释。


四、近平面 & 远平面:前后两个“剪刀口”

先从最容易理解的两个讲起。

4.1 近平面(Near Plane):离相机最近的“可见起点”

定义

  • 在摄像机空间里,是一个与相机平行的平面;
  • 一般是:z = -near(看向 -Z 时),或z = +near(看向 +Z 时,看约定);
  • near 是一个 >0 的距离,比如 0.1、0.3、1.0 等。

它的作用就两点:

  1. 防止太靠近相机的东西被“穿过镜头”显示得奇形怪状;
    比如你把一个物体塞到相机内部,会看到各种穿模怪象。
    近平面相当于说:

    “离我近到一定程度的东西,我就当看不见。”

  2. 配合远平面决定深度缓冲精度:

    • near 设太小,深度分布极度不均,近处特别敏感,远处全挤在一起,
      结果就是各种 Z-fighting(闪动、贴图打架);
    • near 适当大一点,可以显著改善深度精度。

你可以把近平面理解成:

相机前面的一块“无形玻璃”:
玻璃后面开始才是真正的世界,玻璃前面统统忽略。

4.2 远平面(Far Plane):最远能看到的那堵“墙”

定义:

  • 在摄像机空间里,也是一个与相机平行的平面;
  • 一般是:z = -far(或 +far,看约定);
  • far 是一个比较大的距离,比如 100、1000、10000。

作用:

  1. 限制“最远能看到的距离”:
    远平面后面的东西,直接认为不可见;
    否则无限远的世界无法渲染完。

  2. 同样影响深度精度:

    • far 越远,整个 [near, far] 区间越大;
    • 而深度缓冲位数是有限的(比如 24bit);
    • 远处一大段挤在很小的深度差里,导致精度不足。

你可以把远平面理解成:

世界尽头的一堵“隐形墙”:
超过这个墙,你就算再往前走,摄像机也不管你了。


五、左平面 & 右平面:视野左右边界

有了前后边界,再看左右。

5.1 再用“手机相机”的视野感受一下

你举起手机:

  • 屏幕两侧的边缘,就是“你能看到的世界”的左右边界;
  • 再往外的东西就不在画面里,等于你眼睛/相机视野的左右极限。

在 3D 空间里,这两个边界对应的就是两块斜着的平面:
左平面、右平面

5.2 这些平面是怎么决定方向的?

在摄像机空间下:

  • 摄像机在原点 (0,0,0);
  • 看向 -Z;
  • 设置一个水平 FOV(水平视野角)或者用垂直 FOV + 宽高比推出来。

假设垂直视野角是fovY,宽高比是aspect = width/height
通过几何关系可以推得一个水平视角fovX或 x/z 的关系。

简化理解:

  • 在 z = -1 的截面上,能看到的 x 范围是[-right, right]
  • 这个 right = tan(fovX/2);
  • 在更远的 z,比如 z = -k,这个范围就变成[-k*right, k*right]
    也就是一个“向外发散”的斜界线。

把这些边界线在 3D 中延长,就成了一块斜平面。

这块平面就是“左平面”或“右平面”,
所有可见点都必须在这些平面的“内侧”。

5.3 左右平面的平面方程(感受一下,不要害怕公式)

在摄像机空间中,
如果我们取视锥体的一侧边作为一个平面,可以表示成:

N_left · P + d_left >= 0 → 在左平面的内侧 N_right · P + d_right >= 0 → 在右平面的内侧

其中:

  • N_left 是左平面的法线,指向视锥体内部;
  • P 是任意空间中的点;
  • d 是偏移。

你不需要死记推导,只要知道:

这些平面是通过相机原点的斜面,
用几何关系(FOV、aspect)明确了它们的“倾斜角度”;
点如果跑到“外侧”,P 插进去就会算出 “<0” 的值,被裁掉。


六、上平面 & 下平面:视野上下边界

同理,上下两个边界也对应两块斜平面。

6.1 垂直 FOV 决定“头顶能看到多少天花板”

垂直 FOV(Field of View in Y)表示:

相机中轴线以上能看到多少度、以下能看到多少度。

比如 fovY = 60°:

  • 中线以上 30°;
  • 中线以下 30°。

这样在 z = -1 的截面上:

  • y 范围是[-top, top]
  • top = tan(fovY / 2)。

往更远处 z = -k,这个范围变成[-k*top, k*top]
在空间中展开同样形成两个向外倾斜的平面:

  • 上平面;
  • 下平面。

6.2 上下平面的方程同理

同样可以写成:

N_top · P + d_top >= 0 N_bottom· P + d_bottom>= 0

这些平面围成了视锥体的上、下边界。

你可以脑补成:

把相机前面的世界夹在一个“插上了四面斜玻璃板”的漏斗形空间里,
四个斜玻璃板就是“左、右、上、下”平面。


七、把六个平面合在一起:一个完整的视锥体

六块平面一起,就形成这么一个东西:

  • 近平面:靠你比较近的那一块截面;
  • 远平面:远处那一块截面;
  • 左平面 + 右平面 + 上平面 + 下平面:从相机原点出发的四面斜墙。

几何形状就是:

/|\ ← 上平面 / | \ / | \ / | \ /----+----\ ← 远平面 \ | / \ | / \ | / \ | / \|/ ← 下平面

中间是相机眼睛的位置(原点),
你可以想象自己身处顶点,四面八方是这六面看不见的“玻璃墙”。
墙里面的世界,才会被渲染。


八、从“摄像机空间”到“裁剪空间”再到 NDC:平面的“变形记”

前面讲的六个平面是在摄像机空间里的原始定义。
但在实际 GPU 实现里,裁剪经常发生在Clip Space / NDC中。

我们大致梳理一下它们是怎么“变形”的。

8.1 Camera Space → Clip Space(乘投影矩阵)

在顶点阶段,我们做了:

clipPos = Projection * viewPos

viewPos 是摄像机空间坐标(x, y, z, 1)
clipPos 是裁剪空间坐标(x', y', z', w')

投影矩阵的作用包括:

  • 把视锥体压成一个更规整的“标准盒子”的形状;
  • 同时为后续透视除法准备好 w。

在 Clip Space 下,裁剪规则经常被写成:

-w <= x <= w -w <= y <= w -w <= z <= w // 或 0 <= z <= w,视 API 约定

你可以理解为:

原来那六个斜面,在这个空间里变成了“居然就是一些简单的坐标范围限制”。

8.2 Clip Space → NDC(除以 w)

GPU 做了透视除法:

ndc = clipPos / w

得到 NDC:

-1 <= ndc.x <= 1 -1 <= ndc.y <= 1 -1 <= ndc.z <= 1 或 0<=z<=1

在 NDC 下,视锥体就变成了一个标准立方体:

[x,y,z] ∈ [-1,1]^3 或 z∈[0,1]

所以:

  • 六个平面最终在 NDC 中,就是这个立方体的六个“正方形面”:
    • x = -1(左)、x = +1(右);
    • y = -1(下)、y = +1(上);
    • z = -1/0(近)、z = +1(远)。

在这个空间中,裁剪变得非常容易:
判断点/三角形是否在 [-1,1] 盒子里。


九、一个点 / 一个三角形如何通过“六个平面”的考验?

现在我们用“考试通关”的比喻讲一遍流程。

9.1 一个点的判定:过六关才能见相机

假设有个点 P(不管是世界空间还是摄像机空间的坐标,都能转换过来),
要判断它在不在视锥体里,简单数学版就是:

  1. 把 P 变到摄像机空间(或直接用摄像机空间的 P_view);

  2. 用六个平面的方程来测试:

    对于每个平面 i: v_i = N_i · P_view + d_i 如果 v_i < 0:在平面外 → 扔掉 如果对所有 i 都 >= 0:在所有平面内 → 点在视锥体中

形象一点:

P 要经过“左、右、上、下、近、远”这六道门的审查,
在任何一道被打回票,就不会出现在渲染结果中。

在 Clip / NDC 空间下,这个判断甚至更简单直接:

if (x < -1 || x > 1 || y < -1 || y > 1 || z < zMin || z > zMax) 外;否则内。

9.2 三角形的判定 & 裁剪:三个人一起考试

对于一个三角形,有三个顶点 P0, P1, P2,每个都要被平面测试。

对某个平面来说:

  • 三个顶点都在“内侧”:这个三角形对这个平面来说安全;
  • 三个都在“外侧”:对这个平面来说整个三角形 OUT,可以直接丢掉;
  • 有的内,有的外:
    • 在这条平面上,“边 + 平面”的交点处会产生新的顶点;
    • 旧的三角形被切成新的一个或两个三角形,保留在内侧那部分。

对六个平面都执行一遍后:

这个三角形原地发生了“瘦身、截断、或者死亡(被裁掉)”,
剩下的就是最终要光栅化的形状。


十、这六个平面跟投影矩阵 / FOV / near / far 的关系再帮你捋一下

你写代码时,其实是通过几个简洁的参数间接设置这六个平面:

  • fov(视野角);
  • aspect(宽高比);
  • near(近平面距离);
  • far(远平面距离)。

比如在 Unity / OpenGL / DirectX 中,你可能写过:

Matrix4x4P=Matrix4x4.Perspective(fov,aspect,near,far);

或者:

autoP=glm::perspective(fov,aspect,near,far);

这些函数内部做的事可以概括为:

  1. 先在摄像机空间里,根据 fov + aspect 推出左右/上下平面的大致“开口角度”;
  2. 用 near、far 确定前后两个平面的位置;
  3. 用一套数学推导(透视投影矩阵),把这六个平面映射到 Clip Space 的统一形式;
  4. 这样:
    • 你就只需要记住 fov/near/far 这几个高层概念;
    • GPU 内部则能把所有点根据那六个平面进行裁剪。

所以你可以把这套关系记成一句:

你输入的是“摄像机视野参数”,
投影矩阵则在后台帮你算出了“六个裁剪平面”的具体形状,
GPU 利用这些平面把看不见的东西裁掉。


十一、实际开发中,六个裁剪平面能被你“用”在什么地方?

除了 GPU 自动裁剪之外,六个平面的概念还经常在你写游戏逻辑时直接用到,叫:视锥体裁剪(Frustum Culling)

11.1 CPU 端的“粗略裁剪”:不送给 GPU 的物体就更省事

例如:

  • 一个场景里有几千个物体;
  • 其中一大部分压根不在摄像机的视野内(比如在你背后、非常远的地方);
  • 你可以在 CPU 上先做一遍Frustum Culling
    • 用视锥体的六个平面判断物体的包围盒(AABB / 球)在不在视锥体中;
    • 不在的物体,连 DrawCall 都不发——直接不让它上 GPU。

这样能大幅减轻 GPU 负担。

常用写法(伪代码):

foreach object:if(object.BoundingBox 与 ViewFrustum 有交集):提交绘制else:跳过

而 ViewFrustum 就是由那六个平面定义的。

11.2 做特殊效果:镜面、水面、传送门的“自定义裁剪平面”

有时你会把“裁剪平面”的思路,用在有趣的画面技巧上,例如:

  • 渲染水面反射时,只想渲染水面上方的场景:
    在水面那一层设置一个裁剪平面,剪掉下方世界;
  • 渲染镜子:只渲染镜子所面对的一侧空间;
  • 做某种“只显示一个世界窗口”的效果。

这些本质上就是:

在原来的六个平面基础上,人为再加一块平面,
用它去把空间截成两半,只在一边画东西。

高级引擎 & GPU API 常提供接口来设置“额外的 Clip Plane”。


十二、最后,用一大段顺口的大白话把它总结到底

你可以把下面这段当成“六个裁剪平面”的记忆卡片:

  1. 摄像机前面能看到的那一块空间,并不是无限大,也不是随便乱的形状,而是一个由六个平面围起来的视锥体

    • 左平面、右平面:决定视野的左右边界;
    • 上平面、下平面:决定视野的上下边界;
    • 近平面:离相机最近,从这个平面以后才算“开始可见”;
    • 远平面:最远能看到哪一堵“隐形墙”,后面就算存在也不渲染。
  2. 这六个平面起源于:

    • 摄像机的视野角(FOV)、宽高比(aspect);
    • 和你设置的 near / far 距离;
    • 投影矩阵会把这六个平面编码进数学里,最终在 Clip / NDC 空间里变成一个规规矩矩的“标准盒子”。
  3. GPU 在进行裁剪时,本质上就是:

    • 针对每个顶点/三角形,检查它们是否全部落在六个平面的内侧;
    • 全在外:丢弃;
    • 全在内:保留;
    • 一部分在内一部分在外:沿着平面边缘算交点,把三角形切一刀,只留下里面那一块。
  4. 在 NDC 空间中,这六个平面变得超简单:

    • x = -1 / +1→ 左右平面;
    • y = -1 / +1→ 上下平面;
    • z = -1/0 / +1→ 近、远平面;
    • 判断“在不在视野里”就变成了简单的范围判断:
      -1 <= x,y,z <= 1(或 z ∈[0,1])。
  5. 在自己写游戏逻辑时,你也可以显式使用“六个平面”的视锥体来做:

    • CPU 端的视锥裁剪(Frustum Culling);
    • 自定义裁剪效果(镜子、水面、传送门等)。

如果把它特别俗地再说一遍:

六个裁剪平面,就是摄像机前面那一块“无形的玻璃盒子”的六个壁。
盒子里面的世界 GPU 才去管,
盒子外面的一概不理。

你告诉引擎“我视野多大、最近多近、最远多远”,
引擎就帮你把这个玻璃盒子算出来,
GPU 就靠它来决定——
“哪些东西该出现在屏幕上,哪些东西压根就不要算”。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/26 2:45:26

SubtitleOCR技术实现:从视频硬字幕到结构化文本的智能转换

SubtitleOCR技术实现&#xff1a;从视频硬字幕到结构化文本的智能转换 【免费下载链接】SubtitleOCR 快如闪电的硬字幕提取工具。仅需苹果M1芯片或英伟达3060显卡即可达到10倍速提取。A very fast tool for video hardcode subtitle extraction 项目地址: https://gitcode.co…

作者头像 李华
网站建设 2026/5/25 22:24:10

uiautomator2图像识别性能优化实战:从卡顿到流畅的完整解决方案

"为什么我的自动化脚本总是卡在图像识别环节&#xff1f;"这是许多Android自动化开发者经常遇到的困扰。当你在凌晨三点盯着CPU占用率飙升到85%的监控面板时&#xff0c;是否也曾怀疑过自己的代码&#xff1f;本文将带你深入剖析uiautomator2图像识别的性能瓶颈&…

作者头像 李华
网站建设 2026/5/26 3:45:41

终极跨平台直播聚合神器:Dart Simple Live完整使用指南

终极跨平台直播聚合神器&#xff1a;Dart Simple Live完整使用指南 【免费下载链接】dart_simple_live 简简单单的看直播 项目地址: https://gitcode.com/GitHub_Trending/da/dart_simple_live 还在为频繁切换不同直播应用而烦恼吗&#xff1f;想要在一个界面中同时观看…

作者头像 李华
网站建设 2026/5/26 2:02:29

Tiled地图渲染优化:提升大型游戏场景性能的关键技术

Tiled地图渲染优化&#xff1a;提升大型游戏场景性能的关键技术 【免费下载链接】tiled 项目地址: https://gitcode.com/gh_mirrors/til/tiled 在游戏开发领域&#xff0c;Tiled地图编辑器作为专业的瓦片地图创建工具&#xff0c;其渲染性能直接影响游戏体验。面对日益…

作者头像 李华
网站建设 2026/5/25 15:02:42

Flame噪声算法实战指南:从基础理论到地形生成应用

Flame噪声算法实战指南&#xff1a;从基础理论到地形生成应用 【免费下载链接】flame A Flutter based game engine. 项目地址: https://gitcode.com/GitHub_Trending/fl/flame 是否曾为重复的地形设计感到困扰&#xff1f;想要创造无限延伸的自然景观却不知从何入手&am…

作者头像 李华
网站建设 2026/5/26 3:45:58

基于Dlib的疲劳驾驶检测系统:5步快速部署指南

基于Dlib的疲劳驾驶检测系统&#xff1a;5步快速部署指南 【免费下载链接】Fatigue-Driving-Detection-Based-on-Dlib 项目地址: https://gitcode.com/gh_mirrors/fa/Fatigue-Driving-Detection-Based-on-Dlib 疲劳驾驶是道路交通安全的重要隐患&#xff0c;每年因驾驶…

作者头像 李华