第 3 章
深渊中的坐标系
把时间拨回1992年深秋。在德克萨斯州麦迪逊市一座不起眼的办公楼的二楼,id Software的办公室还远不是后来那个被程序员视为圣殿的地方。几台486PC散落在桌上,墙上钉着金属乐队的海报,空气里混着焊锡和披萨的气味。
约翰·卡马克站在一块白板前,手里捏着马克笔,面对着一幅用潦草线条画成的几何图形——那是约翰·罗梅洛设计的关卡地图,被抽象成了十几个相连的多边形区域,每个区域标注着地板高度、天花板高度、液体属性和光照值。卡马克在图形中间画了一条直线,将一个复杂的凹多边形切成两个凸多边形,然后在分割线两端标注了节点编号。这不是在画关卡。这是在编译空间。在场的人当时未必意识到,这个动作标记着一个历史性的分界线。
白板上的线条不是装饰,不是示意,而是一个正在被做出的架构决策:关卡地图将在运行前被离线编译为一棵二叉空间分割树,即BSP树,渲染器在每一帧只需遍历这棵树,就能以恒定的速度确定哪些墙面在玩家视野内、哪些被遮挡、哪些应该从后往前绘制。这个决策的后果将远超性能优化。它将游戏空间从像素网格的牢笼中解放出来,变成一个有自己内在法则的数学坐标系。它将关卡设计师的工作变成在抽象几何空间中放置扇区、设定属性,而引擎负责将这个几何描述实时转化为玩家眼前的视锥。它将“引擎”这个词的含义,从一组渲染工具,永久地改写为一个世界编辑器。
要理解这个决策的分量,需要先理解它所拒绝的东西。1992年的PC游戏图形处于一个奇怪的过渡期。在光谱的一端,是id Software自己的《德军总部3D》所代表的射线投射引擎——它在二维网格地图上发射射线,根据射线撞墙的距离决定墙面的绘制高度,用纹理映射填充每一列像素。
这个方案在386处理器上跑到了流畅的帧率,但它有一个致命的局限:它只能处理正交网格,所有的墙都必须垂直于地面,所有的房间都必须是矩形,天花板和地板都是平的。你可以在纳粹城堡里奔跑,但你永远无法低头看到自己的脚,永远无法抬头看到天空,永远无法进入一个不是由直角构成的房间。射线投射引擎的成功本身就是它极限的证明——它把一维射线投射做到了极致,但极致就是天花板。
在光谱的另一端,是完全不同的空间模型。Origin Systems的《银河飞将》系列代表了当时最昂贵的解决方案:用预渲染的位图序列模拟三维飞行。每一艘敌舰的每一个角度、每一种距离、每一种光照条件下的外观,都被预先渲染成二维位图,存储在CD-ROM中,运行时根据玩家的视角选择最接近的那张位图显示。这个方案可以呈现令人惊叹的视觉效果——1992年的《银河飞将II》的画面质量远超同期任何实时渲染游戏——但它的代价是天文数字的存储空间和对游戏设计的严格限制。
每一个可显示的物体都必须预先渲染,每一个可能的视角都必须预先计算。这不是一个可以自由设计的世界,而是一本预先印刷好的画册,玩家只能在画册的页码之间翻动。
在这两个极端之间,存在着一个被整个行业共享的技术幻觉:程序员们开始相信,可以通过一套可复用的底层代码,在不同硬件上重建同一个游戏世界。Origin的实时空间飞行引擎试图做到这一点,但它受限于预渲染位图的重量。Looking Glass Technologies在1992年开始开发的《系统震荡》走了另一条路——它在真正的三维空间中引入了物理交互和倾斜视角,允许玩家在任意角度的斜坡上行走,让物体在重力作用下坠落,让光影随着光源位置实时变化。但这条路需要486DX2-66级别的处理器才能跑到可玩的帧率,而1992年的主流配置还是386。
更重要的是,《系统震荡》的引擎架构将空间理解为一组独立的3D物体,每个物体有自己的位置、旋转和碰撞体积,但物体之间的关系需要实时计算,没有一种统一的数据结构来预先组织整个空间的可见性。这意味着渲染器在每一帧都必须检查场景中的每一个多边形是否在视野内,随着场景复杂度的增加,性能呈线性甚至超线性下降。
卡马克在白板上画下的BSP树,是对这个困境的回答。BSP树的思想本身并不新鲜。它最早由计算机图形学研究者在1969年提出,用于解决三维场景的可见性判定问题。但在1992年之前,没有人认真考虑过将BSP树用于消费级PC的实时游戏渲染。原因很直接:BSP树的构建是一个计算密集型的离线过程,需要对场景中的每一个多边形进行分割和排序,生成的树结构可能包含数千个节点。在当时的学术研究中,BSP树通常用于静态场景的离线渲染——光线追踪、辐射度计算——这些应用场景对构建时间的容忍度以小时甚至天计。
没有人认为BSP树可以在游戏这种需要快速迭代的场景中发挥作用。卡马克的洞见在于他看到了BSP树的一个被忽略的特性:一旦构建完成,BSP树的遍历速度与场景复杂度几乎无关。无论关卡中有多少个扇区、多少面墙、多少个角落和走廊,渲染器在每一帧只需要从代表玩家位置的叶子节点开始,沿着树结构进行一次有序遍历,就能按照从近到远、从前往后的顺序确定每一面可见墙面的绘制次序。这个遍历的时间复杂度是O(n),其中n是树中节点的数量,而不是场景中多边形的数量。更关键的是,这个遍历过程不需要任何浮点运算,不需要任何硬件加速,可以在386处理器的整数运算单元上高效执行。
这就是那个历史性平衡点的数学基础。BSP树将计算负担从运行时转移到了编译时——从每一帧都必须重复的可见性判定,变成了在关卡加载前一次性完成的树构建。这个转移的后果是深远的。
它意味着关卡设计师可以在一个几乎任意的几何空间中工作——可以设计非正交的墙壁、不同高度的天花板和地板、楼梯、平台、窗户、阳台——只要这个空间可以被分割成一组凸多边形扇区,BSP编译器就能把它变成一棵树,渲染器就能在运行时以恒定的速度遍历它。设计师的自由度和运行时的性能不再是一对不可调和的矛盾。
但这个架构决策还有另一面,一个在当时几乎没有人注意到的后果。当关卡设计师的工作变成在一个抽象几何空间中放置扇区、设定地板与天花板高度、标记液体属性与光照值时,关卡设计本身就变成了一种形式化的空间描述语言。设计师不再直接控制屏幕上的每一个像素,不再手动管理哪些墙在哪些情况下可见。他们在一个数学坐标系中工作,用几何体素和属性标记来表达设计意图,引擎则负责将这个抽象描述转化为具体帧。这是“引擎”概念的一次质变。
在《德军总部3D》的时代,引擎和关卡之间的关系是单向的:引擎提供了一套渲染工具,关卡设计师在这套工具的限制内工作,引擎对关卡的内容一无所知。一个《德军总部3D》的关卡文件本质上是一个二维网格,每个格子标记为墙或空地,再加上敌人和物品的放置坐标。引擎读取这个网格,在运行时用射线投射算法将它渲染成屏幕上的像素。引擎不知道什么是“房间”,什么是“走廊”,什么是“门”——它只知道网格和射线。
在BSP树的架构下,这种关系发生了逆转。BSP编译器读取关卡设计师创建的几何描述——扇区的顶点坐标、邻接关系、地板和天花板高度、液体属性、光照值——然后将这个描述转化为一棵树。在这个过程中,编译器必须理解关卡的空间结构:哪些扇区是相邻的,哪些墙面是共用的,哪些区域从哪些位置可见。编译器不是在被动地读取一个网格,而是在主动地分析一个空间。引擎从一组渲染工具变成了一个理解空间结构的系统。这个转变的具体机制值得仔细拆解。
BSP编译器的输入是关卡设计师用编辑器创建的几何描述——这本身就是一个重要的创新。id Software为《毁灭战士》开发了一套专用的关卡编辑器,允许设计师在一个俯视视角下绘制扇区的轮廓,设定每个扇区的地板高度和天花板高度,标记哪些墙面需要纹理映射,哪些扇区包含液体(熔岩、毒液、水),哪些扇区的光照值低于正常水平。设计师还可以放置“物品”——武器、弹药、医疗包、钥匙卡——以及“怪物”——恶魔、士兵、机械蜘蛛——每个都有自己在扇区内的坐标和朝向。
编译器的任务是将这个几何描述转化为一棵BSP树。这个过程从选择第一个分割平面开始。编译器遍历所有扇区的所有墙面,寻找一个能将当前空间尽可能均匀地分成两半的平面。这个选择不是随意的——一个糟糕的分割平面可能导致后续产生过多的碎片扇区,增加树的深度和遍历时间。编译器使用启发式算法来评估候选平面:它计算每个候选平面会切割多少个扇区,会产生多少个新的碎片扇区,会在左右子树之间产生多大的不平衡。
选择的标准是在分割效率(尽量均匀地分割空间)和树深度(尽量减少碎片扇区)之间找到平衡。一旦选定分割平面,编译器就将当前空间中的所有扇区分配到左子树或右子树——取决于扇区在平面的哪一侧。如果一个扇区跨越了分割平面,编译器必须将它切成两个扇区,分别分配到左右子树。这个过程递归进行,直到每个子空间中的扇区数量降到某个阈值以下,或者无法再找到有效的分割平面。最终生成的BSP树的每一个叶子节点都对应一个凸多边形扇区——这是遍历的终点,渲染器从这里开始绘制墙面。
编译完成后,BSP树被写入一个二进制文件,与纹理资源、声音资源、怪物行为脚本一起分发给玩家。当玩家启动游戏、加载一个关卡时,引擎将BSP树读入内存,将纹理加载到显存(或者更准确地说,在1993年的VGA显卡上,是加载到系统内存中的离屏缓冲区),然后开始主循环。主循环中的渲染过程是BSP树架构的真正表演时刻。
每一帧开始时,引擎首先确定玩家在BSP树中的位置——即玩家坐标落在哪一个叶子扇区中。这个定位过程本身利用了BSP树的结构:从根节点开始,比较玩家坐标与分割平面的位置关系,决定进入左子树还是右子树,递归下降直到到达叶子节点。
然后,渲染器开始从玩家所在的叶子扇区出发,沿着BSP树进行有序遍历。遍历的次序是BSP树渲染的核心。渲染器从玩家所在的叶子扇区开始,先绘制这个扇区内的所有墙面——按照从玩家视角看去的远近次序。然后沿着树结构向上回溯:在每一个内部节点,渲染器判断玩家在分割平面的哪一侧,先遍历玩家所在那一侧的子树(因为那一侧的扇区离玩家更近),再遍历另一侧的子树(因为那一侧的扇区被分割平面遮挡,需要后绘制以避免覆盖更近的墙面)。
这个遍历次序保证了渲染器始终按照从近到远的次序绘制墙面,而“画家算法”——即后绘制的墙面覆盖先绘制的墙面——自然处理了遮挡关系。但这个遍历次序还有一个更微妙的效果:它自动实现了可见性剔除。
当渲染器遍历到某个节点时,它会检查这个节点对应的子空间是否在玩家当前的视野锥内。如果不在,整个子树都可以跳过,无需进一步处理。这个检查本身非常廉价——只需要比较视野锥的边界平面与子空间的包围盒——但它可以一次性剔除大量不可见的几何体。在一个典型的《毁灭战士》关卡中,BSP树的可见性剔除可以将需要绘制的墙面数量减少到场景总墙面数的十分之一甚至更低。
这就是为什么《毁灭战士》能在386处理器上实现流畅的第一人称透视。不是因为它渲染得快——实际上,它的渲染器每一帧都在用软件逐像素计算纹理映射,这在1993年的标准下是极其昂贵的操作。而是因为它渲染得少。BSP树保证了渲染器只处理玩家实际能看到的墙面,而玩家在任何一个时刻能看到的墙面通常不超过关卡总墙面数的百分之十到二十。
这个“少”是通过离线编译换来的——编译器承担了分析空间结构、构建树、分割扇区的计算负担,使得运行时渲染器可以坐享其成。
这个架构决策还有另一个关键维度:它将关卡设计的迭代速度与运行时的渲染性能解耦了。设计师可以在编辑器中修改扇区的几何形状、调整光照值、添加新的走廊和房间,然后重新编译——这个过程在1993年的486上只需要几秒到几十秒。编译完成后,渲染器在运行时的性能不受任何影响——无论关卡被修改了多少次,无论关卡中有多少个扇区和墙面,BSP树的遍历速度始终是O(n),其中n是树中的节点数量。这意味着设计师可以快速迭代,反复测试,不断调整空间布局,而不用担心性能下降。
这在当时的游戏开发中是革命性的。对比是鲜明的。《银河飞将》系列的设计师如果想要添加一个新的敌舰类型,必须等待美术团队为这个敌舰预渲染数百个角度的位图,然后等待程序员将这些位图集成到游戏中,然后测试在目标硬件上的帧率——如果帧率不达标,就必须减少位图数量,降低视觉质量。
《系统震荡》的设计师如果想要添加一个新的房间,必须仔细计算这个房间会增加多少多边形,会对实时可见性判定造成多大压力,然后祈祷在目标硬件上还能跑到可玩的帧率。只有《毁灭战士》的BSP+扇区模型将设计迭代从性能焦虑中解放了出来——设计师在几何空间中自由工作,编译器负责将这个空间转化为可高效遍历的树,渲染器负责将树转化为屏幕上的像素。每个环节有自己独立的计算预算,互不干扰。
但这个架构也有它的代价。BSP树要求场景中的所有几何体都是静态的——墙不能移动,地板不能变形,天花板不能坍塌。因为在编译时构建的树结构假设空间结构在运行时不变。如果一堵墙在运行时移动了,它可能跨越编译时确定的分割平面,破坏整个树的遍历次序。这就是为什么《毁灭战士》中所有的墙都是固定的,所有的门都是垂直升降的(而不是旋转的),所有的电梯都是垂直移动的(而不是倾斜的)。
这些限制不是引擎能力的问题,而是BSP树架构的内在约束——它将空间结构冻结在编译时,以换取运行时的性能。这个代价在当时看来是完全可以接受的。1993年的玩家第一次进入《毁灭战士》的世界时,他们注意到的不是不能旋转的门,而是他们可以抬头看到不同高度的天花板,可以低头看到不同深度的地板,可以在非正交的走廊中奔跑,可以从窗户看到远处的房间,可以站在阳台上俯视下方的熔岩池。这些在今天看来理所当然的空间体验,在1993年是革命性的。
它们之所以可能,正是因为BSP树将游戏空间从二维网格提升为三维坐标系——在这个坐标系中,每一个点都有三个坐标值,每一面墙都有一个朝向,每一个扇区都有独立的地板和天花板高度。这个坐标系还有另一个维度,一个在单机模式下不显眼但在多人模式下至关重要的维度:玩家本身也变成了坐标系中的一个可同步对象。《毁灭战士》的多人模式在当时是一个技术奇迹。它允许最多四个玩家通过局域网连接,在同一个关卡中相互对抗或合作。
实现这个功能的代码量并不大——卡马克在游戏发布前不久才添加了多人支持,据说只花了几周时间——但它的架构意义深远。在多人模式下,每个玩家的机器上运行着同一个关卡的同一个BSP树,渲染着从各自视角看到的画面。但每个玩家的位置、朝向、移动速度、开火状态、生命值、护甲值、武器——这些数据不再是本地唯一的真理。它们变成了需要通过网络同步的状态变量。
同步的机制是点对点的。每个玩家的机器向其他所有玩家的机器周期性发送自己的状态数据包,同时接收其他玩家发来的数据包。数据包的内容非常精简:玩家ID、X坐标、Y坐标、Z坐标(用于计算高度,虽然《毁灭战士》的游戏逻辑在垂直方向上有限制)、朝向角度、移动速度、开火标志、武器类型。这些数据包的大小只有几十个字节,以每秒15到20次的频率发送——在1993年的局域网带宽下,这个数据量完全在可承受范围内。
每个玩家的机器在接收到其他玩家的状态数据后,必须在自己的BSP树中定位这些玩家——即确定他们落在哪一个叶子扇区中,从自己的视角看他们应该出现在屏幕上的什么位置,被哪些墙面遮挡。这个过程与渲染怪物和物品的过程完全相同:其他玩家被当作坐标系中的可移动对象,与怪物共享同一套可见性判定和遮挡处理逻辑。当你在《毁灭战士》的多人模式中看到另一个玩家从走廊拐角处出现时,你的机器正在用BSP树确定这个玩家在你的视野中的位置和遮挡关系,就像它确定一堵墙或一个恶魔在你的视野中的位置一样。
这意味着在多人模式下,共享的不是屏幕上的像素——每个玩家看到的画面完全不同。共享的是空间本身——BSP树定义的那个几何坐标系,以及在这个坐标系中移动的物体的状态。局域网中的每一个数据包都在说:在这个时刻,我的坐标是这个,我的朝向是这个,我正在开火。接收到数据包的机器将这个信息映射到自己的BSP树中,在自己的屏幕上重建这个玩家的视觉呈现。
四个玩家看到的是同一个空间的四个不同视角,但他们都相信自己在同一个空间中——因为这个空间由一个统一的数学框架定义,不依赖任何单个玩家的视角。这是共享空间幻觉的技术基础。
它不是靠传输画面实现的——1993年的局域网带宽根本不可能传输实时视频流。它不是靠传输输入指令实现的——像早期的格斗游戏网络对战那样,同步的是摇杆和按钮的状态。它是靠传输坐标系中的位置和朝向实现的——同步的是玩家在抽象空间中的数学表示。这个方案的前提是:所有玩家的机器上都运行着同一个空间的同一个数学表示——BSP树。如果一台机器上的BSP树与另一台机器上的BSP树不一致——哪怕只是一个扇区的分割方式不同——那么两个玩家就会在不同的空间中移动,同步的位置数据就会产生错误的视觉结果。
这就是BSP树架构的深层后果:它不只是渲染优化,不只是设计工具,它是共享空间的本体论基础。BSP树定义了什么是“这个空间”——哪些区域是可达的,哪些墙面是可见的,哪些位置是合法的。
所有运行这个BSP树的机器都共享这个定义。当玩家在局域网中移动时,他们实际上是在这个共享的数学空间中交换坐标——他们不是在交换画面,不是在交换指令,而是在交换“我在哪里”这个最基本的空间信息。而这个“哪里”之所以有意义,正是因为BSP树将它变成了一个可计算、可同步、可在不同机器上一致重建的数学对象。这个后果在当时几乎没有人注意到。1993年的玩家和评论家谈论的是《毁灭战士》的画面、速度、暴力、恐怖氛围、多人对战的刺激。没有人谈论BSP树,没有人谈论扇区分割,没有人谈论坐标系同步。但这些技术决策正在悄然改变“游戏引擎”这个词的含义。
在《毁灭战士》之前,“引擎”通常指一组渲染工具——绘制精灵图的函数、播放音效的函数、读取输入的函数。《毁灭战士》之后,“引擎”开始意味着一个世界编辑器——一个定义空间法则、物理法则、交互法则的完整框架。这个转变可以从一个具体的对比中看得更清楚。
1993年,《毁灭战士》发布的同时,Looking Glass的《系统震荡》也在开发中。《系统震荡》的引擎在许多方面比《毁灭战士》更先进:它支持真三维多边形渲染(而不是《毁灭战士》的伪三维扇区投影),支持任意角度的斜坡和倾斜视角,支持物理交互(物体可以坠落、弹跳、堆积),支持动态光照(光源移动时光照实时变化)。从纯技术的角度看,《系统震荡》的引擎是更“真实”的三维引擎——它渲染的是真正的三维多边形,而不是用二维扇区投影模拟的三维透视。
但《系统震荡》的引擎有一个根本性的架构缺陷:它没有BSP树。它的空间组织是基于物体的——场景中的每一个多边形都属于某个物体,每个物体有自己的位置和旋转,渲染器在每一帧必须遍历所有物体,检查哪些在视野内,哪些需要绘制。随着场景复杂度的增加,这个遍历的成本线性增长。
更糟糕的是,因为没有BSP树的可见性预组织,渲染器无法高效地确定哪些多边形被遮挡——它只能依赖一个粗粒度的视锥剔除,然后让z-buffer处理剩余的遮挡。这在1993年的软件渲染条件下是极其昂贵的——z-buffer需要为屏幕上的每一个像素存储深度值,在软件中进行逐像素的深度比较和纹理映射,计算量远超《毁灭战士》的扇区投影。《系统震荡》在1994年发布时,即使在486DX2-66上也只能跑到15到20帧每秒,远低于《毁灭战士》在386上就能达到的30帧以上。
这不是因为《系统震荡》的引擎写得不好——它的代码质量同样出色。而是因为它的架构决策没有在性能、可修改性和设计表达之间找到那个平衡点。它选择了更真实的真三维多边形渲染,代价是无法在消费级硬件上跑到流畅的帧率。它选择了更灵活的物体空间组织,代价是无法像BSP树那样高效地剔除不可见面。《毁灭战士》的BSP+扇区模型避免了这两个陷阱。
它通过将空间编译为BSP树,实现了与场景复杂度无关的可见性剔除。它通过使用扇区投影而非真三维多边形渲染,将每一帧的计算量控制在消费级硬件可承受的范围内。它通过将设计迭代与运行时性能解耦,允许设计师在几何空间中自由工作。这三个特性——性能、可修改性、设计表达——在BSP+扇区模型中形成了一个相互支撑的三角,每一个特性都依赖另外两个特性。性能依赖BSP树的离线编译,可修改性依赖编译器和渲染器的分离,设计表达依赖扇区几何描述的灵活性。这个三角就是那个历史性的平衡点。
它不是任何一个单项技术的突破,而是多个技术决策在特定硬件条件下的组合优化。BSP树本身不是新发明——它在学术文献中已经存在了二十多年。扇区投影不是新发明——它在早期的三维游戏中已经有人尝试过。关卡编辑器不是新发明——许多游戏都有自己的内部编辑工具。但将这些技术组合成一个完整的架构——离线编译BSP树、运行时遍历渲染、扇区几何描述、设计师编辑器——这个组合是全新的。
它定义了一种新的游戏引擎范式:引擎不是一组渲染函数,而是一个从几何描述到屏幕像素的完整流水线。这个范式的后果在接下来的十年中逐渐显现。
1993年之后,几乎所有的第一人称射击游戏引擎都采用了某种形式的BSP树或类似的空间分割数据结构。Quake引擎、GoldSrc引擎、Source引擎、Unreal引擎——每一个都在BSP树的基础上进行了扩展和优化,但基本的架构思想保持不变:将空间离线编译为可高效遍历的树结构,在运行时以恒定的速度进行可见性剔除和渲染排序。这个思想甚至延伸到了游戏之外的领域——建筑可视化、虚拟现实、军事模拟——任何需要在消费级硬件上实现实时三维透视的应用,都继承了BSP树的遗产。但BSP树范式的真正历史意义不在于它的长寿,而在于它第一次将游戏空间从硬件中解放了出来。
在BSP树之前,游戏空间是硬件的奴隶——它被限制在正交网格中(射线投射引擎),或者被限制在预渲染位图中(《银河飞将》系列),或者被限制在物体列表中(早期真三维引擎)。游戏空间的样子取决于渲染算法的能力,而渲染算法的能力取决于硬件的算力。设计师在设计空间时,必须时刻想着渲染器能不能处理这个角度、这个距离、这个复杂度。
BSP树改变了这个关系。它通过离线编译将空间分析从运行时剥离,使得渲染器可以在不依赖硬件加速的条件下处理复杂的几何空间。它通过扇区描述语言为设计师提供了一个在抽象几何空间中工作的接口,使得设计意图可以独立于渲染实现。它通过BSP树的数学结构为空间提供了一个独立于硬件的表示,使得同一个空间可以在不同的机器上、不同的显卡上、不同的游戏中一致地重建。当游戏空间从像素网格跃迁为数学坐标系时,它就获得了一种独立的存在——它可以被描述、被编译、被传输、被同步、被修改、被复用,而不依赖任何特定的硬件或渲染算法。
这就是“引擎”从一个工具变成一个世界编辑器的真正含义。引擎不再只是帮助程序员在屏幕上画东西的函数库。引擎变成了一个定义世界法则的系统——空间如何组织,可见性如何判定,物体如何移动,玩家如何交互。这些法则不是硬件的自然属性,不是渲染算法的必然结果,而是引擎设计者的架构决策。选择BSP树而不是物体列表,选择扇区投影而不是真三维多边形,选择离线编译而不是实时计算——这些决策定义了在《毁灭战士》的世界中,什么是可能的,什么是不可能的,什么是容易的,什么是困难的。
1993年12月10日,《毁灭战士》通过威斯康星大学的FTP服务器发布。在接下来的几个小时里,服务器被来自世界各地的下载请求淹没,威斯康星大学的网络基础设施崩溃了。id Software不得不紧急将文件转移到多个镜像站点,但即使如此,下载速度仍然慢得令人绝望。玩家们在BBS上焦急地等待,互相传递着镜像地址,分享着下载进度。
当第一个玩家终于成功运行游戏、进入那个由BSP树定义的空间时,他看到的不是技术的奇迹——他看到的是一个有不同高度天花板和地板的房间,一个从窗户可以看到的远处走廊,一个站在阳台上俯视的熔岩池。
他看到的是一致物理法则统治的三维坐标系,在这个坐标系中,他可以自由移动,敌人可以从任何角度接近,弹道可以穿过窗户和门口,光影可以标记危险和秘密。他看到的是一个世界。而这个世界之所以可能,是因为在一年前的那个秋天,卡马克在白板上画下了一条分割线,将一个凹多边形切成两个凸多边形,然后标注了节点编号。那条线不是装饰,不是示意,而是一个正在被做出的架构决策——一个将游戏空间从像素网格提升为数学坐标系的决策,一个将引擎从渲染工具变成世界编辑器的决策,一个定义了此后十年游戏设计语言的决策。
在386处理器上,以每秒35帧的速度,用240个扇区和1800个BSP树节点,通过每秒15次的数据包同步四个玩家在同一个坐标系中的位置——《毁灭战士》的这组数字不是技术指标的炫耀。它们标记的是一个历史性的跃迁:当游戏空间从像素网格跃迁为数学坐标系时,“引擎”便从一组渲染工具变成了一个世界编辑器。这个跃迁的后果当时还没有人完全理解。但它的方向已经不可逆转。
下一代引擎将在BSP树的阴影中诞生,但它们要面对的问题将不再是“如何在386上渲染三维透视”——这个问题已经被回答。它们要面对的问题将是“如何在一个有硬件加速3D显卡的世界中,重新谈判引擎与硬件之间的契约”。而这个问题的答案,将定义下一个十年的引擎范式。