当现代 GPU 以每秒数万亿次浮点运算的速度渲染场景时,一群开发者却在刻意回到 1993 年的技术约束中 ——320x240 分辨率、256 色调色板、纯 CPU 软件光栅化。这种看似倒退的选择,实则是对固定功能渲染管线本质的重新探索。
调色板渲染的核心机制
VGA Mode 13h(及其变种 Mode-X 320x240)定义了一代 PC 游戏的视觉风格:每像素仅 1 字节,作为索引指向 768 字节(256 色 ×3 通道)的调色板。这种约束迫使每个颜色选择都必须 "谨慎而有目的"。与现代 RGBA32 纹理不同,调色板系统要求开发者在资源创建阶段就完成色彩量化,运行时引擎只处理字节索引而非完整色值。
在软件光栅化管线中,帧缓冲区就是一块线性内存区域。绘制像素简化为单字节写入操作 —— 没有着色器、没有 VRAM 抽象、没有驱动开销。这种直接性带来了可预测的性能特征:每帧的内存写入量精确可控(320×240=76.8KB),缓存行为完全由扫描线顺序决定。
Colormap:O (1) 光照查询的预计算策略
纯调色板渲染面临的最大挑战是动态光照。现代 GPU 在片元着色器中执行浮点颜色乘法,而调色板系统只能操作索引值。若要在运行时对每个像素搜索 "更暗的调色板颜色",计算开销将彻底摧毁帧率。
解决方案是预计算 Colormap—— 一个 256×32 的二维查找表。对于调色板中的每种颜色,预先生成 31 个更暗的变体(共 32 级深度)。生成过程利用 Oklab 色彩空间的感知距离公式,而非简单的欧氏距离,避免暗部色调向灰色漂移。关键优化在于:将 Colormap 行索引计算从逐像素移至逐列(墙体)或逐行(地板),使每帧仅需执行 320+240=560 次距离计算,而非全屏 76,800 次。
运行时光照查询简化为两次内存访问:根据距离确定行索引,根据原色索引确定列偏移,取出的字节即为暗化后的调色板索引。这是严格的 O (1) 操作。
固定点与浮点的工程权衡
1993 年渲染管线在数值表示上存在微妙权衡。游戏逻辑采用固定点数学(如 16.16 格式),确保跨平台确定性行为 —— 这对网络同步和回放系统至关重要。渲染管线则使用浮点,因为像素输出的微小差异不会破坏游戏状态。
这种分离体现了系统设计的核心原则:确定性边界应精确划定。物理模拟、游戏状态更新需要位级一致,而视觉输出允许平台相关的微小偏差。
可落地的实现参数
若要在现代平台复现此管线,建议采用以下配置:
分辨率与色彩
- 目标分辨率:320×240(4:3 方形像素)
- 色深:256 色调色板,透明色预留索引 0
- 帧缓冲:76,800 字节线性数组
Colormap 预计算
- 深度层级:32 级(0 = 原色,31 = 最暗)
- 色彩空间:Oklab 感知距离
- 可选:暗部向暖色调偏移(hue shifting)
距离计算优化
- 墙体:每列计算一次 Colormap 行索引
- 地板 / 天花板:每行计算一次
- 精灵:每个可见实例计算一次
数值类型
- 游戏逻辑:32 位固定点(16.16)
- 渲染管线:单精度浮点
- 坐标变换:固定点转浮点仅在顶点处理阶段
资源管线集成
现代工具链可自动化调色板量化流程。使用 Blender Python API 批量渲染 3D 模型,通过 perceptual closest color 算法将真彩色输出映射到 256 色调色板。关键是在资源导入阶段完成所有色彩压缩,运行时引擎只处理预量化的字节数据。
这种架构的副作用是资源与代码的强耦合 —— 修改调色板意味着重新量化全部纹理。但在单人开发或小型团队中,这种约束反而简化了版本控制:纹理源文件以真彩色存储,构建脚本生成引擎专用的.TEX 格式。
结语
软件光栅化不是对现代 GPU 的否定,而是对渲染本质的回归。当每个像素的成本都精确可知,开发者被迫在约束中做出深思熟虑的选择 —— 这种 "限制催生创造力" 的哲学,正是 1993 年游戏视觉风格的根源。Colormap 技术证明,预计算与查找表仍是性能优化的有效手段,即使在拥有 teraflops 算力的今天。
对于独立开发者而言,这种复古管线提供了可预测的性能基线和完整的渲染控制权 —— 无需驱动更新、无需着色器编译、无需硬件兼容性测试。在特定类型的项目中,这些优势可能远超现代 API 的灵活性。
参考来源
- Marko Stanić, "Catlantean 3D - Making Graphics Like It's 1993", 2026-06-09. https://staniks.github.io/articles/catlantean-3d-blog-1
内容声明:本文无广告投放、无付费植入。
如有事实性问题,欢迎发送勘误至 i@hotdrydog.com。