Catlantean 3D: 使用现代工具重现 1993 年时代的图形
在现代环境中实现复古约束
Catlantean 3D 是一款第一人称射击游戏,旨在通过利用现代编译器和平台抽象层,来复制 1990 年代初期的游戏技术限制。该项目遵循一套严格的自设约束,以确保真实的复古美学和确定性行为。
核心技术约束包括:
- 分辨率与颜色: 目标分辨率为 320x240,并使用 256 色的有限调色板。
- 渲染与音频: 所有渲染和声音混音均从零开始手动实现。
- 确定性: 游戏逻辑使用定点数数学运算,以保证跨平台的行为一致性,而浮点数数学运算则保留给渲染过程。
- 抽象层: 平台抽象层仅限于基础的帧缓冲访问、键盘/鼠标输入、音频缓冲写入以及文件系统 I/O。
基于调色板的渲染与光照
为了实现 VGA 时代游戏的“锐利”外观,Catlantean 3D 使用线性帧缓冲,其中每个像素都是一个索引到 256 色调色板的单字节。
色彩映射光照系统
传统的射线投射(raycasting)往往会导致光照平淡。为了在不增加现代着色器开销的情况下创造深度感,Catlantean 3D 实现了一种色彩映射(colormap)——一种用于快速、O(1) 光照查找的调色板索引二维矩阵。
色彩映射的过程如下:
- 线性化: 将 256 色调色板视为一个线性行。
- 阴影生成: 为每种颜色预先计算 31 种更暗的变体。系统使用 Oklab 色彩空间及其感知距离公式来寻找调色板中匹配度最高的颜色,从而避免了欧几里得距离常见的“冷色调”或灰色偏移问题。
- 色调偏移: 随着颜色变暗,会应用轻微的暖色调偏移,以提升视觉吸引力。
- 运行时查找: 渲染器根据与摄像机的距离计算阴影索引,并从色彩映射矩阵中检索暗化的调色板索引。
为了优化性能,对于墙壁,色彩映射行索引在每个屏幕列中仅计算一次;对于地面,在每个屏幕行中仅计算一次;对于精灵(sprite)则每个精灵计算一次。
资产创建流水线
Catlantean 3D 中的资产分为三类:预渲染、手绘和程序化生成。
预渲染精灵
为了简化复杂动画的迭代,3D 模型在 Blender 中创建并进行骨骼绑定。这些模型使用利用 Blender API 的 Python 脚本渲染为纹理。为了防止缩放导致的“模糊”感,开发者使用 Blender 的合成节点来增加对比度和清晰度,然后通过 Python 脚本进行 Oklab-based palette quantization(基于 Oklab 的调色板量化)。
手绘艺术与像素一致性
某些元素,例如状态栏头像和 HUD,是在 Aseprite 中手绘的,以确保情感表达和清晰度。该项目的一个关键规则是保持一致的像素比例:一个世界单位等于 64 像素。这可以防止不同分辨率资产混合时产生的视觉突兀感,这是低质量独立游戏中的常见问题。
程序化生成与“碎裂效果”(Gibbing)
对于具有重复磨损和污垢的纹理,Python 脚本使用高度图、噪声图和污垢图生成最终纹理。对于“碎裂效果”(即敌人死亡动画)使用了更复杂的程序化流水线:
- Voronoi 分解: 根据随机种子像素将精灵分割成块。
- 伤口渗血: 使用广度优先搜索(BFS)从块边界向内扩散,将颜色混合向调色板定义的血液渐变层。
- 物理模拟: 每个块被分配一个质心、速度、旋转和重力,从而在无需运行时碰撞检测的情况下模拟爆炸效果。
自定义工具与地图编辑
由于 Tiled 等通用工具缺乏对光照等级绘制和特定单元格标志的原生支持,开发者使用 wxPython 构建了一个自定义地图编辑器。
该编辑器通过名为 pybast 的库(通过 pybind 实现 C++ 引擎内部的 Python 绑定)与引擎集成,允许编辑器直接读取游戏数据存档和纹理。这创造了一个紧密的生态系统,使得地图编辑器成为开发过程中的一等公民,并随游戏一同发布,以支持社区模组开发。
社区洞察与技术背景
围绕该项目的讨论突出了所使用的约束条件的的历史意义。贡献者指出,原始的 VGA Mode 13h (320x200) 使用非正方形像素,因为 64,000 字节的缓冲器可以完美适配 16 位段地址,从而简化了早期 CPU 的寻址。
其他开发者观察到,Catlantean 3D 使用的射线投射引擎更接近 Wolfenstein 3D(垂直墙壁、恒定的地面/天花板高度),而非基于 BSP 的 Doom 引擎,后者允许更灵活的墙壁角度和变量高度。