逛街时你一定注意过商场橱窗里的模特:正对街道的一面妆容精致、衣服笔挺,背后却只是光秃秃的塑料骨架。店主心里门儿清——顾客只能隔着玻璃看正面,把预算花在没人看得见的后脑勺上,纯属浪费。3D 渲染的世界里也有这样一位精明的“店主”,它叫 GPU,橱窗里的“模特”则是由无数三角形拼成的三维模型。相机就是橱窗外的顾客,三角形的某条边朝不朝相机,决定了它值不值得被绘制。 背面剔除(Backface Culling)就是这套“精明生意经”:在三角形进入光栅化流水线之前,先判断这个面的朝向,凡是背对相机的面直接跳过,一个像素都不画。一个封闭的三维网格,任意视角下大约有一半三角形是背向的,这一刀砍下去,渲染负担几乎减半。你不需要理解复杂的数学,只要记住橱窗模特的逻辑:正面朝顾客的才打扮,背面的连妆都不用上。接下来,我们会从法线方向、判定条件、性能收益到实现流程,把这块知识完整拆开,看看 GPU 这位“店主”到底是怎么省下这笔钱的。

岗位关联

对 AI 从业者而言,掌握背面剔除意味着你能驾驭大规模三维数据的实时可视化,无论是数字孪生、机器人仿真还是 XR 应用,渲染优化的基本功都是绕不开的护城河。

目录
  1. 可视化演示
  2. 剔除概念
  3. 法线方向
  4. 判定条件
  5. 性能收益
  6. 实现流程
  7. 特殊场景
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

本节配有交互动效(进入视口后自动加载,约 3KB)。

背面剔除效果 F = Front (\u6b63\u9762): N\u00b7V > 0, \u4fdd\u7559\u7ed8\u5236

剔除概念

商场橱窗里的模特永远只把正面留给街道。店主不会在模特后脑勺上画眼影,因为那条街的行人绕不过玻璃,看不见背面。这就是背面剔除的雏形:先想清楚“什么值得被看到”,再决定“什么值得被绘制”。

三维模型由成千上万个三角形拼成。当相机对准模型时,一部分三角形的“脸”朝着相机,叫前向面;另一部分“后脑勺”朝着相机,叫背向面。一个封闭的球体或立方体,任意角度看过去,大约一半三角形是背向的。如果 GPU 老老实实把它们全部画出来,这些背向面绘制的像素最终也会被前面的面挡住,属于纯粹的浪费——相当于店主给模特后脑勺抹了昂贵的粉底。

背面剔除发生在光栅化之前:GPU 拿到一个三角形,先算它的朝向,发现是背向面就直接丢弃,不进入逐像素阶段。它判断朝向的依据,是三角形法线与视线方向的夹角关系。设 $\vec{N}$ 为三角形面法线,$\vec{V}$ 为从三角形指向相机的向量,则判定规则为:

店主省下的是化妆费,GPU 省下的是光栅化与像素填充的开销。背面剔除不做任何“看心情”的取舍,它只有一条铁律:看不见的面,干脆不画。

保留={是N⃗⋅V⃗>0(前向面)否N⃗⋅V⃗<0(背向面)\text{保留} = \begin{cases} \text{是} & \vec{N} \cdot \vec{V} > 0 \text{(前向面)} \\ \text{否} & \vec{N} \cdot \vec{V} < 0 \text{(背向面)} \end{cases}
法线和视线方向一致(夹角小于 90 度),说明面正对相机;方向相反则背对相机,直接丢弃。
  1. 法线向量$\vec{N}$ 是垂直于三角形平面的方向向量,代表“模特脸的朝向”,由三角形顶点绕序决定,而非美术手工指定。
    N⃗=(v⃗1−v⃗0)×(v⃗2−v⃗0)\vec{N} = (\vec{v}_1 - \vec{v}_0) \times (\vec{v}_2 - \vec{v}_0)
  2. 视线向量$\vec{V}$ 是从三角形上某点指向相机位置的向量,代表“顾客目光投来的反方向”,即顾客站在哪里看这个面。
    V⃗=camera_pos−v⃗0\vec{V} = \text{camera\_pos} - \vec{v}_0
  3. 点积运算两个向量做点积,结果等于二者长度之积乘以夹角余弦,符号直接反映夹角是锐角还是钝角。
    N⃗⋅V⃗=∥N⃗∥∥V⃗∥cos⁡θ\vec{N} \cdot \vec{V} = \|\vec{N}\| \|\vec{V}\| \cos\theta
  4. 符号判定点积大于零说明法线与视线大致同向,面朝着相机,保留绘制;小于零说明面背着相机,果断剔除。
    cos⁡θ>0  ⟺  −90∘<θ<90∘\cos\theta > 0 \iff -90^\circ < \theta < 90^\circ

法线方向

橱窗模特凭什么知道自己哪边是正面?答案藏在组装说明书里:所有零件按统一方向摆放,头朝上、脸朝街。三角形也有这样一份“组装说明书”,它叫顶点绕序。三个顶点按什么顺序连接,决定了这个面的正反。

图形学中有一个通用约定:当从三角形正面看过去时,三个顶点呈逆时针排列;从背面看则是顺时针。这个约定被写入 OpenGL、WebGL、DirectX 等所有主流图形接口。只要顶点索引顺序一致,整个模型就能做到“所有面朝外”——就像所有模特都整齐地面朝街道。

把三个顶点记为 $\vec{v}_0, \vec{v}_1, \vec{v}_2$,先构造两条边向量 $\vec{u}$ 和 $\vec{w}$,再用叉积求出垂直于此平面的法线:

法线方向是整套剔除机制的“地基”:绕序决定法线,法线决定朝向,朝向决定生死。只要模型师按统一绕序建模,GPU 就能自动分辨每一张“脸”的正反,不需要任何额外标注。

u⃗=v⃗1−v⃗0,w⃗=v⃗2−v⃗0\vec{u} = \vec{v}_1 - \vec{v}_0, \quad \vec{w} = \vec{v}_2 - \vec{v}_0
N⃗=u⃗×w⃗=(uywz−uzwyuzwx−uxwzuxwy−uywx)\vec{N} = \vec{u} \times \vec{w} = \begin{pmatrix} u_y w_z - u_z w_y \\ u_z w_x - u_x w_z \\ u_x w_y - u_y w_x \end{pmatrix}
叉积的结果是一个同时垂直于 $\vec{u}$ 和 $\vec{w}$ 的新向量,它的方向由“右手定则”决定,恰好对应逆时针绕序的正面朝向。
  1. 构造边向量从 $\vec{v}_0$ 出发指向 $\vec{v}_1$ 得到 $\vec{u}$,指向 $\vec{v}_2$ 得到 $\vec{w}$,两条边张成三角形所在平面。
    u⃗=v⃗1−v⃗0,w⃗=v⃗2−v⃗0\vec{u} = \vec{v}_1 - \vec{v}_0, \quad \vec{w} = \vec{v}_2 - \vec{v}_0
  2. 叉积定义$\vec{u} \times \vec{w}$ 的结果垂直于这两条边,也就是垂直于整个三角形平面,即法线方向。
    N⃗=u⃗×w⃗\vec{N} = \vec{u} \times \vec{w}
  3. 分量展开叉积可用行列式展开,$x$ 分量由 $u_y w_z$ 减去 $u_z w_y$ 得到,其余分量同理轮换。
    Nx=uywz−uzwy,Ny=uzwx−uxwz,Nz=uxwy−uywxN_x = u_y w_z - u_z w_y, \quad N_y = u_z w_x - u_x w_z, \quad N_z = u_x w_y - u_y w_x
  4. 右手定则四指从 $\vec{u}$ 弯向 $\vec{w}$,拇指指向就是法线方向;逆时针绕序恰好让法线指向观察者。
    N⃗=u⃗×w⃗=−w⃗×u⃗\vec{N} = \vec{u} \times \vec{w} = -\vec{w} \times \vec{u}

判定条件

店主站在橱窗里侧,顾客走远走近、从左到右,他都能一眼看出模特是正脸还是后脑勺。GPU 也一样,不管相机怎么转、模型怎么翻,它总能快速回答同一个问题:“这个面,朝不朝我?”

答案藏在三角形投影到屏幕后的“旋转方向”里。想象你从相机的位置看出去,前向三角形因为逆时针绕序,在屏幕上看起来也是逆时针;背向三角形则变成顺时针。于是 GPU 只需要做一次简单的算术:取三角形三个顶点的屏幕坐标,计算有符号面积:

这样,GPU 在光栅化之前就完成了“目测”,连三维空间中的法线和点积都省了。判定条件如此廉价,以至于它对每个三角形只花几个时钟周期——这正是它能成为渲染管线标配的原因。店员扫一眼模特朝向,比把模特转过来细细端详要快得多。

S=(x1−x0)(y2−y0)−(y1−y0)(x2−x0)S = (x_1 - x_0)(y_2 - y_0) - (y_1 - y_0)(x_2 - x_0)
$S$ 的正负代表了顶点在屏幕上的绕行方向。$S > 0$ 为逆时针(前向面),$S < 0$ 为顺时针(背向面),这个符号就是剔除的判决书。
  1. 屏幕坐标三角形经过模型、视图、投影变换后投影到屏幕上,得到三个二维坐标 $(x_0,y_0)$、$(x_1,y_1)$、$(x_2,y_2)$。
    p0=(x0,y0),p1=(x1,y1),p2=(x2,y2)p_0 = (x_0, y_0), \quad p_1 = (x_1, y_1), \quad p_2 = (x_2, y_2)
  2. 构建向量以 $p_0$ 为起点,分别指向 $p_1$ 和 $p_2$,构造屏幕空间的两条边。
    a⃗=(x1−x0,y1−y0),b⃗=(x2−x0,y2−y0)\vec{a} = (x_1 - x_0, y_1 - y_0), \quad \vec{b} = (x_2 - x_0, y_2 - y_0)
  3. 二维叉积在二维平面上,叉积退化为一个标量 $S$,数值上等于平行四边形面积,符号表示绕行方向。
    S=axby−aybxS = a_x b_y - a_y b_x
  4. 符号判决约定逆时针绕序为正面时,$S > 0$ 保留,$S < 0$ 剔除,$S = 0$ 说明三角形退化为一条线,通常也剔除。
    S>0⇒前向面,S<0⇒背向面S > 0 \Rightarrow \text{前向面}, \quad S < 0 \Rightarrow \text{背向面}

性能收益

店主算了一笔账:给模特后脑勺化妆要 10 分钟,却从没有一个顾客看见过。去掉这道工序,每天可以多陈列 3 个新模特。GPU 也算过这笔账:一个背向三角形从光栅化到像素填充,涉及成千上万个片元的计算,而它最终会被更近的面完全盖住。

背面剔除的收益首先体现在几何处理上:三角形在进入光栅化器之前就被拦截,后续的顶点插值、片元着色、深度比较全部免谈。其次是像素填充率:被剔除的面不会产生任何片元,这对高分辨率、多重采样抗锯齿(MSAA)等填充率受限的场景尤为重要。一个闭合网格,无论相机朝向如何,可见的前向面大约只占一半,这意味着渲染负载近乎腰斩:

这里要澄清一个关键区别:背面剔除节省的是“面”,而遮挡剔除(Occlusion Culling)节省的是“被挡住的面”,二者不是一回事。墙壁后面的敌人属于遮挡问题,由深度缓冲在像素阶段解决;而背对相机的面,在源头就被掐掉了。店主省下的钱用来多摆几个新模特,GPU 省下的算力则用来渲染更丰富的场景。

节省率=(1−N绘制N全部)×100%≈50%\text{节省率} = \left(1 - \frac{N_{\text{绘制}}}{N_{\text{全部}}}\right) \times 100\% \approx 50\%
对典型闭合网格,任意视角下约一半三角形是背向的。节省率随视角变化,但始终在 50% 附近波动。
  1. 绘制三角形数$N_{\text{绘制}}$ 是剔除后真正进入光栅化的前向三角形数量,它是“值得打扮的正面”。
    N绘制=∑前向面1N_{\text{绘制}} = \sum_{\text{前向面}} 1
  2. 全部三角形数$N_{\text{全部}}$ 是网格里所有三角形的总数,包含前向面与背向面,是流水线原本要处理的量。
    N全部=N绘制+N剔除N_{\text{全部}} = N_{\text{绘制}} + N_{\text{剔除}}
  3. 节省比例用被剔除的三角形数除以总数,得到省下的处理比例;闭合网格在随机视角下约为一半。
    节省率=N全部−N绘制N全部×100%\text{节省率} = \frac{N_{\text{全部}} - N_{\text{绘制}}}{N_{\text{全部}}} \times 100\%
  4. 约等一半封闭凸体与大多数闭合网格,任意视点下可见面与不可见面数量大致相等。
    N绘制N全部≈12\frac{N_{\text{绘制}}}{N_{\text{全部}}} \approx \frac{1}{2}

实现流程

模特从仓库到橱窗要经过开箱、组装、摆位多道工序。店主在“摆上展台”之前设了一道检查岗:先看正面朝不朝街,不朝的直接退回仓库。这道检查岗在渲染管线里的位置,就是顶点变换之后、光栅化之前。

显卡拿到顶点数据后,先经过模型变换、视图变换、投影变换,把三维坐标换算到屏幕附近的空间。此时每个三角形都已经“就位”,正好可以检查朝向。由于屏幕空间的二维叉积足够廉价,GPU 通常在这里完成剔除,然后才把幸存的前向面交给光栅化器。整个流程可以概括为:

工程上开启背面剔除只需一行代码。OpenGL / WebGL 中调用 gl.enable(gl.CULL_FACE) 即可启用剔除,用 gl.cullFace(gl.BACK) 指定剔除背向面;DirectX 则在光栅化状态中设置 RasterizerDesc.CullMode。绝大多数渲染引擎默认开启这个选项。店主在入口把关,省下的每一分算力都转化为流畅的画面帧率。

(xsys)=fzc(xcyc)+(cxcy)\begin{pmatrix} x_s \\ y_s \end{pmatrix} = \frac{f}{z_c} \begin{pmatrix} x_c \\ y_c \end{pmatrix} + \begin{pmatrix} c_x \\ c_y \end{pmatrix}
其中 $(x_c, y_c, z_c)$ 是相机空间坐标,$f$ 是焦距相关缩放,$(c_x, c_y)$ 是屏幕中心偏移。投影后的 $(x_s, y_s)$ 正是用于有符号面积判定的屏幕坐标。
  1. 相机空间坐标顶点经过模型和视图变换后位于相机空间,$z_c$ 表示顶点到相机的深度。
    pc=(xc,yc,zc)p_c = (x_c, y_c, z_c)
  2. 透视投影将三维坐标除以深度 $z_c$,实现“近大远小”的透视效果,$f$ 控制视野缩放。
    xndc=f⋅xczc,yndc=f⋅yczcx_{ndc} = \frac{f \cdot x_c}{z_c}, \quad y_{ndc} = \frac{f \cdot y_c}{z_c}
  3. 屏幕映射把归一化坐标平移到屏幕中心 $(c_x, c_y)$,得到光栅化使用的像素坐标。
    xs=xndc+cx,ys=yndc+cyx_s = x_{ndc} + c_x, \quad y_s = y_{ndc} + c_y
  4. 剔除介入拿到 $(x_s, y_s)$ 后立即计算有符号面积 $S$,背向面在进入光栅化器之前被丢弃。
    S<0⇒discard before rasterizationS < 0 \Rightarrow \text{discard before rasterization}

特殊场景

并不是所有模特都只有一张脸。橱窗正中央的旋转展示台上,模特前后都穿着精心设计的衣服,顾客转一圈能看个遍。这样的“双面展示品”,店主绝不会拦下。渲染中对应的是双面材质:叶片、纸张、布料这类薄片模型,正反两面都可能被看到,背面剔除反而会帮倒忙。

处理这类特殊情况,规则只有一条:看模型是否需要双面可见。树叶、翅膀、告示牌等开放网格,关闭剔除或开启双面渲染;而墙体、箱子等闭合网格,保持剔除以获得最佳性能。判定逻辑可以写成:

另一个常见坑是绕序不统一:部分美术工具导出的模型顶点顺序混乱,导致本该朝外的面被判成背向面,出现“面消失”的破洞。排查思路是先关掉剔除确认模型完整,再检查法线可视化,最后统一重算绕序。真正的精明店主知道:什么时候该省、什么时候不该省,比一味省钱更重要。

可见=front(N⃗,V⃗)∨twoSided\text{可见} = \text{front}(\vec{N}, \vec{V}) \lor \text{twoSided}
front(N⃗,V⃗)=(N⃗⋅V⃗>0)\text{front}(\vec{N}, \vec{V}) = (\vec{N} \cdot \vec{V} > 0)
双面模式等价于“无论正面背面都保留”。代价是三角形处理量翻倍,因此只对确有需要的薄片模型开启。
  1. 前向面判定$\text{front}$ 为真表示面正对相机,此时无论是否双面都应当保留绘制。
    front=(N⃗⋅V⃗>0)\text{front} = (\vec{N} \cdot \vec{V} > 0)
  2. 双面开关$\text{twoSided}$ 为真表示该材质允许看到背面,此时背向面也被保留。
    twoSided=true⇒保留全部面\text{twoSided} = \text{true} \Rightarrow \text{保留全部面}
  3. 逻辑或合并前向面保留,或双面模式下背向面也保留,二者满足其一即可通过。
    可见=front∨twoSided\text{可见} = \text{front} \lor \text{twoSided}
  4. 代价权衡双面渲染让每帧处理的三角形数接近翻倍,仅在薄片类模型上划算。
    N双面≈2×N单面N_{\text{双面}} \approx 2 \times N_{\text{单面}}

AI 应用场景

游戏引擎大规模场景渲染

阶段:渲染性能优化与场景管理

解决的问题:开放世界游戏中,场景可能包含数百万三角形。若全部绘制,GPU 几何处理与像素填充双双超载,帧率骤降。背面剔除作为第一道关卡,在光栅化前砍掉约一半三角形,是引擎性能预算的基石。

具体案例:在 Unity 中,模型导入设置的 “Generate Colliders“ 旁通常默认开启 ”Backface Cull”;Unreal 的材质面板也有 Two Sided 选项。当玩家在城市场景中奔跑时,每帧数万栋建筑背向面被静态剔除,配合遮挡剔除,最终只绘制可见的前向面,保证 60 FPS 流畅体验。

数字孪生与 Web 端三维可视化

阶段:三维数据可视化与前端渲染管线搭建

解决的问题:BIM 建筑模型、工业设备点云等动辄百万级三角形,浏览器端受 CPU/GPU 资源限制,直接全量绘制会卡顿甚至崩溃。利用背面剔除配合 LOD 技术,可显著降低绘制指令数与填充压力。

具体案例:在 Three.js 中,MeshBasicMaterial 默认 side: THREE.FrontSide,即自动剔除背面。当开发者加载一栋楼的 BIM 模型时,墙体、楼板的背向面被自动跳过;遇到玻璃幕墙或半透明围栏时,改用 THREE.DoubleSide 保证双面可见,兼顾真实感与性能。

移动端 AR/XR 低功耗渲染

阶段:端侧推理与渲染协同优化

解决的问题:手机处理器与电池容量有限,AR 应用需要同时运行相机、SLAM 与渲染。背面剔除能减少一半以上的片元着色工作量,直接降低 GPU 功耗与发热,延长设备续航。

具体案例:在 ARKit/ARCore 应用中放置虚拟家具时,家具模型背面永远不会被用户看到(相机总是从外部观察)。默认开启背面剔除后,单个沙发的三角形绘制量从数万降至数千,帧率稳定在 30 FPS 以上,设备温度下降约 2-3 摄氏度。

Python 演示

python51 行
import math  def compute_normal(v0, v1, v2):    # 用叉积计算三角形面法线, 方向由顶点绕序决定    u = (v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2])    w = (v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2])    return (u[1] * w[2] - u[2] * w[1],            u[2] * w[0] - u[0] * w[2],            u[0] * w[1] - u[1] * w[0])  def dot(a, b):    # 三维向量点积    return a[0] * b[0] + a[1] * b[1] + a[2] * b[2]  def is_front_face(v0, v1, v2, camera):    # 法线与视线同向则为前向面, 点积大于零保留    n = compute_normal(v0, v1, v2)    view = (camera[0] - v0[0], camera[1] - v0[1], camera[2] - v0[2])    return dot(n, view) > 0  def cube_triangles():    # 立方体 8 个顶点, 索引 0-7    V = [(-1,-1,-1), (1,-1,-1), (1,1,-1), (-1,1,-1),         (-1,-1, 1), (1,-1, 1), (1,1, 1), (-1,1, 1)]    # 六个面, 每个面 4 个顶点按逆时针绕序(从外部看)    quads = [(1,2,6,5), (0,4,7,3), (3,7,6,2),             (0,1,5,4), (4,5,6,7), (0,3,2,1)]    tris = []    for q in quads:        tris.append((V[q[0]], V[q[1]], V[q[2]]))        tris.append((V[q[0]], V[q[2]], V[q[3]]))    return tris  def main():    camera = (3.0, 2.0, 5.0)  # 相机放在右上前方    tris = cube_triangles()    kept = [t for t in tris if is_front_face(*t, camera)]    culled = len(tris) - len(kept)    print('total triangles:', len(tris))    print('front faces kept:', len(kept))    print('back faces culled:', culled)    print('culling rate: %.1f%%' % (100.0 * culled / len(tris)))  if __name__ == '__main__':    main()
目的:用纯 Python 实现背面剔除的核心数学——叉积算面法线、点积判朝向,并在一个立方体网格上统计剔除比例。
  1. compute_normal(v0, v1, v2)用两条边做叉积,得到垂直三角形面的法线,方向由顶点绕序决定。
  2. dot(n, view) > 0法线与视线点积大于零表示面朝相机,这是前向面的核心判定。
  3. view = (camera[0]-v0[0], ...)构造从三角形指向相机的视线向量。
  4. quads = [(1,2,6,5), ...]每个面按“从外部看逆时针”的顺序给出 4 个顶点索引,保证法线朝外。
  5. tris.append((V[q[0]], V[q[1]], V[q[2]]))把四边形拆成两个三角形,方便逐个判定。
  6. kept = [t for t in tris if ...]列表推导一次性筛出所有前向面,模拟剔除后的幸存者。
  7. culling rate相机在右上前方时,恰好 6 个面被剔除,比例 50%,与理论吻合。

常见误区

误区我以为背面剔除能把被遮挡的物体一起省掉,比如站在墙后面的敌人,因为看不见所以会被剔除。

正解背面剔除只处理“背对相机”的三角形,墙体后面的敌人属于“被更近的物体挡住”,要靠深度缓冲在像素阶段解决。前者在光栅化前删面,后者在光栅化后挡像素,分工完全不同。橱窗比喻里,背面剔除是让店主不打扮模特后脑勺;而顾客前面站了个人挡住模特,那是换站位(遮挡)的问题。

为何常见:两者都叫“剔除”,听起来都在省掉看不见的东西,初学者极容易把职责搞混。

误区判断背面必须有三维法线和相机位置,三角形一旦投影到屏幕就丢了深度信息,怎么可能还分得清正反?

正解投影确实丢失了深度值,但保留了顶点的绕行方向。逆时针的前向面投影后仍是逆时针,背向面会变成顺时针,用有符号面积公式一个符号就能判定。GPU 正是利用这个性质,在屏幕空间完成剔除,省去一次三维点积。

为何常见:直觉上“朝向”是三维属性,很难相信二维投影还能保留方向信息,忽略了绕序的拓扑不变性。

误区法线不是美术在建模软件里指定好的吗?剔除直接用那个法线和相机点积不就行了?

正解美术提供的是顶点法线,用于光照明暗计算,与几何面的朝向无关。GPU 做背面剔除时用的是几何法线,它由三角形顶点索引顺序(绕序)经过叉积算出。顶点法线像模特脸上的妆容,绕序才是模特骨架的朝向,两者不能混用。

为何常见:图形学里“法线”一词高频出现,面法线、顶点法线、法线贴图含义各异,初学者容易张冠李戴。

误区反正背面看不见,开启剔除对所有模型都没有副作用,那就全开呗。

正解开放网格或双面材质一旦开启剔除,就会出现“面消失”的破洞——树叶只剩一半、纸张只剩一面。剔除的正确性依赖网格的封闭性和绕序一致性。叶片、布料等薄片模型必须关闭剔除或开启双面渲染,才能保证视觉完整。

为何常见:在闭合网格上测试时确实看不出差别,于是把结论错误地推广到所有网格类型。

误区只要剔除比例高,比如砍掉 80% 的三角形,性能就一定大幅提升。

正解背面剔除节约的是光栅化前的几何处理和光栅化后的像素填充。如果三角形极小、过绘制本身很低,或者瓶颈在顶点变换而非像素着色,收益会大打折扣。桌面端顶点瓶颈时剔除省下的填充不算数,必须用性能分析工具确认瓶颈所在,再决定优化方向。

为何常见:把“节省三角形数量”直接等同于“帧率提升”,忽略了渲染管线的瓶颈可能在不同阶段。

面试题

Q1. 背面剔除的主要目标是什么?

单选题 · easy

  • A.删除被遮挡的像素
  • B.合并相邻网格减少批次
  • C.跳过背向相机的三角形← 正确答案
  • D.压缩纹理以节省显存

答案:C。背面剔除在光栅化前跳过背向相机的三角形,减少几何与填充开销。被遮挡的像素由深度缓冲处理,合并批次是合批优化,压缩纹理是显存优化,三者都与背面剔除无关。橱窗比喻中,这是店主决定不给模特后脑勺化妆。

Q2. 屏幕空间中如何判断三角形是否背向相机?

单选题 · mid

  • A.求三角形面积的绝对值
  • B.比较三个顶点的深度值
  • C.检查顶点的颜色是否相同
  • D.看屏幕坐标叉积符号← 正确答案

答案:D。三角形投影到屏幕后,计算有符号面积(二维叉积),符号代表顶点绕行方向,逆时针为前向、顺时针为背向。面积绝对值丢掉了方向信息,深度值比较属于深度缓冲的职责,顶点颜色与朝向无关。

Q3. 逆时针绕序约定下,法线与视线点积大于零表示什么?

单选题 · mid

  • A.三角形正对相机
  • B.三角形平行于视线
  • C.三角形背对相机← 正确答案
  • D.三角形处于裁剪区外

答案:C。法线指向三角形正面,视线从三角形指向相机,二者点积大于零说明夹角小于 90 度,面正对相机,应当保留绘制。平行时点积接近零,背面时点积为负,裁剪区与朝向判定无关。

Q4. 背面剔除在渲染管线中的哪个阶段进行?

单选题 · easy

  • A.深度测试完成之后
  • B.顶点光栅化之前← 正确答案
  • C.像素着色器之后
  • D.纹理采样流程之后

答案:B。背面剔除发生在顶点变换之后、光栅化之前,此时三角形已投影到屏幕但尚未生成像素。深度测试、像素着色、纹理采样都在光栅化之后,顺序上晚于剔除。这相当于橱窗检查岗设在摆上展台之前。

Q5. 背面剔除无法替代以下哪种功能?

单选题 · hard

  • A.顶点数据缓存机制
  • B.动态合批减少切换
  • C.法线贴图采样细节
  • D.深度缓冲的遮挡消除← 正确答案

答案:D。背面剔除只删背向面,被前方物体遮挡的面仍需深度缓冲逐像素比较决定显隐。二者一个在光栅化前、一个在光栅化后,是互补关系。顶点缓存、合批、法线贴图都与可见性判断无关。

Q6. 渲染双面可见的树叶模型,正确的做法是?

单选题 · hard

  • A.关闭该物体的剔除开关← 正确答案
  • B.增加三角形的顶点数量
  • C.使用深度缓冲偏移技巧
  • D.强制所有面单面绘制

答案:A。树叶是开放网格,正反两面都可能被看到,必须关闭剔除或开启双面渲染,否则翻转后叶片会消失。增加顶点不改变朝向判定,深度偏移只解决面片重叠,强制单面绘制会加剧破洞问题。

Q7. 一个闭合网格在任意视角下,背面剔除的典型节省比例约为?

单选题 · mid

  • A.只有一成左右
  • B.全部的三角面片
  • C.接近一半的三角形← 正确答案
  • D.几乎没有任何节省

答案:C。闭合网格任意视角下,面向相机的前向面与背向面数量大致相等,剔除率接近 50%。一成左右偏低,全部面片不可能被剔除(否则画面全空),零节省说明网格可能全是双面材质。

Q8. 下列哪些情况下背面剔除的性能收益最明显?

多选题 · mid

  • A.闭合网格大量充满屏幕← 正确答案
  • B.相机围绕封闭物体运动← 正确答案
  • C.物体全部使用双面材质
  • D.屏幕分辨率设置极小

答案:AB。闭合网格充满屏幕时背向面占比高,剔除收益大;相机围绕封闭物体运动时始终有一半面被剔除。双面材质关闭了剔除,收益归零;分辨率极小时填充成本本就很低,剔除的填充收益不明显。

Q9. 关于法线方向与背面剔除的关系,正确的是?

多选题 · hard

  • A.顶点绕序决定法线朝向← 正确答案
  • B.叉积结果必须归一化后才能使用
  • C.交换两顶点会翻转朝向← 正确答案
  • D.法线只与顶点坐标位置相关

答案:AC。法线由顶点索引顺序经叉积算出,逆时针绕序对应正面朝向;交换任意两个顶点会反转叉积方向,从而翻转正反面。判定朝向只需要叉积符号,无需归一化;顶点坐标位置只是输入,真正决定朝向的是连接顺序。

Q10. 封闭网格使用背面剔除带来的好处有哪些?

多选题 · mid

  • A.减少几何处理阶段的开销← 正确答案
  • B.降低像素填充率的压力← 正确答案
  • C.替代所有遮挡剔除工作
  • D.消除所有边缘锯齿现象

答案:AB。背向面在光栅化前被丢弃,几何处理量减半,同时不产生任何片元,填充率压力随之降低。但被遮挡的面仍靠深度缓冲处理,背面剔除无法替代遮挡剔除;抗锯齿属于采样技术,与剔除无关。