Catlantean 3D: 使用现代工具重现 1993 年时代的图形

在现代环境中实现复古约束

Catlantean 3D 是一款第一人称射击游戏,旨在通过利用现代编译器和平台抽象层,来复制 1990 年代初期的游戏技术限制。该项目遵循一套严格的自设约束,以确保真实的复古美学和确定性行为。

核心技术约束包括:

  • 分辨率与颜色: 目标分辨率为 320x240,并使用 256 色的有限调色板。
  • 渲染与音频: 所有渲染和声音混音均从零开始手动实现。
  • 确定性: 游戏逻辑使用定点数数学运算,以保证跨平台的行为一致性,而浮点数数学运算则保留给渲染过程。
  • 抽象层: 平台抽象层仅限于基础的帧缓冲访问、键盘/鼠标输入、音频缓冲写入以及文件系统 I/O。

基于调色板的渲染与光照

为了实现 VGA 时代游戏的“锐利”外观,Catlantean 3D 使用线性帧缓冲,其中每个像素都是一个索引到 256 色调色板的单字节。

色彩映射光照系统

传统的射线投射(raycasting)往往会导致光照平淡。为了在不增加现代着色器开销的情况下创造深度感,Catlantean 3D 实现了一种色彩映射(colormap)——一种用于快速、O(1) 光照查找的调色板索引二维矩阵。

色彩映射的过程如下:

  1. 线性化: 将 256 色调色板视为一个线性行。
  2. 阴影生成: 为每种颜色预先计算 31 种更暗的变体。系统使用 Oklab 色彩空间及其感知距离公式来寻找调色板中匹配度最高的颜色,从而避免了欧几里得距离常见的“冷色调”或灰色偏移问题。
  3. 色调偏移: 随着颜色变暗,会应用轻微的暖色调偏移,以提升视觉吸引力。
  4. 运行时查找: 渲染器根据与摄像机的距离计算阴影索引,并从色彩映射矩阵中检索暗化的调色板索引。

为了优化性能,对于墙壁,色彩映射行索引在每个屏幕列中仅计算一次;对于地面,在每个屏幕行中仅计算一次;对于精灵(sprite)则每个精灵计算一次。

资产创建流水线

Catlantean 3D 中的资产分为三类:预渲染、手绘和程序化生成。

预渲染精灵

为了简化复杂动画的迭代,3D 模型在 Blender 中创建并进行骨骼绑定。这些模型使用利用 Blender API 的 Python 脚本渲染为纹理。为了防止缩放导致的“模糊”感,开发者使用 Blender 的合成节点来增加对比度和清晰度,然后通过 Python 脚本进行 Oklab-based palette quantization(基于 Oklab 的调色板量化)。

手绘艺术与像素一致性

某些元素,例如状态栏头像和 HUD,是在 Aseprite 中手绘的,以确保情感表达和清晰度。该项目的一个关键规则是保持一致的像素比例:一个世界单位等于 64 像素。这可以防止不同分辨率资产混合时产生的视觉突兀感,这是低质量独立游戏中的常见问题。

程序化生成与“碎裂效果”(Gibbing)

对于具有重复磨损和污垢的纹理,Python 脚本使用高度图、噪声图和污垢图生成最终纹理。对于“碎裂效果”(即敌人死亡动画)使用了更复杂的程序化流水线:

  1. Voronoi 分解: 根据随机种子像素将精灵分割成块。
  2. 伤口渗血: 使用广度优先搜索(BFS)从块边界向内扩散,将颜色混合向调色板定义的血液渐变层。
  3. 物理模拟: 每个块被分配一个质心、速度、旋转和重力,从而在无需运行时碰撞检测的情况下模拟爆炸效果。

自定义工具与地图编辑

由于 Tiled 等通用工具缺乏对光照等级绘制和特定单元格标志的原生支持,开发者使用 wxPython 构建了一个自定义地图编辑器。

该编辑器通过名为 pybast 的库(通过 pybind 实现 C++ 引擎内部的 Python 绑定)与引擎集成,允许编辑器直接读取游戏数据存档和纹理。这创造了一个紧密的生态系统,使得地图编辑器成为开发过程中的一等公民,并随游戏一同发布,以支持社区模组开发。

社区洞察与技术背景

围绕该项目的讨论突出了所使用的约束条件的的历史意义。贡献者指出,原始的 VGA Mode 13h (320x200) 使用非正方形像素,因为 64,000 字节的缓冲器可以完美适配 16 位段地址,从而简化了早期 CPU 的寻址。

其他开发者观察到,Catlantean 3D 使用的射线投射引擎更接近 Wolfenstein 3D(垂直墙壁、恒定的地面/天花板高度),而非基于 BSP 的 Doom 引擎,后者允许更灵活的墙壁角度和变量高度。

Sources