Geometry — meshes, curves & implicit surfaces几何——网格、曲线与隐式曲面
Lesson 02 built the transform chain that moves points from object space to the screen — but it kept assuming an object is just a bag of triangle vertices. Where do those triangles come from, why triangles and not squares or spheres, and what other ways are there to describe a shape? This lesson defines the geometry the whole pipeline exists to move: the triangle and why it is graphics' atom, the mesh that stores millions of them, the smooth curves and surfaces artists actually author, and the implicit surfaces a ray tracer prefers. Two rival answers to one question — how do you write down a shape? — set up everything downstream. 第 02 课造好了把点从物体空间搬到屏幕的变换链——但它一直假设物体就是一袋三角形顶点。那些三角形从哪来?为什么是三角形而不是正方形或球?还有哪些描述形状的方式?这一课定义整条流水线存在的意义所在——它要搬运的几何:三角形以及它为何是图形学的原子、存放着数百万个三角形的网格、艺术家真正编辑的光滑曲线与曲面、以及光线追踪偏爱的隐式曲面。对同一个问题——你如何把一个形状写下来?——两种彼此竞争的答案,铺垫了下游的一切。
Five moves. (1) Earn the triangle as graphics' atom — always planar, always convex, its interior trivially defined, every attribute interpolating linearly across it (barycentric, previewing lessons 04–05), and hardware built for it. (2) Store many of them: the mesh — vertex buffer + index buffer, per-vertex attributes, face vs vertex normals, winding, and manifoldness. (3) Author smooth: cubic Béziers via Bernstein / de Casteljau, C0/C1/C2 continuity, why splines are piecewise, subdivision surfaces — then tessellate to triangles. (4) Flip representations: implicit surfaces & SDFs, f(x)=0, cheap inside/outside and normals-by-gradient, CSG by min/max — the explicit-vs-implicit tradeoff. (5) Confront the cost: level of detail — more triangles are smoother but pricier, so tessellation is adaptive. The widget makes move (3) concrete: how many straight segments does a curve need before it looks smooth?
五步走。(1) 把三角形挣成图形学的原子——恒为平面、恒为凸、内部平凡可判、任意属性在其上线性插值(重心坐标,预告第 04–05 课),而硬件就是为它造的。(2) 存放许多三角形:网格——顶点缓冲 + 索引缓冲、逐顶点属性、面法线与顶点法线、绕序、以及流形性。(3) 编辑光滑:用 Bernstein / de Casteljau 的三次贝塞尔、C0/C1/C2 连续性、为什么样条是分段的、细分曲面——然后再细分成三角形。(4) 翻转表示:隐式曲面与 SDF,f(x)=0、廉价的内外判定与用梯度求法线、用 min/max 做 CSG——显式与隐式的取舍。(5) 直面代价:细节层级——三角形越多越光滑但越贵,所以曲面细分是自适应的。控件把第 (3) 步变具体:一条曲线要多少直线段才看起来光滑?
1 · Why the triangle is graphics' atom为什么三角形是图形学的原子
Lesson 02 spoke casually of "a triangle at depth Z." It could just as easily have said a square, a pentagon, or a disc — so why does the entire pipeline privilege the triangle? Four properties, and none of them is arbitrary. They compound into the single reason GPUs draw triangles and nothing else.
第 02 课随口说到“深度 Z 处的一个三角形”。它本可以同样轻松地说正方形、五边形或圆盘——那么为什么整条流水线独独偏爱三角形?四条性质,没有一条是随意的。它们叠加起来,构成 GPU 只画三角形、别的什么都不画的那唯一理由。
- Always planar. Three points define a plane, exactly. A quadrilateral's four corners need not be coplanar — bend one vertex out of the plane and "the surface" is ambiguous (which way does the middle bulge?). A triangle can never be non-planar, so its geometry is unambiguous no matter how you transform it.恒为平面。三点恰好确定一个平面。四边形的四个角未必共面——把一个顶点掰出平面,“这个面”就含糊了(中间往哪边鼓?)。三角形永远不可能非平面,所以无论你怎么变换它,它的几何都毫不含糊。
- Always convex. A triangle has no dents; the straight segment between any two interior points stays inside. Convexity makes "is this pixel inside?" a trivial test — the point is inside iff it lies on the same side of all three edges (the edge functions of lesson 04). A general polygon can be concave, and inside/outside becomes a winding-number headache.恒为凸。三角形没有凹陷;任意两内点之间的直线段都留在内部。凸性让“这个像素在里面吗?”成为平凡的判断——点在内部当且仅当它在三条边的同一侧(第 04 课的边函数)。一般多边形可以是凹的,内外判定就变成绕数带来的麻烦事。
- Attributes interpolate linearly. Give the three corners a color, a normal, or a UV; every interior point is a unique weighted blend of the three via its barycentric coordinates (α, β, γ) with α+β+γ=1. This is what makes smooth shading and texturing possible (lessons 05, 07), and three weights summing to one is only well-defined for three points — a quad has no such canonical interpolation.属性线性插值。给三个角赋一个颜色、一个法线或一个 UV;每个内点都是这三者经其重心坐标 (α, β, γ)(满足 α+β+γ=1)唯一确定的加权混合。这正是光滑着色与纹理映射得以可能的原因(第 05、07 课),而“三个权重之和为一”只对三个点良定义——四边形没有这种规范的插值。
- Hardware is built for it. Because the three above are guaranteed, a GPU's rasterizer, interpolators, and clippers are all specialized to the triangle. Feeding it triangles is the fast path; everything else is emulated on top.硬件就是为它造的。正因为上面三条有保证,GPU 的光栅化器、插值器与裁剪器都专门针对三角形。喂它三角形就是快速路径;其余一切都是在此之上模拟出来的。
The clincher: any polygon decomposes into triangles, so nothing is lost by standardizing on them. A convex n-gon fans into n−2 triangles from one vertex; a general polygon needs a real triangulation, but the result is still triangles. So the pipeline can accept quads or n-gons for authoring convenience and quietly triangulate before drawing.
决定性的一点:任何多边形都可以分解成三角形,所以以三角形为标准并无损失。一个凸 n 边形从某一顶点扇形分成 n−2 个三角形;一般多边形需要真正的三角剖分,但结果仍是三角形。于是流水线可以为了编辑方便接受四边形或 n 边形,再在绘制前悄悄三角化。
2 · The mesh data structure — vertices, indices, normals, winding网格数据结构——顶点、索引、法线、绕序
A single triangle is the atom; a mesh is how we store millions of them. The naive layout — list every triangle as three full vertices — is wasteful, because in a closed surface each vertex is shared by roughly six triangles, so its position would be duplicated six times. The fix is an indexed mesh: one vertex buffer holds each unique vertex once, and one index buffer holds triangles as triples of integer indices into it.
单个三角形是原子;网格是我们存放数百万个三角形的方式。朴素布局——把每个三角形列成三个完整的顶点——很浪费,因为在一个封闭曲面里每个顶点大约被六个三角形共享,于是它的位置会被复制六遍。修复办法是索引网格:一个顶点缓冲把每个唯一顶点只存一次,一个索引缓冲把三角形存成指向它的整数索引三元组。
// Indexed mesh: unique vertices once, triangles as index triples.
positions = [ (0,0,0), (1,0,0), (1,1,0), (0,1,0), ... ] // one per unique vertex
indices = [ 0,1,2, 0,2,3, ... ] // 3 ints per triangle
// A vertex shared by 6 triangles is stored ONCE, referenced 6 times.
// Memory: ~12 bytes/vertex + 6 bytes/triangle, vs 36 bytes/triangle unindexed.
Each vertex carries a bundle of attributes, not just a position: a normal (for lighting, lesson 08), a UV texture coordinate (lesson 07), often a tangent (for normal mapping). The rasterizer interpolates each attribute across the triangle using the barycentric weights of section 1. The single most important derived attribute is the normal, and there is a subtlety worth pinning down. The face normal — one per triangle, pointing out of its plane — is a cross product of two edge vectors:
每个顶点携带一束属性,不止一个位置:一个法线(用于光照,第 08 课)、一个UV 纹理坐标(第 07 课)、常常还有一个切线(用于法线贴图)。光栅化器用第 1 节的重心权重把每个属性跨三角形插值。最重要的派生属性是法线,其中有个值得钉死的微妙之处。面法线——每个三角形一个、指向其平面之外——是两条边向量的叉积:
A face normal is constant over the whole triangle, so shading with it gives each facet a single flat tone — correct for a genuinely faceted object (a cut gem), but wrong for a sphere approximated by triangles, where it produces visible flat patches. The alternative is a vertex normal: at each vertex, average the face normals of all triangles meeting there and store the result per vertex. Now the rasterizer interpolates the normal smoothly across the triangle, and a coarse mesh looks curved. This is the flat-vs-smooth distinction, and it is purely a choice of where normals live — per face or per vertex.
面法线在整个三角形上是常量,所以用它着色会让每个小平面呈现单一的平坦色调——对一个真正有棱面的物体(一颗切割过的宝石)是正确的,但对一个用三角形近似的球就错了,那会产生可见的平面斑块。替代做法是顶点法线:在每个顶点处,把交汇于此的所有三角形的面法线平均,逐顶点存下结果。现在光栅化器把法线跨三角形平滑插值,一个粗糙网格就看起来是弯曲的。这就是平坦与光滑之别,而它纯粹是“法线放在哪”的选择——逐面还是逐顶点。
Two more properties keep a mesh sane. Winding order is the direction the three indices circulate — counter-clockwise (CCW) as seen from outside is the standard "front face." It tells the rasterizer which side you are looking at, so it can back-face cull (skip triangles facing away, roughly half of a closed object — a free ~2× on many scenes). Consistent winding also defines a consistent outward normal via the cross-product order above. Manifoldness is the stronger condition that every edge is shared by exactly two triangles and the surface has a clean inside/outside — required by many algorithms (subdivision, boolean operations, physics). A mesh with holes or T-junctions is non-manifold and breaks them.
还有两条性质让网格保持理智。绕序是三个索引循环的方向——从外部看逆时针(CCW)是标准的“正面”。它告诉光栅化器你正看着哪一侧,于是它能背面剔除(跳过背对你的三角形,一个封闭物体大约占一半——在许多场景里白赚约 2 倍)。一致的绕序还通过上面的叉积顺序定义了一致朝外的法线。流形性是更强的条件:每条边恰好被两个三角形共享,且曲面有干净的内外之分——许多算法(细分、布尔运算、物理)都要求它。带洞或有 T 形接缝的网格是非流形的,会让这些算法失效。
Two meshes that render identically at rest can behave completely differently once lit or culled. Inconsistent winding flips normals on some triangles, so back-face culling erases the wrong faces and the object looks holed; averaging vertex normals across a hard edge (a cube corner that should stay sharp) smears the crease into a soft blur. The fix is to duplicate vertices along intended hard edges so the crease has two normals, not one averaged mush — the reason a game-ready cube has 24 vertices, not 8.
两个静止时渲染一模一样的网格,一旦被点亮或剔除,行为可能截然不同。不一致的绕序会翻转部分三角形的法线,于是背面剔除抹掉了错误的面,物体看起来像有洞;跨一条硬边(本该保持锐利的立方体棱角)平均顶点法线,会把折痕抹成柔和的模糊。修复办法是沿着预期的硬边复制顶点,让折痕有两个法线、而非一团被平均的糊——这正是一个游戏可用的立方体有 24 个顶点、而非 8 个的原因。
3 · Smooth curves & surfaces — Béziers, continuity, subdivision光滑曲线与曲面——贝塞尔、连续性、细分
Triangles are flat, but the world is not — a car body, a font glyph, a character's cheek are smooth. Authoring those as raw triangles would be agony, so artists work with parametric curves and surfaces defined by a few control points, and the machine tessellates them to triangles at the last moment. The workhorse curve is the cubic Bézier, four control points P0..P3 blended by the cubic Bernstein polynomials as the parameter t runs 0→1:
三角形是平的,可世界不是——一辆车的车身、一个字体字形、一个角色的脸颊,都是光滑的。把它们直接当作三角形来编辑将是折磨,所以艺术家用由少数控制点定义的参数曲线与曲面工作,机器在最后一刻才把它们细分成三角形。主力曲线是三次贝塞尔,四个控制点 P0..P3 随参数 t 从 0→1 变化,由三次 Bernstein 多项式混合:
The four weights are always non-negative and sum to 1, so the curve is a weighted average of the control points and never escapes their convex hull. It passes through the endpoints (B(0)=P0, B(1)=P3) but only leans toward the interior handles P1, P2, which set the entry and exit tangents. An equivalent, more geometric evaluation is de Casteljau's algorithm: repeatedly linearly interpolate — lerp adjacent control points by t, giving 3 points, then 2, then 1; that last point is B(t). It is just nested lerps, numerically stable, and it also hands you the tangent and the two sub-curves for free (useful for adaptive tessellation).
这四个权重恒非负且和为 1,所以曲线是控制点的加权平均,绝不逃出它们的凸包。它经过端点(B(0)=P0, B(1)=P3),但只朝内部把手 P1, P2 倾斜,这两个把手设定了进入与离开的切线。一个等价、更几何化的求值是de Casteljau 算法:反复做线性插值——把相邻控制点按 t 做 lerp,得到 3 个点,再 2 个,再 1 个;最后那个点就是 B(t)。它无非是嵌套的 lerp、数值稳定,还顺带免费给你切线与两条子曲线(对自适应细分有用)。
Why cubic, and why stitch many short ones instead of one high-degree curve? A single polynomial through many points must have high degree, and high-degree polynomials wiggle wildly between their control points (Runge's phenomenon) and give every point global control — nudge one handle and the whole curve writhes. So we use splines: many low-degree (usually cubic) pieces joined end to end, giving local control and predictable shape. The only question is how smoothly the pieces meet, measured by continuity:
为什么是三次,又为什么拼接许多短段而不是一条高次曲线?一条穿过众多点的单一多项式必然次数很高,而高次多项式在其控制点之间剧烈摆动(Runge 现象),并让每个点都具有全局控制——轻推一个把手,整条曲线就扭动。所以我们用样条:许多低次(通常三次)段首尾相接,带来局部控制与可预测的形状。唯一的问题是这些段接得多光滑,由连续性衡量:
| Continuity | Meaning at the join | Looks like |
|---|---|---|
| C0 | positions match | connected, but a visible corner (kink) |
| C1 | positions + tangents (velocity) match | smooth direction, no kink |
| C2 | positions + tangents + curvature match | no visible change in bending — needed for reflective surfaces & camera paths |
| 连续性 | 接点处含义 | 看起来 |
|---|---|---|
| C0 | 位置相同 | 连上了,但有可见的尖角(折痕) |
| C1 | 位置 + 切线(速度)相同 | 方向光滑,无折痕 |
| C2 | 位置 + 切线 + 曲率相同 | 弯曲程度无可见跳变——反光曲面与相机路径需要它 |
The same idea lifts to surfaces. One route is a tensor-product patch (a grid of control points, Bézier in two parameters). The route that dominates production is subdivision surfaces: start from a coarse control mesh and repeatedly refine — split each face and reposition points by a weighted average of neighbors. Catmull–Clark is the standard scheme; each round quadruples the faces and the mesh converges to a smooth limit surface. Artists sculpt a low-poly cage; the renderer subdivides to the needed smoothness. Either way, the authored surface is smooth, and it is tessellated to triangles at runtime — which is exactly the approximation the widget below quantifies on a single curve.
同样的思想上升到曲面。一条路径是张量积面片(一格控制点网格,在两个参数上做贝塞尔)。在生产中占主导的路径是细分曲面:从一个粗糙的控制网格出发,反复精化——切分每个面,并按邻居的加权平均重新定位各点。Catmull–Clark 是标准方案;每一轮面数翻四倍,网格收敛到一个光滑的极限曲面。艺术家雕刻一个低面数的笼子;渲染器细分到所需的光滑度。无论哪种方式,编辑出的曲面都是光滑的,并在运行时被细分成三角形——这恰是下面控件在单条曲线上量化的那个近似。
Watch what the error does: it shrinks roughly like 1/N² for a smooth curve, so a handful of extra segments buys a big accuracy jump early, then diminishing returns — which is precisely why tessellation should be adaptive (section 5), spending segments only where the curve actually bends.
看误差如何变化:对光滑曲线它大致以 1/N² 收缩,所以早期多加寥寥几段就换来精度的大跳升,随后回报递减——这正是曲面细分应当自适应(第 5 节)的原因:只在曲线真正弯曲处才花段数。
4 · Implicit surfaces & SDFs — a shape as f(x)=0隐式曲面与 SDF——把形状写成 f(x)=0
Everything so far is explicit: geometry given as a list of points you can enumerate and draw. There is a completely different way to write down a shape — implicit: a function f over all of space, with the surface being the level set f(x)=0. You do not list the points; you get a rule that tests any point. The cleanest kind is a signed distance function (SDF), where f(x) returns the distance to the surface, negative inside, positive outside. A sphere of radius r centered at the origin is a one-liner:
目前为止的一切都是显式的:几何以一份你能枚举并绘制的点的列表给出。还有一种截然不同的写下形状的方式——隐式:一个定义在整个空间上的函数 f,曲面是其水平集 f(x)=0。你不列出这些点;你得到一条能测试任意点的规则。最干净的一种是有向距离函数(SDF),其中 f(x) 返回到曲面的距离,内部为负、外部为正。一个以原点为心、半径 r 的球是一行代码:
This representation is a gift to ray tracers. Inside/outside is just the sign of one evaluation — no triangle loop. The surface normal is the normalized gradient ∇f, which for a clean SDF you can get numerically from a few nearby samples — no stored per-vertex normals. And an SDF enables sphere tracing (lesson 10's ray-marching): from a point, the SDF value is a guaranteed-safe step distance toward the surface, so you leap along the ray in provably empty space and converge fast. Explicit meshes cannot offer any of this for free.
这种表示是给光线追踪的一份厚礼。内外判定只是一次求值的符号——没有三角形循环。曲面法线是归一化的梯度 ∇f,对干净的 SDF 你可以从附近几个采样数值地求得——无需存储逐顶点法线。而 SDF 使能球面追踪(第 10 课的光线步进):从一个点出发,SDF 的值是朝曲面迈出的一个保证安全的步长,于是你在可证明为空的空间里沿光线跳跃并快速收敛。显式网格无法免费提供这其中任何一样。
The other superpower is constructive solid geometry (CSG): combine shapes with pointwise operations on their SDFs. Union is min(fA, fB), intersection is max(fA, fB), and subtraction is max(fA, −fB). Drilling a hole through a block is one line; doing the same boolean on two triangle meshes is a notoriously fiddly, crack-prone algorithm. This is the crux of the tradeoff:
另一项超能力是构造实体几何(CSG):用对各自 SDF 的逐点运算来组合形状。并集是 min(fA, fB),交集是 max(fA, fB),差集是 max(fA, −fB)。在一块料上钻个洞就是一行;对两个三角形网格做同样的布尔运算,则是一个出了名地琐碎、易开裂的算法。这正是取舍的关键:
| Explicit (mesh) | Implicit (SDF) | |
|---|---|---|
| Shape is… | a list of triangles | a function f(x)=0 |
| Rasterize / draw on GPU | native, fast | must polygonize first (marching cubes) |
| Inside / outside test | expensive (ray casting) | trivial (sign of f) |
| Boolean ops (CSG) | fiddly, crack-prone | trivial (min / max) |
| Normals | stored per vertex | gradient ∇f, computed |
| 显式(网格) | 隐式(SDF) | |
|---|---|---|
| 形状是…… | 一份三角形列表 | 一个函数 f(x)=0 |
| 在 GPU 上光栅化 / 绘制 | 原生、快 | 必须先多边形化(marching cubes) |
| 内 / 外判定 | 昂贵(光线投射) | 平凡(f 的符号) |
| 布尔运算(CSG) | 琐碎、易开裂 | 平凡(min / max) |
| 法线 | 逐顶点存储 | 梯度 ∇f,计算得来 |
Neither wins outright, which is why real engines carry both and convert between them. To rasterize an implicit surface you must polygonize it — evaluate f on a grid and extract the f=0 crossing, the marching cubes algorithm (this course's lesson 10, and the same isosurface idea 3D-vision uses to turn a learned occupancy field into a mesh). Conversely you can bake a mesh into an SDF for collision or effects. The representation you pick follows the operation you need most: draw it fast → mesh; carve, blend, or query it → SDF.
两者都不完胜,所以真实引擎两者都带、并在它们之间转换。要光栅化一个隐式曲面,你必须把它多边形化——在一格网格上求 f、抽取 f=0 的穿越处,即 marching cubes 算法(本课第 10 课,也是三维视觉用来把学到的占据场变成网格的同一个等值面思想)。反过来,你也可以把网格烘焙成 SDF 用于碰撞或特效。你选哪种表示,取决于你最需要的操作:快速绘制 → 网格;雕刻、融合或查询 → SDF。
5 · Level of detail & the tessellation tradeoff细节层级与曲面细分的取舍
The widget showed the core tension on one curve, and it scales to whole scenes. Every smooth surface becomes triangles, and you choose how many. More triangles shrink the discretization error — the gap between the true surface and its flat approximation — so silhouettes get rounder and shading gets smoother. But every triangle costs vertex processing, and eventually you hit the point where a triangle is smaller than a pixel: pure cost, zero visible benefit, and a real efficiency cliff (the quad-overshading problem hinted at in lesson 04).
控件在一条曲线上展示了核心矛盾,它可以放大到整个场景。每个光滑曲面都变成三角形,而你选择用多少个。三角形越多,离散化误差——真实曲面与其平面近似之间的差距——越小,于是轮廓更圆、着色更平滑。但每个三角形都要花顶点处理,最终你会撞到某个点:三角形小过一个像素——纯粹是开销、零可见收益,还有一个真实的效率悬崖(第 04 课暗示的四元组过度着色问题)。
The resolution is to spend triangles only where they pay. Level of detail (LOD) swaps in a coarser mesh as an object recedes — a distant character covering 20 pixels needs nowhere near its close-up triangle count. Adaptive tessellation goes finer: subdivide more where the surface curves sharply or is near the camera or the silhouette, less where it is flat or far — driven by a screen-space error target (keep every chord under, say, one pixel), exactly the 1/N² error curve the widget traces, applied per patch. The guiding principle is the whole course in miniature: match the geometric budget to the visible benefit, and stop the instant the eye can no longer tell.
解决之道是只在划算处花三角形。细节层级(LOD)在物体远去时换上更粗的网格——一个只占 20 像素的远处角色,远不需要它特写时的三角形数量。自适应曲面细分走得更细:在曲面弯得急、或靠近相机或轮廓处细分得更多,在平坦或遥远处更少——由一个屏幕空间误差目标驱动(让每段弦都小于,比方说,一个像素),正是控件描出的那条 1/N² 误差曲线,逐面片地施用。指导原则是整门课的缩影:让几何预算匹配可见收益,一旦眼睛再也分辨不出就立即停手。
"More triangles always look better" is false past the sub-pixel point. When triangles get smaller than a pixel, the GPU still shades in 2×2 pixel quads, so a mesh of pixel-sized triangles can shade each pixel several times over — throughput collapses with no visible gain. Worse, tiny triangles alias badly. The right instinct is a target error in screen space, not world space: a mountain 3 km away and a teapot on the desk should both tessellate to roughly one-pixel chords, which means the mountain gets far fewer triangles.
过了亚像素点,“三角形越多越好看”就是错的。当三角形小过一个像素,GPU 仍以 2×2 像素的四元组着色,于是一个由像素大小三角形构成的网格会把每个像素着色好几遍——吞吐崩溃却毫无可见收益。更糟的是,微小三角形严重走样。正确的直觉是把误差目标定在屏幕空间、而非世界空间:3 公里外的一座山和桌上的一把茶壶,都应细分到大约一像素的弦,这意味着那座山得到的三角形要少得多。
Where this points next接下来指向何处
We now have the geometry the pipeline moves: the triangle as its atom, the indexed mesh with its normals and winding, the smooth curves and surfaces artists author, and the implicit shapes a ray tracer loves — all ultimately tessellated to triangles under a budget. Lesson 02 gave us the transform chain to move a vertex to the screen; this lesson gave us the vertices to move. What remains is to actually draw them: take a triangle sitting in screen space and decide which pixels it covers and what color each gets. That is rasterization — lesson 04 finally runs, for real, the triangle-outer loop we sketched in lesson 01, using the edge functions and barycentric interpolation this lesson kept promising. Every property of the triangle we earned in section 1 is about to pay off in hardware.
现在我们有了流水线要搬运的几何:作为原子的三角形、带法线与绕序的索引网格、艺术家编辑的光滑曲线与曲面、以及光线追踪偏爱的隐式形状——它们最终都在预算下被细分成三角形。第 02 课给了我们把顶点搬到屏幕的变换链;这一课给了我们要搬运的顶点。剩下的是真正去画它们:拿一个躺在屏幕空间的三角形,判定它覆盖哪些像素、每个像素得到什么颜色。那就是光栅化——第 04 课终于真刀真枪地跑起第 01 课勾勒过的“三角形外层”循环,用上这一课一直预告的边函数与重心插值。第 1 节我们挣得的三角形的每一条性质,都即将在硬件里兑现。
The triangle is graphics' atom because it is always planar and convex, its interior is a trivial inside test, any attribute interpolates linearly across it by barycentric weights, hardware is specialized to it, and any polygon decomposes into triangles. A mesh stores many via a vertex buffer + index buffer (indices deduplicate shared vertices); the face normal is normalize((v1−v0)×(v2−v0)), and putting normals per-face gives flat shading while per-vertex (averaged) gives smooth; winding (CCW front) drives back-face culling. Smooth shapes are authored as cubic Béziers B(t)=(1−t)³P0+3(1−t)²t P1+3(1−t)t² P2+t³P3, joined into piecewise splines (local control, C0/C1/C2 continuity) or grown by subdivision (Catmull–Clark) — then tessellated to triangles. Implicit surfaces / SDFs write a shape as f(x)=0 (sphere: |p|−r): trivial inside/outside, gradient normals, easy CSG by min/max — but must be polygonized to rasterize. Explicit = easy to draw, hard to boolean; implicit = easy to boolean, must be meshed. Finally, more triangles cut discretization error but cost throughput, so LOD and adaptive tessellation target a screen-space error and stop at the sub-pixel point.
三角形是图形学的原子,因为它恒为平面与凸、内部是一个平凡的内外判定、任意属性可经重心权重在其上线性插值、硬件专门为它优化、且任何多边形都能分解成三角形。网格经由顶点缓冲 + 索引缓冲存放许多三角形(索引对共享顶点去重);面法线为 normalize((v1−v0)×(v2−v0)),把法线放逐面得到平坦着色,逐顶点(平均)则得到光滑;绕序(CCW 为正面)驱动背面剔除。光滑形状被编辑为三次贝塞尔 B(t)=(1−t)³P0+3(1−t)²t P1+3(1−t)t² P2+t³P3,拼成分段样条(局部控制,C0/C1/C2 连续性)或由细分(Catmull–Clark)生长——然后细分成三角形。隐式曲面 / SDF 把形状写成 f(x)=0(球:|p|−r):平凡的内外判定、梯度法线、用 min/max 轻松做 CSG——但要光栅化必须先多边形化。显式 = 易绘制、难布尔;隐式 = 易布尔、须成网格。最后,三角形越多越能削减离散化误差却耗吞吐,所以 LOD 与自适应细分瞄准一个屏幕空间误差,并在亚像素点停手。
Interview prompts面试题
- Why is the triangle the primitive of choice over quads or n-gons? (§1 — always planar (3 pts = 1 plane) and convex, trivial inside test, unique barycentric interpolation of attributes, hardware-specialized; any polygon decomposes into triangles anyway.) 为什么三角形是首选图元,而非四边形或 n 边形?(§1 — 恒为平面(3 点 = 1 平面)且凸、内外判定平凡、属性有唯一的重心插值、硬件专门优化;何况任何多边形都能分解成三角形。)
- How do you compute a triangle's face normal, and what is the difference between flat and smooth shading? (§2 — normalize((v1−v0)×(v2−v0)); flat = one normal per face (faceted), smooth = per-vertex normals averaged from adjacent faces and interpolated.) 如何计算一个三角形的面法线?平坦着色与光滑着色有何区别?(§2 — normalize((v1−v0)×(v2−v0));平坦 = 每面一个法线(棱面感),光滑 = 从相邻面平均得到的逐顶点法线并插值。)
- What does winding order encode, and why does back-face culling depend on it? (§2 — the circulation of the indices (CCW = front) sets which side faces the viewer; a back-facing triangle can be skipped, ~halving work, but only if winding is consistent.) 绕序编码了什么,为什么背面剔除依赖它?(§2 — 索引的循环方向(CCW = 正面)确定哪一侧朝向观察者;背对的三角形可被跳过、约省一半工作,但前提是绕序一致。)
- Define C0, C1, C2 continuity, and why are splines built from many low-degree pieces? (§3 — position / +tangent / +curvature match at joins; piecewise cubics give local control and avoid the wild oscillation of one high-degree polynomial.) 定义 C0、C1、C2 连续性,为什么样条由许多低次段构成?(§3 — 接点处位置 / +切线 / +曲率相同;分段三次曲线带来局部控制,并避免单一高次多项式的剧烈振荡。)
- Contrast an explicit mesh with an implicit SDF — one operation each is easy at, one hard. (§4 — mesh: native to rasterize, but booleans are crack-prone; SDF: trivial inside/outside & CSG by min/max & gradient normals, but must be polygonized (marching cubes) to draw.) 对比显式网格与隐式 SDF——各自擅长一种操作、又各有一种为难。(§4 — 网格:光栅化是原生的,但布尔运算易开裂;SDF:内外判定平凡、用 min/max 做 CSG、梯度法线,但要绘制须多边形化(marching cubes)。)
- Is "more triangles is always better"? Explain the LOD / tessellation tradeoff. (§5 — no; triangles cut discretization error but cost vertex work, and past sub-pixel size they only add cost and aliasing, so target a screen-space error via LOD / adaptive tessellation.) “三角形越多越好”对吗?解释 LOD / 曲面细分的取舍。(§5 — 不对;三角形削减离散化误差却耗顶点处理,过了亚像素大小只增开销与走样,故应经 LOD / 自适应细分瞄准一个屏幕空间误差。)