all lessons / computer_graphics / 01 · what is rendering lesson 1 / 15

What is rendering? The forward problem & two visibility strategies什么是渲染?正向问题与两种可见性策略

The index framed graphics as four things — a scene, light transport, visibility, and a budget. This first lesson earns the frame. We define rendering as the exact inverse of vision, build the virtual camera that makes an image a measurement, write the one equation the whole track approximates, and then discover that "what is visible?" has two answers whose loops are transposes of each other — the fork that splits the rest of the field into rasterization and ray tracing. 目录把图形学归结为四件事——场景、光的传输可见性、以及预算。这第一课要把这个框架挣出来。我们把渲染定义为视觉的严格逆问题,搭起那台让图像成为一次测量的虚拟相机,写下整门课都在近似的那一个方程,然后发现“什么是可见的?”有两种答案,它们的循环互为转置——正是这个分岔,把余下的领域劈成了光栅化与光线追踪。

The plan本课计划

Five moves. (1) State the forward problem and why it is vision run backwards. (2) Build the virtual pinhole camera so "image" means something precise: samples of light arriving at a grid of sensors. (3) Write the rendering equation and read it, term by term — we will not solve it for eight more lessons, but everything is an approximation of it. (4) Confront visibility: the same question — "which surface is seen here?" — answered by two transposed loops, rasterization (for each triangle, which pixels?) and ray tracing (for each pixel, which triangle?). (5) See how the budget — milliseconds vs minutes — picks which loop, and set up lesson 02, which supplies the coordinates all of this runs in.

五步走。(1) 说清正向问题,以及它为何是“把视觉倒着做”。(2) 搭起虚拟针孔相机,让“图像”有精确含义:光到达一格格传感器时被采下的样本。(3) 写下渲染方程并逐项读它——再过八课我们才会去解它,但一切都是对它的近似。(4) 正面迎战可见性:同一个问题——“这里看到的是哪个表面?”——由两个互为转置的循环回答,光栅化(对每个三角形,覆盖哪些像素?)与光线追踪(对每个像素,命中哪个三角形?)。(5) 看预算——毫秒还是分钟——如何决定用哪个循环,并引出第 02 课,它提供这一切运行所在的坐标。

1 · Graphics is vision, run backwards图形学,就是把视觉倒着做

Computer vision starts with an image and asks what was out there: what objects, what depth, what motion. Computer graphics starts with a description of what is out there and asks what image would that produce. Same arrow, opposite direction. Vision is the inverse problem (hard, ambiguous, one image consistent with infinitely many worlds); graphics is the forward problem (well-posed — given the world, the image is determined by physics).

计算机视觉从一张图像出发,问外面本来有什么:什么物体、多深、如何运动。计算机图形学从“外面有什么”的描述出发,问那会产生怎样的图像。同一支箭,方向相反。视觉是逆问题(难、含糊,一张图像可以与无穷多个世界相容);图形学是正向问题(良定义——给定世界,图像由物理唯一确定)。

"Determined by physics" is the whole reason this track can be first-principles. We are not guessing; we are simulating a measurement. So the object we must define first is the measuring instrument. A rendered image is exactly what a camera placed inside the virtual scene would record. Pin down that camera and "make an image" stops being a vague art task and becomes a computation with a right answer.

“由物理唯一确定”正是这门课能讲第一性原理的根本原因。我们不是在猜;我们是在模拟一次测量。所以要先定义的对象,是那台测量仪器。一张渲染出的图像,恰好就是——把一台相机放进虚拟场景里——它会记录下来的东西。把这台相机钉死,“画一张图”就不再是含糊的美术活儿,而变成一个有正确答案的计算。

Vision · 视觉
image → scene (inverse, ill-posed)图像 → 场景(逆问题,病态)
Graphics · 图形学
scene → image (forward, well-posed)场景 → 图像(正向,良定义)
Shared object · 共同对象
the camera + the physics of light相机 + 光的物理

2 · The virtual camera: a pinhole and a grid of sensors虚拟相机:一个针孔加一格格传感器

The simplest camera that still obeys real optics is the pinhole: a single point (the eye / center of projection) and, behind it, an image plane tiled into little sensors — the pixels. Light travels in straight lines; a scene point is imaged by drawing the straight line from that point through the pinhole and seeing where it strikes the plane. That single rule gives perspective: things farther away project smaller.

仍然遵守真实光学、又最简单的相机,是针孔相机:一个点(眼睛 / 投影中心),以及在它后面、被铺成一格格小传感器的成像平面——也就是像素。光沿直线传播;一个场景点如何成像,就是从该点经针孔画一条直线、看它打在平面的哪里。仅此一条规则就给出了透视:越远的东西投影越小。

Put the pinhole at the origin looking down +Z, with the image plane at distance f (the focal length). A scene point (X, Y, Z) projects to plane coordinates by similar triangles:

把针孔放在原点、朝 +Z 看,成像平面在距离 f(焦距)处。由相似三角形,场景点 (X, Y, Z) 投影到平面坐标:

x′ = f · X / Z ,    y′ = f · Y / Z .

The important character here is that division by Z — the perspective divide. It is nonlinear (it mixes depth into the result), which is exactly why lesson 02 has to leave ordinary 3×3 linear algebra and adopt homogeneous coordinates: they let us defer that divide to the very end of a chain of otherwise-linear matrix steps. For now just hold the picture: a ray per pixel, fanning out from the eye through the grid into the scene.

这里的关键角色,是那个除以 Z——即透视除法。它是非线性的(把深度掺进了结果),这正是第 02 课必须离开普通的 3×3 线性代数、改用齐次坐标的原因:齐次坐标让我们能把这个除法推迟到一串本来线性的矩阵步骤的最末尾。眼下只需记住这幅画面:每个像素一条光线,从眼睛出发、穿过网格、扇形散入场景。

A rendered pixel is then a measurement: the amount and color of light arriving at that little sensor, along the bundle of rays it accepts. Note the word "little" — a pixel is an area, not a mathematical point, and pretending otherwise is the origin of aliasing (lesson 06). And "amount of light" is a physical quantity we have not defined yet — that is section 3.

于是一个渲染出的像素就是一次测量:沿它所接受的那束光线,到达那个小传感器的光的多少与颜色。注意“小”这个字——像素是一块面积,而不是一个数学上的点;假装它是点,正是走样(第 06 课)的源头。而“光的多少”是一个我们还没定义的物理量——那是第 3 节的事。

3 · The rendering equation — the whole field on one line渲染方程——整个领域浓缩成一行

Here is the destination of the entire track, stated once so every later lesson can point back to it. The light leaving a surface point x toward the eye, in direction ωo, is what the surface emits plus what it reflects — and what it reflects is an integral, over every incoming direction ωi on the hemisphere, of incoming light times how much this material bounces light from ωi into ωo:

这里是整门课的终点,先陈述一次,好让后面每一课都能回指它。从表面点 x 沿方向 ωo 离开、射向眼睛的光,等于该表面自身发射的光,加上它反射的光——而反射的部分是一个积分:在半球上每一个入射方向 ωi 上,把入射光乘以“这种材质把光从 ωi 弹向 ωo 的比例”,再积起来:

Lo(x, ωo) = Le(x, ωo) + ∫Ω fr(x, ωi, ωo) · Li(x, ωi) · (ωi · n) dωi
Read it as a budget map把它当成一张预算地图来读

Every technique in this course is a way to afford some piece of this integral. Ignore the recursion and evaluate fr once per light → local shading (lesson 08). Sample the integral with random rays → path tracing (lesson 11). Precompute it and store the result → lightmaps and probes (lesson 12). Keep the equation in view and the field stops looking like a pile of unrelated tricks.

这门课里的每一种技术,都是让这个积分的某一块负担得起的办法。忽略递归、每个光源只求一次 fr → 局部着色(第 08 课)。用随机光线去采样这个积分 → 路径追踪(第 11 课)。把它预计算好、把结果存起来 → 光照贴图与探针(第 12 课)。把这个方程一直摆在眼前,整个领域就不再像一堆互不相干的技巧了。

4 · Visibility: one question, two transposed loops可见性:一个问题,两个转置的循环

Before we can shade anything with that equation, we must answer a prior, purely geometric question: at each pixel, which surface is the eye actually looking at? Nearer surfaces hide farther ones. This is visibility, and it is where graphics forks. The scene is triangles; the image is pixels; visibility is a relationship between the two sets — and you can loop over the sets in either order.

在用那个方程给任何东西着色之前,必须先回答一个更靠前、纯几何的问题:在每个像素处,眼睛实际看到的是哪个表面?近的表面挡住远的。这就是可见性,也是图形学分叉之处。场景是三角形,图像是像素,可见性是这两个集合之间的一种关系——而你可以按任意顺序遍历这两个集合。

RASTERIZATION (outer loop = triangles) RAY TRACING (outer loop = pixels) ──────────────────────────────────── ────────────────────────────────── for each triangle T: for each pixel P: for each pixel P in T's bounding box: shoot a ray from eye through P if P inside T and T nearer: for each triangle T: framebuffer[P] = T, z[P] = depth if ray hits T and nearer: hit = T → "push" geometry onto the screen → "pull" the scene through each pixel → loves many pixels, few big triangles → loves many triangles, arbitrary rays

They compute the same visibility — the widget below proves it by producing an identical final image both ways. What differs is the loop nesting, and that changes everything downstream:

它们计算的是同一个可见性——下面的小控件会通过两种方式产出完全相同的最终图像来证明这一点。不同的只是循环的嵌套顺序,而这会改变下游的一切:

Same visibility, two loops — watch rasterization and ray tracing build the identical image同一个可见性,两种循环——看光栅化与光线追踪拼出完全相同的图像
Three overlapping triangles at different depths. Pick a strategy and scrub the step slider. Rasterization's outer loop is triangles (3 steps): each step stamps one whole triangle, depth-tested. Ray tracing's outer loop is pixels: each step resolves one more pixel by testing all triangles. The final image is byte-for-byte the same; only the work order and the operation counts differ.三个不同深度、彼此重叠的三角形。选一种策略,然后拖动步进滑块。光栅化的外层循环是三角形(3 步):每步盖上一整个三角形并做深度测试。光线追踪的外层循环是像素:每步通过测试所有三角形来解决多一个像素。最终图像逐字节相同,不同的只是工作顺序与操作次数。
Outer loop
triangles
Coverage / ray tests
Pixels resolved
Same final image?
yes ✓
Show the core JS查看核心 JS
// One point-in-triangle test via edge functions (previews lesson 04).
function edge(ax,ay,bx,by,px,py){ return (px-ax)*(by-ay) - (py-ay)*(bx-ax); }
function inside(tri, px, py){
  var w0 = edge(tri[1][0],tri[1][1], tri[2][0],tri[2][1], px,py);
  var w1 = edge(tri[2][0],tri[2][1], tri[0][0],tri[0][1], px,py);
  var w2 = edge(tri[0][0],tri[0][1], tri[1][0],tri[1][1], px,py);
  return (w0>=0 && w1>=0 && w2>=0) || (w0<=0 && w1<=0 && w2<=0);
}
// RASTER: outer = triangles, keep nearest via a z-buffer.
for (var t=0; t<tris.length; t++)
  for (var p=0; p<pixels.length; p++)
    if (inside(tris[t], px, py) && tris[t].z < zbuf[p]) { color[p]=tris[t].c; zbuf[p]=tris[t].z; }
// RAY: outer = pixels, test every triangle, keep nearest hit. Same result.
for (var p=0; p<pixels.length; p++)
  for (var t=0; t<tris.length; t++)
    if (inside(tris[t], px, py) && tris[t].z < best) { color[p]=tris[t].c; best=tris[t].z; }
Trap易错点

"Ray tracing is just better" is the wrong lesson. For a full screen of large triangles, rasterization's total work is roughly triangles × their own pixels, while naive ray tracing is pixels × all triangles — often far more. Rasterization dominated real-time rendering for decades precisely because its loop order matches GPU hardware. Ray tracing wins where its uniform rays buy you effects (shadows, reflections, GI) that rasterization can only fake — and only once a BVH tames the inner loop. The two are converging (lesson 15), not competing.

“光线追踪就是更好”是个错误结论。对于一屏幕的大三角形,光栅化的总工作量大致是三角形数 × 各自的像素数,而朴素光线追踪是像素数 × 全部三角形数——往往多得多。光栅化统治实时渲染数十年,恰恰因为它的循环顺序契合 GPU 硬件。光线追踪的胜场,在于它的一致光线能换来光栅化只能伪造的效果(阴影、反射、全局光照)——而且要等 BVH 驯服了内层循环之后。二者正在融合(第 15 课),而非彼此竞争。

5 · The budget decides — real-time vs offline预算说了算——实时 vs 离线

Nothing above tells you which approximation to use. The budget does. A game must finish a frame in about 16.7 ms (60 frames per second), so it rasterizes and fakes the expensive integral terms with precomputation and clever screen-space tricks. A film frame can take minutes to hours on a render farm, so it path-traces the equation almost literally and simply accepts the cost for correctness. Same rendering equation, opposite ends of the budget axis — and most of this course is a tour of the approximations that live between those poles.

上面这些都没告诉你该用哪种近似。是预算说了算。游戏必须在大约 16.7 毫秒 内画完一帧(每秒 60 帧),所以它用光栅化,并用预计算和聪明的屏幕空间技巧去伪造那些昂贵的积分项。一帧电影画面可以在渲染农场上花 几分钟到几小时,所以它几乎照字面地对方程做路径追踪,为了正确性而干脆接受这份开销。同一个渲染方程,处在预算轴的两端——而这门课的大部分,就是一趟游历于两极之间那些近似的旅程。

AxisReal-time (games, AR)Offline (film, design)
Budget / frame~1–16 msseconds to hours
Visibilityrasterization (+ some hardware rays)ray / path tracing
Global illuminationfaked: probes, lightmaps, SSAO/SSRsimulated: Monte Carlo path tracing
Guiding metricframe time, "good enough"convergence to the true integral
维度实时(游戏、AR)离线(电影、设计)
每帧预算约 1–16 毫秒数秒到数小时
可见性光栅化(+ 部分硬件光线)光线 / 路径追踪
全局光照伪造:探针、光照贴图、SSAO/SSR模拟:蒙特卡洛路径追踪
指导性指标帧时间、“够好就行”收敛到真实积分

Where this points next接下来指向何处

We now have the frame: rendering is a forward measurement by a virtual camera, defined exactly by the rendering equation, and split by how we answer visibility under a budget. But every sentence above quietly assumed we could place a triangle "at depth Z," project it through the pinhole, and know which pixels it covers. None of that works without a coordinate system and a way to move points between spaces — object space, world space, the camera's view, and the screen. Lesson 02 builds exactly that machine: homogeneous coordinates and the transform chain, including the deferred perspective divide we met in section 2. It is the algebra that makes both loops in the widget above actually runnable.

我们现在有了框架:渲染是虚拟相机做的一次正向测量,由渲染方程精确定义,并按“在预算下如何回答可见性”而分叉。但上面每一句都悄悄假设了:我们能把一个三角形放“在深度 Z 处”、经针孔投影它、并知道它覆盖哪些像素。没有坐标系、没有在各空间之间搬运点的办法,这些都行不通——物体空间、世界空间、相机视角、屏幕。第 02 课就来造这台机器:齐次坐标与变换链,其中就包括第 2 节遇到的那个被推迟的透视除法。正是这套代数,让上面控件里的两个循环真的能跑起来。

Takeaway要点

Rendering is the forward problem: simulate the image a virtual camera would measure — the inverse of computer vision, and well-posed because physics determines the answer. That camera is a pinhole, so pixels are samples of light and depth enters through the nonlinear perspective divide (x′ = fX/Z). The one equation the whole track approximates is the rendering equation, Lo = Le + ∫ fr Lii·n) dωi — recursive, which is why global illumination is hard. Before shading, we must solve visibility, and it has two transposed answers computing the same result: rasterization (loop triangles, keep nearest with a z-buffer — GPU-friendly, real-time) and ray tracing (loop pixels, shoot uniform rays — natural shadows/reflections/GI, needs a BVH). Which you pick is set by the budget: milliseconds → rasterize and fake; minutes → path-trace and converge.

渲染是正向问题:模拟虚拟相机会测到的图像——是计算机视觉的逆,并且因为物理决定了答案而良定义。那台相机是针孔,所以像素是光的样本,而深度通过非线性的透视除法进入(x′ = fX/Z)。整门课都在近似的那一个方程是渲染方程Lo = Le + ∫ fr Lii·n) dωi——它是递归的,这就是全局光照难的原因。着色之前,必须先解可见性,它有两个互为转置、算出相同结果的答案:光栅化(遍历三角形、用深度缓冲保留最近者——对 GPU 友好、实时)与光线追踪(遍历像素、发射一致的光线——天然的阴影/反射/全局光照,需要 BVH)。选哪个由预算决定:毫秒 → 光栅化 + 伪造;分钟 → 路径追踪 + 收敛。

Interview prompts面试题