Nintendo 64 上的加法混合(Additive Blending)

Lobsters Hottest 工具

摘要

文章介绍了在 Nintendo 64 上实现加法混合的技术挑战,由于该平台缺乏颜色钳制功能,作者提出了一种使用 Libdragon 的解决方案,通过管理 32 位缓冲区并进行颜色转换来实现更出色的视觉效果。

<p><a href="https://lobste.rs/s/pavhcb/additive_blending_on_nintendo_64">评论</a></p>
查看原文
查看缓存全文

缓存时间: 2026/05/08 09:40

# Nintendo 64 上的加法混合 来源:https://phoboslab.org/log/2026/05/n64-additive-blending 你有没有想过,为什么爆炸和其他特效在初代 PlayStation 上看起来比在 Nintendo 64 上酷得多? *PSX 上的《Silent Bomber》* *N64 上的《Star Fox 64》* 原因在于加法混合!或者说,在 N64 的情况下,**缺少**这项功能。虽然 N64 实际上确实支持加法混合,但它实际上完全无法使用。 ### PSX PSX 支持 4 种不同的混合模式(除了直接覆盖像素之外),用于控制精灵和几何体如何与现有的帧缓冲混合: ``` 0: (src + dst) / 2 1: src + dst 2: dst - src 3: dst + src/4 ``` 你在《Silent Bomber》中看到的就是概念上最简单的一种:`src + dst`。也就是说,颜色只是被加到帧缓冲中已有的颜色上。 ``` | R | G | B | | src (精灵) | 171 | 42 | 226 | | + dst (帧缓冲) | 63 | 141 | 170 | | = 结果 | 234 | 183 | 255 | ``` 在场景上绘制精灵只能让它**更亮**,永远不会更暗。非常适合爆炸、等离子束和魔法咒语。重要的是,注意这个例子中的 `B` 值相加达到了 `396`,但 PSX 的 GPU 会友好地将其限制在最大范围 `255` 内。 (题外话:PSX 的 GPU 实际上只以 16 位精度工作,颜色分量为 5 位,所以数值范围是 `0 .. 31`;数学原理是一样的。) ### N64 N64 的"Reality Display Processor"(固定功能光栅化器,简称 RDP)有一种更灵活的混合控制方式:可配置的"Color Combiner"。这与 OpenGL 的 `glBlendFunc()` 有些相似。 Libdragon (https://libdragon.dev/) 通过 `RDPQ_BLENDER((P, A, Q, B))` 宏公开了这项功能,该宏指示 RDP 执行 `(P * A) + (Q * B)`,其中每个*槽位*可以是几种输入之一。 用这个设置加法混合很简单: ``` RDPQ_BLENDER(( IN_RGB, IN_ALPHA, MEMORY_RGB, ONE )) ``` 问题是,RDP 不会对结果进行钳制。 ``` | R | G | B | | src (精灵) | 171 | 42 | 226 | | + dst (帧缓冲) | 63 | 141 | 170 | | = 结果 | 234 | 183 | 140 | ^ 回绕了! ``` 最终的输出效果很不理想: 当然,你可以选择在"Reality Signal Processor"(RSP,N64 的向量协处理器)上绘制这种效果。但如果你想要做旋转、缩放或任何真正的 3D 内容,这很快就会变得复杂。RDP 更适合这个任务。显示就是它的本职工作! 虽然 RDP*可以*绘制到 32 位缓冲中,但游戏很少这样做。几乎所有 N64 游戏都使用 16 位帧缓冲作为最终输出。但想到这一点,我想出了一个不同的方案: 让 RDP 绘制到一个 32 位的 `RGBA 8888`(每分量 8 位)缓冲中,但将所有精灵放在 16 位的 `RGBA 5551`(每颜色分量 5 位,1 位 alpha)范围内。我可以预先处理资源,将 RGB 除以 `8`(或右移 `3` 位)。这本质上会让所有东西画出来都太暗,但反过来给了我们大量的余量用于加法混合。 *当所有加法混合的精灵结果都小于 255 时,不会发生回绕* 更好的是,我们不需要离线进行这种图像预处理。我们可以直接指示颜色组合器在绘制时为我们完成。免费的! ``` // 滥用雾效 alpha 值,让所有颜色以 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 位范围内。 ```c 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 的一些优化帮助下,现在处理整帧大约只需要 3.1 毫秒! (*趣闻*:我想插一句。在 N64 的语境中通常被称为"GPU 微码"的东西,实际上是运行在 RSP 上的 MIPS/汇编,或者像我最近开始称呼的那样,MIPS 外加汇编。) 现代 N64 开发工具非常出色。虽然了解一些汇编知识会有帮助,但你不再需要手写 MIPS 汇编了。HailToDodongo 发明了一种名为 RSPL (https://github.com/HailToDodongo/rspl) 的类 C 语言,可以直接编译为 MIPS 汇编。 所以整个设置看起来是这样的: ```c // 用 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); ``` 结果是大量 glorious 的加法混合精灵,没有任何回绕伪影。 当然,大多数游戏最初使用 16 位帧缓冲是有原因的:N64 糟糕的内存吞吐量。绘制到 32 位缓冲比 16 位缓冲几乎多花一倍的时间,因为 RDP 必须从存储在 RDRAM 中的帧缓冲来回搬运两倍的数据量。 不过,这项技术的效果比我预期的要好。对于某些应用来说肯定够用了。 我还可以看到进一步优化潜力:只将需要加法混合的精灵绘制到 32 位缓冲——甚至可以以更低分辨率——然后在 RSP 上将其与场景的其余 16 位缓冲合并…… 上面视频的一个简单演示项目可以在 GitHub 上找到:github.com/phoboslab/n64_addblend (https://github.com/phoboslab/n64_addblend)

相似文章

SNES: 精灵与背景渲染

Fabien Sanglard

本文解释了SNES的PPU如何在紧张的VRAM带宽限制下渲染精灵和背景,描述了不同视频模式中的硬件权衡。

这个周末你打算做什么?

Lobsters Hottest

一位开发者描述了将《完美黑暗64》关卡移植到 noclip.website 的过程,强调了读取 N64 显示列表和重新实现渲染引擎的挑战。

像1993年那样制作图形

Hacker News Top

一位开发者详细介绍了如何构建《Catlantean 3D》——一款采用1993年时代图形技术(256色、320x240分辨率、手工制作资产、无人工智能)的第一人称射击游戏,计划在Steam上发布,重点讲解调色板渲染和资产创建。

SNES 图形系统工作原理

Fabien Sanglard

基于 Jonathon Donaldson 的原理图,对超级任天堂图形硬件的详细技术解释,包括 PPU1 和 PPU2 芯片、VRAM、OAM 和 CGRAM。