当前位置: 首页 > news >正文

任天堂 64 缺乏加法混合效果?这项技术让特效无溢出伪影!

任天堂 64 上的加法混合效果

各位是否曾疑惑,为什么原版 PlayStation 上的爆炸和其他特效看起来比任天堂 64(Nintendo 64)上的要酷炫得多?原因就在于加法混合(additive blending),或者更确切地说,是 N64 缺乏这种效果。虽然 N64 实际上支持加法混合,但在实际应用中却几乎无法使用。

PlayStation

PlayStation(PSX)支持 4 种不同的混合模式(除了直接覆盖像素之外),用于控制精灵(sprite)和几何图形如何与现有的帧缓冲区(frame buffer)进行混合:

0: (源颜色 + 目标颜色) / 2
1: 源颜色 + 目标颜色
2: 目标颜色 - 源颜色
3: 目标颜色 + 源颜色 / 4

在《沉默轰炸机》中使用的是概念上最简单的模式:`源颜色 + 目标颜色`。也就是说,颜色会直接添加到帧缓冲区中现有的颜色上。

| R | G | B |
| 源颜色(精灵) | 171 | 42 | 226 |
| + 目标颜色(帧缓冲区) | 63 | 141 | 170 |
| = 结果 | 234 | 183 | 255 |

在场景上绘制精灵只会让画面变得更亮,而不会变暗。这非常适合用于爆炸、等离子束和魔法咒语等特效。值得注意的是,在这个例子中,`B` 值相加为 `396`,但 PSX 的图形处理器(GPU)会将其限制在 `255` 的最大值范围内。(顺便提一下,PSX 的 GPU 实际上只使用 16 位精度,每个颜色分量为 5 位,因此值的范围是 `0 .. 31`,但原理是一样的。)

任天堂 64

N64 的“现实显示处理器”(Reality Display Processor,简称 RDP,一种固定功能的光栅化器)有一种更灵活的方式来控制混合效果:可配置的“颜色组合器”(Color Combiner)。这有点类似于 OpenGL 的 `glBlendFunc()` 函数。

[Libdragon] 通过 `RDPQ_BLENDER((P, A, Q, B))` 宏来实现这一功能,该宏指示 RDP 执行 `(P * A) + (Q * B)` 操作,其中每个“插槽”可以是几种输入之一。

使用这个宏来设置加法混合非常简单:

RDPQ_BLENDER(( IN_RGB, IN_ALPHA, MEMORY_RGB, ONE ))

问题在于,RDP 不会对结果进行限制。

| R | G | B |
| 源颜色(精灵) | 171 | 42 | 226 |
| + 目标颜色(帧缓冲区) | 63 | 141 | 170 |
| = 结果 | 234 | 183 | 140 |
^
溢出了!

这样得到的输出效果并不理想。

当然,你可以选择在 N64 的向量协处理器“现实信号处理器”(Reality Signal Processor,简称 RSP)上绘制这些特效。但如果你想进行旋转、缩放或任何实际的 3D 操作,这很快就会变得复杂起来。而 RDP 更适合做这些事情,显示本来就是它的职责!

虽然 RDP 可以绘制到 32 位缓冲区中,但游戏很少这样做。几乎所有 N64 游戏最终输出都使用 16 位帧缓冲区。不过,考虑到这一点,想出了一个不同的方案:让 RDP 绘制到一个 32 位的 `RGBA 8888`(每个分量 8 位)缓冲区中,但将所有精灵限制在 16 位的 `RGBA 5551`(每个颜色分量 5 位,1 位透明度)范围内。可以通过将 RGB 值除以 `8`(或者右移 `3` 位)来对资源进行预处理。这样做会让所有东西看起来太暗,但反过来也为加法混合提供了很大的空间。

当所有加法混合的精灵结果都小于 255 时,不会发生溢出。

更棒的是,不需要离线进行图像预处理。可以在绘制时让颜色组合器为完成这个任务,而且是免费的!

// 利用雾的透明度值,以 1/8 的强度绘制所有颜色
rdpq_set_fog_color(RGBA32(0, 0, 0, 256/8));
rdpq_mode_blender(RDPQ_BLENDER(( IN_RGB, FOG_ALPHA, MEMORY_RGB, ONE )));

那么,如何将其恢复到正常亮度呢?很简单:使用 16 位帧缓冲区进行显示,并将所有 32 位颜色“复制”到其中。只需要小心地将所有 8 位颜色分量限制在 5 位范围内。

void cpu_rgba_8888_to_5551(uint32_t *rgba32_in, uint16_t *rgba16_out) {
for (int i = 0; i < 320 * 240; i++) {
color_t c = color_from_packed32(rgba32_in[i]);
if (c.r > 31) { c.r = 31; }
if (c.g > 31) { c.g = 31; }
if (c.b > 31) { c.b = 31; }
rgba16_out[i] = (c.r << 11) | (c.g << 6) | (c.b << 1) | 0x1;
}
}

当然,在 CPU 上执行这个操作成本非常高。对于一个 320×240 的帧,大约需要 70 毫秒。但这正是 RSP 协处理器发挥优势的地方。现在问题变得简单多了。

RSP 的 128 位向量指令可以一次处理 8 个像素。在 #N64Brew Discord 上的 HailToDodongo 的帮助下,对 GPU 微代码进行了优化,现在整个帧的处理时间约为 3.1 毫秒!(小知识:在 N64 的语境中,通常所说的“GPU 微代码”实际上是在 RSP 上运行的 MIPS 汇编代码,或者就像最近开始称呼的那样,MIPS 加汇编。)

现代的 N64 开发工具非常出色。虽然了解一些汇编知识会有帮助,但不再需要手动编写 MIPS 汇编代码了。HailToDodongo 发明了一种类似 C 语言的语言 [RSPL],它可以直接编译成汇编代码。

所以整个设置如下:

// 使用 16 位帧缓冲区初始化显示
display_init(RESOLUTION_320x240, DEPTH_16_BPP, 3, GAMMA_NONE, FILTERS_DISABLED);

// 创建一个 32 位的辅助渲染缓冲区,并将其设置为渲染目标
surface_t render32 = surface_alloc(FMT_RGBA32, 320, 240);
rdpq_set_color_image(render32);

// 配置颜色组合器,以 1/8 的强度绘制
rdpq_set_fog_color(RGBA32(0, 0, 0, 256/8));
rdpq_mode_blender(RDPQ_BLENDER((IN_RGB, FOG_ALPHA, MEMORY_RGB, ONE)));

// 绘制包含大量加法混合精灵的场景
render_scene();

// 在 RSP 上启动从 32 位渲染缓冲区到 16 位帧缓冲区的转换
rsp_rgba_8888_to_5551(render32->buffer, screen->buffer);

// 显示 16 位帧缓冲区
display_show(screen);

这样就可以得到大量没有溢出伪影的加法混合精灵特效。

当然,大多数游戏一开始就使用 16 位帧缓冲区是有原因的:N64 的内存吞吐量非常糟糕。与 16 位缓冲区相比,绘制到 32 位缓冲区几乎需要两倍的时间,因为 RDP 必须从存储在 RDRAM 中的帧缓冲区中来回传输两倍的数据。

尽管如此,这种技术的效果比预期的要好。对于某些应用来说,它肯定足够好了。还看到了进一步优化的潜力,比如只将需要加法混合的精灵绘制到 32 位缓冲区中,甚至可以降低分辨率,然后在 RSP 上将其与场景的其他 16 位缓冲区合并……

提醒各位,上述视频的简单演示项目可以在 GitHub 上找到。

http://www.cnnetsun.cn/news/2421560.html

相关文章:

  • 新能源电网电磁暂态仿真方法【附仿真】
  • 搞可靠性就是浪费钱?
  • 阅读APP书源终极配置指南:3步免费获取海量小说资源 [特殊字符]
  • FSearch终极指南:如何在Linux上实现秒级文件搜索
  • 微信数据管理遇难题?本地化方案PyWxDump的合规启示与技术探索
  • Office RibbonX Editor:免费开源工具助你轻松定制Office界面
  • Unlock Music终极指南:如何在浏览器中免费解锁12种加密音乐格式
  • J公司S车间布局优化【附代码】
  • 【ElevenLabs阿拉伯文语音实战指南】:20年AI语音工程师亲授7大本地化陷阱与3步高保真合成法
  • Pearcleaner深度解析:如何构建macOS应用残留清理的专业级架构?
  • 重构的黄金法则:别一边加功能一边改结构
  • 基于Arduino与NeoPixel的可穿戴LED智能帽子制作全解析
  • 告别网页!用ESP32-CAM+ST7789屏幕打造你的离线监控小电视(附完整代码)
  • Go语言设计模式:创建型模式
  • 量子计算优化Benders分解:减少量子比特与提升收敛效率
  • STM32 IAP升级后APP程序中断不响应?手把手教你配置VTOR寄存器搞定
  • LaTeX中文排版一站式解决方案:告别字体配置烦恼
  • 2026年5月最新盘点:为什么选择必火AI数字人?
  • 华为AirEngine5760-10通过SFTP恢复Fit模式实战指南
  • 游戏开发工作流程
  • 别再手动对比了!在Ubuntu 22.04上5分钟搞定Beyond Compare 4安装与破解(附最新可用密钥)
  • 中望3D 2021隐藏技巧:用基准坐标系(Datum CSYS)玩转高效装配与工程图
  • 告别静态图:用PPT 3D对象实现分子结构的“沉浸式”讲解
  • C公司N产品装配线平衡优化【附代码】
  • DPDK TestPMD实战:如何用多核配置压测出万兆网卡的真实转发性能?
  • 别再只做静态展示了!用Vue+Unity WebGL给你的数字孪生模型注入实时数据灵魂(附Node.js后端源码)
  • 在ZYNQ上玩转uCOSIII网络功能:从Hello World到TCP/IP通信实战
  • 基于ZYNQ的阵列涡流检测硬件架构:从多通道采集到数字相敏检波
  • 告别命令行恐惧!用SourceTree可视化搞定Git分支、合并与冲突(附实战截图)
  • 3D打印与EL电致发光技术:打造可穿戴发光艺术品的完整指南