目录
欢迎继续《游戏引擎实现原理》。卷一《引擎内核》里,我们从零写出了一个能跑的引擎内核 helio——它管内存、管句柄、管实体和组件、管并发。但它是个「盲人」:它知道方块在哪,却不知道方块长什么样。这一卷要做的,就是把那份数据变成屏幕上的像素。
卷二《渲染引擎》要陪你做的是另一件实在的事:在卷一的 helio 内核之上,用 TypeScript 从零接出一套完整的渲染引擎,让《微光岛》的小岛第一次真正被画出来——有光、有影、有后处理、有 UI。
为什么先讲「RHI」再讲「画面」?因为渲染引擎里最容易被讲糊、也最贵的部分,不是那些看得见的着色技巧,而是看不见的抽象层——设备怎么建、命令怎么录、资源怎么绑、内存怎么管、多后端怎么统一。这一层站稳了,后面的几何、光照、帧图、后处理才不是空中楼阁。先有 RHI,才谈得上别的。
市面上讲渲染的书大多从「图形学管线」或「某个引擎的材质系统」切入。本卷走第三条路:每一节都写代码,每一节的代码都在本机真跑过,跑出来的就是这一节的结论——真实像素、真实计时。
这一卷写给谁
| 读者 | 现状 | 读完能拿到什么 |
|---|---|---|
| 读过卷一《引擎内核》的读者 | 内核能跑,但没有画面 | 一套接在内核之上的渲染引擎,和「数据怎么变像素」的完整链条 |
| 有图形学基础、想理解引擎实现 | 懂管线与着色器,但不知道引擎怎么组织它们 | RHI、帧图、批次、阴影这些工程层的真实实现 |
| 前端 / Web 图形开发者 | 熟 WebGL / WebGPU API,缺引擎视角 | 多后端抽象、资源生命周期、命令录制的系统化做法 |
| 已读过渲染原理、想做自研渲染器 | 懂「为什么这么画」,缺「怎么搭起来」 | 一条从设备抽象到综合 demo 的可复制实现路径 |
前置知识:读完卷一《引擎内核》,或至少了解句柄、ECS 与 Job System 的基本概念;会写 TypeScript。不需要安装任何游戏引擎——本卷全部代码在 Node 26 与 headless Chromium 下运行。
三类读者,三类收获
下面把这四类人分别展开,你可以对照自己的处境挑一段读——它决定了你该走哪条阅读路径。
如果你读过卷一、想把内核接出画面。 你手里已经有一个能跑的 helio 内核,但它只会算、不会画。这一卷正好补上那道接缝:内核产出「数据 + 句柄」,渲染消费它们。你会看到卷一的世代句柄怎么变成资源引用、第 4.3 章的顶点布局怎么真正传上 GPU、第 15 章的跨线程同步点怎么落到「逻辑线程 ↔ 渲染线程」的真实边界。读完你得到的不是一堆孤立技巧,而是一条把内核数据喂给 GPU 的完整通路。
如果你有图形学基础、想理解引擎怎么组织渲染。 你可能已经会写着色器、懂管线,但面对「一帧到底怎么排」还是发虚。这一卷讲的是工程层:RHI 怎么把多后端统一成一套接口、帧图怎么用依赖 + 拓扑自动排 Pass、批次怎么按状态键排序、阴影怎么拟合级联。这些正是「会写着色器」和「能搭渲染器」之间的那层。
如果你是前端 / Web 图形开发者。 你对 WebGL2 / WebGPU 的 API 不陌生,但很少从「引擎」的角度把它们包起来:怎么让同一份渲染代码跑在两个后端上、怎么管理资源的上传与生命周期、怎么把命令录制分到多个线程。这一卷把这些平时散落在业务里的做法,收拢成一套可复用的抽象。
如果你已读过渲染原理、想做自研渲染器。 你懂「为什么这么画」,缺的是「怎么搭起来」的次序感。这一卷的 18 章本身就是一份施工顺序表:先立 RHI,再补资源与场景,然后才是材质、光照、帧图、后处理、2D、多线程,最后装配。每一章都能独立跑通,你可以在任何一步停下,把它接到自己的项目里。
与卷一《引擎内核》的关系
卷一和卷二不是「上半本 / 下半本」,而是**「地基 / 楼」**。卷二里的每一行渲染代码,几乎都能在卷一里找到它依赖的那根桩:
| 卷二要建的 | 它接在卷一的哪一处 |
|---|---|
| RHI 设备与多后端 | 平台层 PAL 之上,新增 render/device |
| 资源对象与生命周期 | 复用第 6 章的世代句柄做资源引用 |
| 顶点与纹理上传 | 消费第 4.3 章的顶点布局与对齐约定 |
| 场景图与变换 | 复用第 8–9 章的矩阵与四元数 |
| 帧图的 Pass 编排 | 复用第 3 章的拓扑排序与第 15 章的任务图 |
| 渲染线程与同步 | 复用第 15 章的跨线程同步点 |
内核与渲染的接缝只有一句话:内核产出「数据 + 句柄」,渲染消费它们。 渲染层不认识 ECS 内部怎么存,它只拿到一份「这个句柄、这个变换、这个网格」的清单;内核也不知道 GPU 长什么样,它只把数据准备好。这条缝越清晰,两边越能各自演进——所以卷二往 render/ 里加 GPU 代码时,内核一行都不用改。
还没读卷一的读者,建议先去卷一目录 把内核过一遍;至少读完第 4 章(内存布局)、第 6 章(句柄)与第 15 章(同步),再回来看卷二会轻松很多。
本卷的差异化
和「讲图形学」或「讲某个引擎」的书比,本卷有三条很硬的不同:
- 在 RHI 上从零实现。 我们不调用现成的渲染框架,而是自己定义设备、队列、命令编码器、管线状态、绑定组这一整套抽象,再为它写 WebGPU 与 WebGL2 两个真实后端,外加一个记录式的 NullRHI。你看到的是「引擎作者怎么写 RHI」,不是「怎么用某个 RHI」。
- 每节可跑出真实产物。 每一节都产出一份能在本机运行、能截出图、能读到数字的东西:一张渲染出的图、一份 GPU 计时、一段命令录制的回放。结论不是「理论上会更快」,而是「本机实测快了这么多」。
- 真实像素与计时结果。 凡是给出「这样画更好」的结论,背后都有一张真实渲染的图或一段真实计时。看到数字时,请连着它的测量条件一起看,而不是只记那个倍数。
本卷的可运行性口径
本套书的每一节都标注它属于哪一层,你据此知道「这一节的东西能不能在你机器上跑起来」:
| 层级 | 含义 | 你怎么验证 |
|---|---|---|
| L1 | 纯 TypeScript 逻辑,零运行时依赖 | Node 26 直接跑 .ts,或 vitest 跑单测 |
| L2 | 需要 GPU / 浏览器 | 用 headless Chromium 跑(本卷大量使用) |
| L3 | 只能做类型检查 | tsc --noEmit 通过 |
| L4 | 命令行不可复现 | 正文会明确标注「需手动验证」 |
| B | 性能基准 | 跑出计时 JSON,数字写进正文对照表 |
这五层各自意味着什么,值得展开说清楚:
- L1——能真跑。 排序键的比较、批次合并的算法、包围体的相交测试、帧图的依赖解析,这些都是纯 TypeScript,
node src/xxx.ts就能跑,配vitest就能断言。你看到的每一段 L1 代码,都是在本机跑过、被单测盯过的。 - L2——需要 GPU / 浏览器。 这是本卷的主战场。凡是碰 GPU 的代码——设备、命令、管线、绘制——都得靠 headless Chromium 跑起来验证。本卷会给出统一的跑法,让你在本机也能复现每一张图。
- L3——只能类型检查。 少数代码依赖只在特定后端才有的类型(比如某个扩展声明),能通过
tsc --noEmit但无法直接执行。这类代码会被明确标注,正文会说明「为什么它跑不了」。 - L4——命令行不可复现。 涉及真实硬件或真实帧率的观察(比如某个 GPU 上的实际帧率抖动),命令行里复现不了。正文会标「需手动验证」,并给出你该观察什么。
- B——性能基准。 凡是给出「快了 N 倍」的结论,背后都有一份 B 级基准:它跑出计时 JSON,原始数字写进对照表。看到 B 级结论时,请连着原始数据一起看。
贯穿项目:《微光岛》从内核走进画面
卷一里那个「会走动的方块」,在这一卷里要真正被看见。全书仍然只有这一个项目:《微光岛》——一款低多边形 3D 收集冒险小游戏。卷二要做的,是给这个小岛接上一整套渲染能力:
| 画面需求 | 牵出的技术点 | 对应章节 |
|---|---|---|
| 小岛和角色被画出来 | RHI 设备、命令、管线 | 2–5 |
| 地表、岩石、树木的几何 | 顶点格式、程序化网格、纹理 | 6 |
| 相机跟随角色、可拾取物体 | 场景图、变换、相机与射线 | 7 |
| 岛上成百上千个物体不掉帧 | 视锥剔除、BVH、实例化合批 | 8、9 |
| 不同表面有不同观感 | 材质、着色器变体、参数绑定 | 10、11 |
| 白天阳光、夜晚路灯 | 光照模型、光源类型、集群光照 | 12 |
| 树木与角色投下影子 | 阴影贴图、级联阴影 | 13 |
| 一帧有条不紊地排布 | 帧图、Pass 依赖与资源生命周期 | 14 |
| 画面调色、边缘平滑 | 色调映射、抗锯齿、全屏 Pass 链 | 15 |
| 背包与血条的 UI | 精灵批次、SDF 文本、合成顺序 | 16 |
| 主线程不卡顿 | 渲染线程、并行命令录制 | 17 |
| 昼夜循环、可剖析、可收口 | 渲染器装配、性能剖析 | 18 |
范围红线:本卷做到「《微光岛》在真实 GPU 上被完整画出来」为止。物理、动画、音频等更上层的内容留给后续卷次。先把渲染这条链打通,是本卷唯一的目标。
关于基准数字的诚实声明
本卷会出现不少性能数字——绘制调用数、GPU 计时、内存占用、加速比。它们都来自作者本机实测,并附带测量条件。但它们有一个必须说清楚的边界:
这些数字是同机相对量级,绝对数值不可移植。 GPU 驱动、浏览器版本、后台负载都会影响结果;同一段渲染代码,换个批次大小、换个队列策略,帧时间可能差出一截。所以当你看到「合批后绘制调用降到原来的十分之一」时,请把重点放在方向而不是倍数上。
真正稳定的结论是那些量级差异,比如「逐物体绘制产生上千次绘制调用,合批后降到几十次」——这种结论不会因为环境微调而翻转。本卷会刻意把「方向性结论」和「具体倍数」分开表述:前者你可以直接采信,后者请当作「作者机器上的参考值」,并鼓励你在自己的机器上复现一遍。
三条阅读路径
本卷 18 章 54 节,一次读完大约 6 周(每天一小时),但不是每个人都该按同一种顺序读。下面三条路径覆盖了绝大多数人的需求。
路径 A:顺序通读(约 6 周)
最完整的一条。按顺序走完 18 章,每章的代码都亲手敲一遍。
第 1 章(总览)→ 第 2–5 章(RHI 四章)→ 第 6–9 章(资源与场景)
→ 第 10–13 章(材质、着色与光照)→ 第 14–16 章(帧图、后处理与 2D)
→ 第 17–18 章(多线程与综合)
适合谁:想把渲染引擎从头搭到尾、或打算自研渲染器的读者。第 18 章会把前面所有零件装配成一个能跑的渲染器,不要跳过。这条路径最慢,但拿到的是一套完整的、能自己复现的渲染器。
路径 B:只读 RHI 四章(约 2 周)
已经懂图形学、只关心「引擎怎么把 GPU 抽象出来」的话:
2.1 → 2.2 → 2.3 → 3.1 → 3.2 → 3.3 → 4.1 → 4.2 → 4.3 → 5.1 → 5.2 → 5.3
适合谁:时间有限、目标是「吃透 RHI 这一层」的读者。这 12 节从抽象设计一路走到多后端实现,是一条自洽的线。建议至少把第 3 章(命令与队列)和第 4 章(资源与内存)完整读——它们决定了后面所有性能结论的地基;跳过它们,第 14、15 章的实测数字会变成一堆没有根据的结论。
路径 C:直奔帧图与后处理(约 2 周)
只想知道「一帧怎么排、画面怎么调」的话:
1.1 → 14.1 → 14.2 → 14.3 → 15.1 → 15.2 → 15.3
适合谁:已经有一个能画的渲染器、想搞清「帧图与后处理怎么组织」的读者。这条路径最短,但会牺牲 RHI 的「为什么」。建议把 1.1 一帧都发生了什么 当作前置先读,它给了整条 CPU / GPU 时间线的全貌。
路径 D:按问题查(随时)
不打算通读,只在遇到具体问题时翻对应章节。
画面不出现 / 全黑 → 第 3、5 章;物体位置不对 → 第 7 章;帧率低 → 第 8、9 章
材质不生效 → 第 10、11 章;光照发灰 → 第 12 章;影子抖动 → 第 13 章
后处理花屏 → 第 14、15 章;UI 遮挡错乱 → 第 16 章;主线程卡顿 → 第 17 章
适合谁:已经有一个能跑的渲染器、正被某个具体问题卡住的读者。下节「常见问题速查」就是为这条路准备的——它把「你想解决的问题」直接映射到「去看哪一节」。
18 章分组清单
下面把 18 章按五个部分分组,每章链到它的第 1 节。想跳读时,从这里直接进。
第一部分:架构与 RHI(第 1–5 章)
先看一帧的全貌,再花四章把 GPU 抽象层从零建起来。
- 第 1 章 渲染架构总览 —— 一帧的 CPU / GPU 时间线、渲染器分层、与站内 graphics 专题的分工
- 第 2 章 RHI(一):抽象设计 —— 为什么要 RHI、资源对象、管线状态与着色器模块
- 第 3 章 RHI(二):命令与队列 —— 命令编码器、渲染通道、队列提交与帧同步、三缓冲
- 第 4 章 RHI(三):GPU 资源与内存 —— 资源绑定、上传与 mipmap、GPU 内存池与预算
- 第 5 章 RHI(四):多后端实现 —— WebGPU、WebGL2 能力降级、NullRHI 记录式假后端
第二部分:资源与场景(第 6–9 章)
把几何、纹理、场景、相机接上,再解决「画哪些、按什么顺序画」。
- 第 6 章 几何与纹理资源 —— 顶点格式、程序化网格、纹理格式与压缩、采样器与各向异性
- 第 7 章 场景、变换与相机 —— 场景图与变换层级、脏标记传播、相机与拾取射线
- 第 8 章 可见性剔除与 LOD —— 视锥剔除与包围体、空间划分与 BVH、GPU 驱动剔除
- 第 9 章 排序与批次 —— 排序键与前后绘制、状态缓存与冗余剔除、实例化与合批
第三部分:材质、着色与光照(第 10–13 章)
让画面「有材质、有光、有影」。
- 第 10 章 材质与着色器(一) —— 材质模型与参数、着色器变体与宏、变体爆炸与编译缓存
- 第 11 章 材质与着色器(二) —— WGSL 组织与 include、反射驱动的参数绑定、着色器调试
- 第 12 章 光照(一) —— 光照模型与着色路径、光源类型与衰减、集群光照装配
- 第 13 章 光照(二):阴影 —— 阴影贴图的引擎侧实现、级联阴影拟合、性能与质量权衡
第四部分:帧图、后处理与 2D(第 14–16 章)
把一帧的 Pass 编排成图,加上全屏特效,再接上 UI。
- 第 14 章 帧图 —— Pass 依赖与资源生命周期、WebGPU 约束下的帧图、编译与执行
- 第 15 章 后处理与全屏特效 —— 全屏 Pass 链、色调映射与抗锯齿、后处理链的性能预算
- 第 16 章 2D 与 UI 渲染 —— 精灵批次与图集、SDF 文本与矢量 UI、UI 与 3D 的合成顺序
第五部分:多线程与综合(第 17–18 章)
让它跑满多核,最后把全部零件装成一个能画的渲染器。
- 第 17 章 多线程渲染 —— 渲染线程模型与命令缓冲、OffscreenCanvas 并行录制、与 Job System 对接
- 第 18 章 综合 —— 渲染器装配与场景接入、昼夜循环与光照表现、渲染性能剖析与收口
周计划表
按路径 A 的顺序通读,可以照下面这张表安排节奏(每天约一小时):
| 周次 | 章节 | 本周焦点 | 本周产物 |
|---|---|---|---|
| 第 1 周 | 第 1–3 章 | 一帧全貌与 RHI 抽象、命令与队列 | 能录一段命令并提交 |
| 第 2 周 | 第 4–5 章 | GPU 资源、内存池与多后端 | 两个后端画出同一张三角形 |
| 第 3 周 | 第 6–9 章 | 几何纹理、场景相机、剔除与批次 | 一个能剔除、能排序的场景 |
| 第 4 周 | 第 10–13 章 | 材质、着色器、光照与阴影 | 一个有光有影的模型 |
| 第 5 周 | 第 14–16 章 | 帧图、后处理与 2D / UI | 一条带后处理的完整帧 |
| 第 6 周 | 第 17–18 章 | 多线程渲染与综合 | 一个能跑的渲染器与《微光岛》画面 |
如果只有零散时间,可以只挑第 2–5 章(RHI)或第 14–15 章(帧图与后处理)这两段——它们各自成篇,是整卷最容易被单独拿走的两块。
常见问题速查
| 你想解决的问题 | 去看 |
|---|---|
| 屏幕全黑,什么都没画出来 | 3.1 、5.1 |
| 同一份代码想在两个后端都跑 | 2.1 、5.2 |
| 资源删了但 GPU 还在用、偶发花屏 | 3.3 、4.3 |
| 纹理上传后是黑的 / mipmap 有缝 | 4.2 、6.2 |
| 物体位置或朝向不对 | 7.1 、7.2 |
| 相机画面裁剪、拾取点不中 | 7.3 |
| 远处物体太多、帧率上不去 | 8.1 、8.2 |
| 大量同类物体逐个体绘制太慢 | 9.3 、8.3 |
| 绘制顺序乱、透明物穿透 | 9.1 |
| 每帧重复设状态、开销大 | 9.2 |
| 改一个开关就多出一堆着色器变体 | 10.2 、10.3 |
| 着色器参数绑定老是漏 | 11.1 、11.2 |
| 着色器编译报错却看不出哪一行 | 11.3 |
| 光照发灰、明暗不对 | 12.1 、12.2 |
| 光源一多就卡 | 12.3 |
| 阴影锯齿重、接缝明显 | 13.1 、13.2 |
| 开了阴影帧率掉一半 | 13.3 |
| Pass 之间依赖理不清、改一处崩一片 | 14.1 、14.3 |
| 后处理链花屏或没效果 | 15.1 、15.2 |
| 后处理把帧率吃光了 | 15.3 |
| 精灵一多就掉帧 | 16.1 |
| UI 文字糊、矢量图标发虚 | 16.2 |
| UI 被 3D 挡住或挡反了 | 16.3 |
| 录制命令卡主线程 | 17.1 、17.2 |
| 多线程录制和内核 Job System 对不上 | 17.3 |
| 想把所有零件装成一个渲染器 | 18.1 、18.3 |
怎么用这份索引
- 想系统学:走路径 A,从第 1 章 1.1 开始。
- 想抓重点:走路径 B,从第 2 章 2.1 开始,把 RHI 四章吃透。
- 想查问题:直接用上面的「常见问题速查」,或跳到对应小节。
- 想配代码:每一节的代码块都标了层级与来源文件,L1 的可以直接复制出来在 Node 26 上跑,L2 的按正文给的 headless Chromium 跑法复现。
- 每篇正文末行都有「上一节 / 下一节」导航,也可以随时回到本页或卷索引 。
相关阅读
- 卷一《引擎内核》目录 :本卷的地基,从零写引擎内核
- 游戏引擎专题导航 :Unity、Unreal 与 Defold 的选型与入门
- 图形学专题 :渲染管线、着色器与 GPU 的深度文章
- 书稿书架 :全部电子书与创作手记
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。