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

发布时间:2026/5/17 0:04:13

任天堂 64 缺乏加法混合效果?这项技术让特效无溢出伪影! 任天堂 64 上的加法混合效果各位是否曾疑惑为什么原版 PlayStation 上的爆炸和其他特效看起来比任天堂 64Nintendo 64上的要酷炫得多原因就在于加法混合additive blending或者更确切地说是 N64 缺乏这种效果。虽然 N64 实际上支持加法混合但在实际应用中却几乎无法使用。PlayStationPlayStationPSX支持 4 种不同的混合模式除了直接覆盖像素之外用于控制精灵sprite和几何图形如何与现有的帧缓冲区frame buffer进行混合0: (源颜色 目标颜色) / 21: 源颜色 目标颜色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但原理是一样的。任天堂 64N64 的“现实显示处理器”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 上找到。

相关新闻