# 一条光线的旅程：光线追踪凭什么让画面“突然变真”

从像素出发发射光线、与场景求交、递归追踪反射/折射/阴影——讲清这套"反向追踪"为什么能自然产生真实光影

> Whitted 光线追踪 · 渲染原理 · 全局光照 · 约 6 分钟 · 07 月 09 日

## 本篇要点

1. 光线追踪从像素出发、向场景发射主光线，两层循环（外层像素、内层物体）正好与光栅化相反。
2. 在交点处递归发射反射光线、折射光线和阴影光线——这是 Whitted 1980 年论文的核心创新，让跨物体光学效果自然出现。
3. 反射、折射、阴影在光线追踪里是同一物理模拟过程的副产品，不需要光栅化那种针对每种效果的单独 hack。
4. 代价是计算量巨大：每条光线都要和场景中所有三角形做相交测试，直到近年硬件加速才走进实时游戏。

---

上一篇我们聊了光栅化——它快，是因为"每个三角形各画各的"。但阴影、反射、折射这些跨物体的光学效果，它天生做不好，只能靠各种 hack 勉强糊弄。

那光线追踪是怎么解决这个问题的？答案很简单：**把整个流程翻过来。**

光栅化是从物体出发，问"这个三角形覆盖了哪些像素"；光线追踪是从像素出发，问"这个像素看到了什么"。

这一篇，我们就跟着一条光线，走完它从相机到场景的完整旅程。

## 从像素出发：发射一条"视线"

想象你站在一个房间的窗前，透过窗户看外面的世界。你看到某个点上的颜色，是因为从那个点出发的光线穿过了窗户、进入了你的眼睛。

光线追踪"作弊"了——它反着走：**从你的眼睛出发，穿过窗户上每一个小格子（像素），射向场景，看它撞到什么东西。**

具体来说，对于屏幕上 1920×1080 个像素中的每一个，光线追踪做三件事（[1][2]）：

1. 从相机位置发出一条射线，穿过这个像素的中心，射向场景
2. 找到这条射线在场景中**最先碰到**的物体
3. 根据这个碰点的材质、光照等信息，算出这个像素的颜色

这条从相机出发的射线叫**主光线（primary ray）**。它的方向很好算：从相机位置到像素中心连一条线，延伸出去就是。

用伪代码表示就是：

```
for (每个像素 in 屏幕) {
    发射一条主光线穿过这个像素的中心
    for (每个物体 in 场景) {
        检测光线是否与这个物体相交
    }
    取最近的交点，计算颜色，写入像素
}
```

![光线追踪的基础示意：从相机位置通过像素发射光线进入场景](https://www.scratchapixel.com/images/ray-tracing-refresher/rt-setup.png)

两层循环——外层是像素，内层是场景中所有物体——正好和光栅化反过来。[1]

## 找到"最先碰到"的那一个

一条光线射出去，可能穿过好几个物体——比如先穿过一个玻璃杯，再撞到后面的墙。但每个像素只能显示离相机**最近**的那个物体的颜色。

所以光线追踪在做相交测试时，不是"碰到第一个就算"，而是**遍历场景中所有物体，记录下最近的交点**。这个"最近"的判定，靠的是光线参数方程里的参数 $t$：

$$P(t) = O + t \cdot D$$

其中 $O$ 是光线起点，$D$ 是方向，$t$ 是沿着光线走的距离。$t$ 越小，交点越近。遍历所有物体后，取 $t$ 最小的那个交点，就是像素真正应该显示的东西。[3]

这个机制和光栅化里的深度缓冲（z-buffer）做的事一样——解决"谁在前面"的问题。但光线追踪用一个统一的框架就能搞定，不需要额外的深度缓冲。

## 在交点处"着色"：本地光照

找到交点之后，要算出这个点应该是什么颜色。这涉及三个因素（[2][4]）：

- 物体的材质（颜色、粗糙度、是否反光等）
- 光源的位置和强度
- 观察者的方向

最简单的着色模型是**Phong 模型**，它把光照分成三部分：环境光（ambient，一个常数，让暗部不至于全黑）、漫反射（diffuse，粗糙表面的散射）、镜面高光（specular，光滑表面的亮斑）。

但到这里为止，光线追踪还没有展现出比光栅化高明的地方——上面的计算，光栅化也能做。

真正的杀手锏，在下一步。

## 递归追踪：反射、折射、阴影

这是 Whitted 在 1980 年那篇经典论文里引入的核心思想（[5]）：**在交点处，不只算本地光照，还要继续发射新的光线，去"问"周围的环境。**

具体来说，从交点出发会发射三种**次级光线（secondary rays）**：

**反射光线（reflection ray）**：如果物体表面是光滑的（比如镜子、金属、水面），就在交点处按反射定律弹出一条新光线，继续追踪它撞到什么。那条新光线的颜色，会成为这个反射点颜色的一部分。

**折射/透射光线（transmission/refraction ray）**：如果物体是透明的（比如玻璃、水），就按斯涅尔定律算出光线穿过物体后的方向，继续追踪。这就能产生玻璃球后面物体扭曲变形的效果。

**阴影光线（shadow ray）**：从交点向每一个光源发射一条光线。如果这条光线在半路被其他物体挡住了，说明这个交点处于阴影中——光源的贡献就不计算。[2][5]

这些次级光线自己又会碰到新的物体，在那些新交点上继续发射次级光线……**递归下去**，直到达到设定的最大深度（比如 5 层、10 层），或者光线射出了场景。

![主光线与次级光线（反射、折射、阴影）的递归追踪示意](https://www.scratchapixel.com/images/ray-tracing-refresher/rt-look.png)

## 为什么这能"自然产生"真实光影？

对比一下光栅化和光线追踪，你就明白了。

在光栅化里，要做一个镜面反射，程序员得额外渲染一张"环境贴图"贴在镜子上——而且这张图是预先烘焙好的，场景里动态移动的物体反射不出来。

但在光线追踪里，反射是**自然发生**的：主光线打到镜面，反射光线弹出去，撞到场景里的另一个物体——那个物体的颜色就直接被反射回来了。不需要预烘焙，不需要额外 hack，场景里任何物体动了，反射内容自动跟着更新。[1][2]

同理，阴影在光线追踪里就是"从交点向光源射一条光线，看它有没有被挡住"——简单、直接、物理正确。玻璃折射也是，光线穿过玻璃时按折射定律偏折，再在另一面继续追踪，扭曲效果自然产生。

这就是为什么光线追踪渲染的画面会有那种"突然变真"的感觉：**阴影不是硬邦邦的一团黑，反射不是贴上去的假图片，玻璃不是半透明的塑料片。** 所有效果都是同一个物理模拟过程的自然产物。

## 那代价呢？

代价就是计算量，而且是巨大的计算量。

想想看：每个像素要发射一条主光线，每条主光线如果打到反射面，又要发射反射光线，反射光线再打到物体，可能还要发射阴影光线……场景有 100 万个三角形，每一条光线都要和这些三角形做相交测试。[3]

1980 年 Whitted 用他的算法渲染一张图，分辨率 512×512，花了**好几十分钟**。直到 2018 年英伟达推出 RTX 系列显卡，才第一次让光线追踪在实时游戏里跑起来——靠的是专门为光线/三角形相交测试设计的硬件加速单元。

但那是下一篇的内容了。今天你只需要记住光线追踪的核心理念：**从像素出发，沿着光线的路径反向追踪，在每一个交点问"你看到了什么"，然后递归地追问下去。** 这种"一条光线的旅程"，让阴影、反射、折射这些跨物体的光学效果，从"需要额外 hack 的难题"变成了"自然发生的副产品"。

## 来源

1. [Scratchapixel — Overview of the Ray-Tracing Rendering Technique，含伪代码、主光线/次级光线定义、循环结构对比](https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-overview/ray-tracing-rendering-technique-overview.html)
2. [PBRT 4th Edition — Photorealistic Rendering and the Ray-Tracing Algorithm，涵盖相机模型、求交、阴影光线、递归追踪](https://pbr-book.org/4ed/Introduction/Photorealistic_Rendering_and_the_Ray-Tracing_Algorithm)
3. [Scratchapixel — Ray-Tracing: Generating Camera Rays，光线的参数方程定义与主光线生成](https://www.scratchapixel.com/lessons/3d-basic-rendering/ray-tracing-generating-camera-rays/definition-ray.html)
4. [UC Berkeley CS184 — Intro to Raytracing 课件，光线追踪算法基础、着色、递归追踪](https://cs184.eecs.berkeley.edu/su25/assets/lectures/09-11-ray-tracing+acceleration.pdf)
5. [Whitted 1980 — An Improved Illumination Model for Shaded Display，首次提出递归光线追踪与反射/折射/阴影光线的经典论文](https://www.cs.rpi.edu/~cutler/classes/advancedgraphics/S11/papers/whitted_raytracing_80.pdf)

---

原文：https://pangzhengboyin.com/articles/journey-of-a-single-ray-20b99e9e

> **庞征博引** · 想学的，慢慢都会
>
> 庞征博引是把想学的东西写成连载的 AI 学习工具。说出想学什么，它会先了解你的基础，再把主题写成一篇篇 5–10 分钟能读完的文章；边读边问，接下来学什么跟着你走。这篇就是这样写出来的。
>
> 开始你自己的连载 → https://pangzhengboyin.com
