这一部分有些不太寻常,因为这款游戏机不仅有多个屏幕需要绘制,而且把传统的图块(tile)引擎与现代渲染器结合在了一起。
首先说说硬件:NDS包含两个LCD屏幕,每个屏幕的都有256x192个像素,比GBA多约20%个像素点。 它们可以显示262144种颜色(18位),刷新率约60 Hz。
架构
图形子系统可以绘制2D和3D对象。 前者由二维几何图形组成,其中填充了8x8像素的位图(称为"图块"),而后者则使用顶点绘制三维对象(多边形)。
深入了解操作这些屏幕的内部芯片,我们可以观察到这个掌机具有独特的2D和3D几何图形硬件。 2D数据由我们熟悉的引擎PPU(现在只称之为2D引擎)操作,而3D数据由一个全新的子系统处理。 值得一提的是,虽然这并不是第一款推出3D图形功能的游戏机,但它仍然是第一款使用自研图形渲染器来呈现3D图形的游戏机。
现在,这两个引擎必须连接到两个屏幕其中之一。对于只有2D图形的游戏来说,这不是问题,因为每个屏幕都有一个2D引擎。 然而,那些想展示前沿特性的游戏却只有一个3D引擎可用。 因此,每次只能在一个屏幕上使用3D功能。 但是2D和3D对象如何混合显示? 别急,我先分别解释清楚这两个引擎,之后再讨论这个问题。
使用2D图形构建帧
在我们检查各阶段之前,我建议先阅读有关GBA PPU的这篇文章,因为在这里我只会提到构建"下一代"2D游戏的变化。 另外,由于有两个引擎,这里我们把第一个引擎称为主引擎(Main),第二个则称为辅助引擎(Sub)。 虽然这并不一定意味着哪个屏幕连接了哪个引擎, 而且主引擎比辅助引擎包含更多的功能。
我将借用新超级马里奥兄弟(New Super Mario Bros)的资源(assets)来帮助解释。
图块(Tiles)
到目前为止,我们都知道基本的图块系统是如何工作的,但是NDS的图块处理有什么特别之处呢? 其实,总共有656 KB VRAM可用,这一块被分割成不同的bank:四个128 KB bank、一个64 KB bank、一个32 KB bank和三个16 KB bank。 开发者可以随便往bank里填充数据并将引擎指向所需数据的位置。 两个引擎都可以从这些bank读数据,但是它们不能同时访问同一个bank。
尽管如此,数据排布方式还是有一些限制。 例如,ARM7只能访问两个128 KB的bank, 这两个bank不能存储sprite;最后16 KB bank只能被"辅助引擎"访问; 等等等等......你懂得。
最后一句,3D引擎我们稍后再讨论,它可以访问其中某些bank以获取纹理(texture)。
背景类型
从Super Nintendo时代开始,PPU一直致力于为构建背景图层提供更多的灵活性。 14年后,有这样一款芯片,可以获取图块并应用许多仿射变换。甚至,最终可以从frame buffer构建图层。
在我们讨论2D引擎显示背景的不同模式之前,让我们先了解一下引擎可以生成哪些类型的背景:
- 字符类型(Character type)组:这些背景类型遵循传统的图块系统,通过填充图块来渲染帧。
- 静态(Static)或者说"文本"(text)背景: 普通背景, 最大支持512x512像素,256色和16个调色板。 包括了所有典型的特效(effects)(H/V翻转、H/V滚动、马赛克、alpha混合),外加一个额外的"褪色"效果。 最多可使用1024个图块。
- 仿射(Affine) 背景:一个带有仿射变换(affine transformations)的背景。 不支持水平/垂直翻转,并且只能获取256个图块(最大值的四分之一)。 图层大小为1024x1024像素。
- 仿射扩展 - Character:与仿射背景相同,但是可使用1024个图块,并且支持水平/垂直翻转。
- 位图(Bitmap)类型组:引擎不再处理图块,直接把VRAM用作frame buffer。
- 仿射扩展 - 256色:继承"仿射扩展 - character"中所有可用的特效。 区别在于本模式将它们应用于单个512x512 px位图。
- 仿射扩展 - 真彩色:与256色扩展类似,但本模式下frame buffer支持的颜色可达32768种(15比特)。
- 大屏幕(Large screen):利用所有四块128 KB大的VRAM块来渲染一个1024x512 px大的frame-buffer。
- 3D背景:将3D引擎的渲染结果显示为背景图层,这是展示3D引擎处理结果所必需的。 虽然它没有提供很多2D效果,但它有一些有趣的特性,比如水平滚动和alpha混合(与其他背景层)。 此外,它是唯一支持多达262,144种颜色(18位)的类型。
开发者不能任意选择背景类型。但是游戏机提供了一系列背景模式,为各类型设置了不同的组合。 尽管如此,你可以在这里看到Affine扩展模式有三种不同的风格("字符","256色"和"直接颜色")。 因此,在下一节中,当你看到X背景模式支持"仿射扩展"类型时,开发者可以选择他们想要的变体。
背景模式

背景图层0 (BG0)。 这个特定的图层将在某些扫描线上水平位移以模拟云的移动。
背景类型实战演示。 "主"与"副"提供多种操作模式。 所有这些模式生成四个背景图层,然而,每个图层的功能取决于所激活的模式:
- 模式0:4个静态图层。
- 模式1:3个静态图层+1个仿射图层。
- 模式2:2个静态图层+2个仿射图层。
- 模式3:3个静态图层+1个仿射扩展图层。
- 模式4:2个静态图层+1个仿射图层+1个仿射扩展图层。
- 模式5:2个静态图层+2个仿射扩展图层。
- 这是最常用的模式,因为极其灵活。
- 模式6:1个3D背景图层+1个大屏幕。
- 由于VRAM bank的空间只够一个frame buffer,因此只有主引擎能使用该模式。
此外,在模式0到5中,'主'引擎可以将第一个静态图层用作3D背景。 3D功能将在稍后讨论。
Sprites
Sprites或者说"objects"继承了GBA PPU的功能,但新增两个重要功能。
首先,OAM(存储sprite条目(sprites entries)的区域)的大小现在有2 KB,使每屏每帧可以显示多达128个sprite。 因此,两个引擎都分配有1 KB。
其次,OAM现在可以引用VRAM中的位图,而不仅仅使用图块和调色板。 这是一种与tile系统不同的方法。 实际上,同一帧中可以同时存在这两种sprite"模式",因为此选项是在每个单独的 sprite 上设置的。
结果
由于每个图层都是即时渲染的,最后阶段需要合并所有内容并将其发送到所选屏幕。 以前基于PPU渲染游戏机基本如此,但这是否意味着NDS也到这里结束了?
还没呢! 主引擎仍然必须从另一个引擎——最强大的引擎——获取图层。
3D 加速
如果你之前玩过NDS,那么你就知道这款游戏机可以显示一定数量的3D图形。 不同于GBA游戏,这些3D图形并非由CPU处理。 不过,CPU-NTR包括两个组件来构建3D引擎。 有趣的是,任天堂采用的设计让我想起了SGI的RCP。
回顾"背景模式"部分,你会注意到每个模式都至少有一个static背景,这是因为你可以用3D引擎生成的图形来填充该图层。 唯一需要注意的是只有主引擎可以这样做,这也是模式6仅适用于主引擎的原因之一。
几何引擎
如果读过第五代或第六代游戏机的文章,你可能会想知道...... SIMD 处理器哪去了? 这是个好问题,因为ARM9并不擅长矢量运算,而且我不认为专用除法器很够用。 这就是为什么任天堂嵌入了一个称为几何引擎的组件,负责处理顶点转换、投影、光照、裁剪、剔除和多边形排序,最后一项对正确使用透明特性至关重要。
这个引擎有一些严格的限制,特别是它能够处理的多边形数量:有额外的248 KB RAM可用于存储处理过的几何体,数量可以达到2048个三角形或1706个四边形。使用多边形条带的话(而不是单个多边形)还可以存储更多。 为了对这个数字有一些概念,我建议查看之前文章中的"交互模型"部分,你会发现这个限制令人担忧,但不要忘记掌机的屏幕分辨率也要小得多......所以算是抵消了一点。
无论如何,这个引擎是通过一个Command FIFO来控制的,其数据是由CPU或DMA填充。 该FIFO可以存储256个条目,并且它还有一个叫做PIPE的缓冲区,用于存储额外的四个命令(总共260个命令)。
渲染引擎
渲染引擎负责将向量转换为像素(光栅化),着色(纹理映射)并应用光照和其他效果。 它依靠透视校正和Gouraud 着色分别用来处理插值纹理和光照。 此外,该单元提供一些现代特性,例如fog,alpha混合,深度缓冲(Z缓冲,或是一种被称为W缓冲的变体),模板测试和抗锯齿。 虽然最后一项非常原始(将多边形的外部边缘设置透明而已),而且只能用于不透明像素。
渲染系统采用了新旧结合的方式:它没有直接渲染到frame buffer,而是采用行缓存渲染(line buffer rendering),在每一条扫描线上填充像素(类似于2D引擎),并将结果存储在一个较小的缓缓冲区中。 这是因为3D引擎必须与2D绘图器同步工作。
没有传统的frame buffer,光栅化器采用了扫描线渲染(scan-line rendering),遍历每个扫描线以处理其中的多边形边缘。 Arisotura(MelonDS模拟器的开发者)称,对于每个四边形,渲染器只能在每条扫描线上填充一个span。 这可能会有隐患,因为如果四边形是凹四边形或者有交叉的边,结果会变得一团糟。
关于效果,该单元还提供shadowing和一个被称为Toon Shading的独特功能(又称Cel Shading):该单元不可编程,但也可以通过改变照明参数达到卡通效果。
结果
渲染引擎将不会将结果写回frame buffer以供显示,而是写入一个名为Color Buffer的块中,该块可以存储多达48条扫描线。 2D引擎会按照先进先出(FIFO)的方式获取每条扫描线,以填充BG0图层。
3D渲染在2D渲染之前开始,使后者在必要时能够应用图形变换到新图层。 主引擎还允许捕获生成的2D帧、3D帧或组合帧,将其与VRAM中的另一个帧混合(blend),并将结果写回VRAM,之后可以用于显示。
在控制方面,由于采用了基于双缓冲的机制,渲染引擎还允许在帧中间改变参数,该机制可以保留旧状态的副本,直到当前帧绘制完成。 因此,不会出现图像撕裂的现象。
知名游戏比较
一些最初在NDS上发布的游戏试图模仿另一台游戏机(即N64)上的游戏。 玩家可能会看到两种版本之间存在重大差异,我想简要总结一下原因:
第一个例子

超级马力欧64 DS(2004)。
以256x192像素渲染。
第二个例子
所以,为了解释这里发生了什么,我将根据一些论坛用户的说法,整理出几条不同的解释:
- NDS版的纹理看起来更加方块 -> 渲染引擎没有使用任何滤波器,因此纹理使用"最近邻"方法进行插值。
- NDS版的纹理看起来更加丰富 -> 渲染引擎没有4KB TMEM的限制。相反,除了提供的压缩机制外,最多还有512KB的VRAM可用,因此可以加载更多数据。
- NDS模型的边缘出现了锯齿 -> 与N64相比,NDS模型的分辨率较低。
- NDS的纹理当从远处看起来会出现失真的情况 -> 光栅化器使用的是定点坐标。 低分辨率和mip-mapping的缺乏也加剧了走形程度。
简要的概述这就是这些。如果想要了解更专业的情况,你可能需要深入研究两个引擎,甚至反汇编这两个游戏来研究所使用的功能以及他们的用法。
交互模型
我更新了wee model查看器,添加了"最近邻插值"功能。这样你就可以使用你自己的GPU来查看NDS模型了。

"现代版本"中可用的互动模型
任天狗狗(2005)。
750个三角形。

"现代版本"中可用的互动模型
新马力欧兄弟(2006)。
636个三角形。
尽管我们讨论了图形子系统的诸多限制,但很多游戏确实充分利用了它的功能。










