Skip to content

渲染管线 — 概念

渲染管线总览

GBA 的图形输出通过一条固定的像素处理管线生成。在 pokeemerald-wasm 中,这条管线由 WASM 模块内的 WasmRenderFrame()src/wasm_display.c)驱动,逐像素地从内存数据合成最终的 RGBA 帧。宿主端随后把这个帧缓冲区搬到屏幕(浏览器用 putImageData,原生前端用纹理或帧缓冲)。

渲染器维护两块静态缓冲区:

  • sWasmDisplayRgba[240×160×4]:最终输出的 RGBA 像素,宿主通过 WasmDisplayBuffer() 直接读取。
  • sLayerData[240×160]:每个像素当前所属的层号(LAYER_BG0..LAYER_BG3LAYER_OBJLAYER_BACKDROP),供 Alpha 混合判断「目标层」使用。

BG 显示模式

GBA 的 DISPCNT 寄存器(0x04000000)的低 3 位决定了当前的显示模式,不同模式决定了背景层的类型和数量。WasmRenderFrame() 据此把 Mode 3/4 分发到位图路径,Mode 0/1/2 分发到 Tile 路径:

模式背景层类型用途
Mode 0BG0-BG3文字菜单、对话框等静态界面
Mode 1BG0-BG1 文字, BG2 仿射混合地图场景(最常用)
Mode 2BG2-BG3 仿射仿射旋转/缩放场景
Mode 3位图 16bit全帧直接像素写入
Mode 4位图 8bit索引色全帧(支持双缓冲)

BgLayersForMode() 根据 DISPCNT 的「层启用位」(0x100 << bg)和模式,返回当前激活的 BG 层列表,每层标记 type(0 = 文字,1 = 仿射)。

Tile 渲染详解

文字背景(Text BG)

文字背景是最常用的渲染模式。每个背景层由一个 Tile Map(屏幕块矩阵)和一组 Tile(8×8 像素图块)组成:

Tile Map 中每个 Entry(2 字节)的结构:

含义
[9:0]Tile 编号(0-1023)
[11:10]水平/垂直翻转
[15:12]调色板编号(16 色模式下)

TextBgPixel(bg, x, y, &color) 关键步骤:

  1. BGxCNT 读取 charBase(Tile 数据基址)、screenBase(Tile Map 基址)、color256 标志、尺寸
  2. 用扫描线采样到的 hofs/vofs(见下文 HBlank DMA)计算屏幕坐标对应的地图坐标,并按尺寸环绕
  3. 从 Tile Map 读取 Entry,获取 Tile 编号、翻转标志、调色板编号
  4. 计算 Tile 内的像素坐标(考虑翻转)
  5. VRAM 读取像素的颜色索引(4bpp 打包或 8bpp)
  6. 通过 GbaColor() 将 BGR555 颜色值转换为 RGB;索引 0 视为透明,返回 FALSE

仿射背景(Affine BG)

仿射背景支持旋转和缩放变换,通过一个 2×2 仿射矩阵(PA/PB/PC/PD)实现。AffineBgPixel() 对每个屏幕像素反向映射回纹理坐标:

仿射参考点 refX/refY 是 28 位有符号定点数(Signed28),矩阵元素是 16 位有符号(Signed16),右移 8 位完成定点运算。

精灵渲染

Sprite 渲染从 OAM 读取精灵属性,将其像素叠加到画面上。RenderSprites(dispcnt, priority) 只渲染指定优先级的精灵(位图模式下 priority = -1 表示全部):

OAM 属性详解

每个精灵在 OAM 中占用 4 个半字(8 字节,最后 2 字节对普通精灵未用),渲染读前 3 个:

Attribute 0a0):

含义
[7:0]Y 坐标
[9:8]仿射/普通模式(affineMode
[10:11]对象模式(objMode,0=正常,1=半透明,2=窗口)
[13]256 色 / 16 色模式
[15:14]形状(正方形/横向/纵向)

Attribute 1a1):

含义
[8:0]X 坐标(9 位,超出 240 取负)
[12]水平翻转(普通精灵)
[13]垂直翻转(普通精灵)
[8:4]仿射矩阵索引(仿射精灵,(a1>>9)&31
[15:14]尺寸

Attribute 2a2):

含义
[9:0]Tile 基地址
[11:10]优先级(0-3)
[15:12]调色板编号(16 色模式)

仿射精灵

仿射精灵从 OAM 的矩阵区(OAM + matrix*32)读取 4 个矩阵参数(PA/PB/PC/PD),对每个输出像素反向映射回纹理坐标:

dx = x - drawCx,  dy = y - drawCy        // 相对绘制中心的偏移
px = (PA * dx + PB * dy) >> 8 + texCx
py = (PC * dx + PD * dy) >> 8 + texCy

其中 drawCx/drawCy 是绘制中心,texCx/texCy 是纹理中心。affineMode == 3(双倍尺寸)时绘制区域放大一倍。

优先级系统

Tile 模式下的渲染采用 优先级交替合成 策略,在 RenderTiled() 中实现,确保 BG 层和精灵按正确顺序叠加:

外层循环从 Priority 3 到 Priority 0,每个级别先画该优先级的 BG 层、再画该优先级的精灵;后写入的像素覆盖先写入的(PutPixel 直接写 RGBA 并更新 sLayerData)。这意味着一个 Priority 0 的精灵会显示在所有 Priority 1-3 的 BG 层之上,但可能与 Priority 0 的 BG 层交错。

调色板转换

GBA 使用 BGR555 格式存储颜色(15 位,1 位未用),需要转换为 RGBA8888。GbaColor() 完成此转换,将每个 5 位色阶(0-31)线性映射到 8 位色阶(0-255):

c
// src/wasm_display.c — GbaColor()
static inline struct Rgb GbaColor(u16 value)
{
    struct Rgb color;
    color.r = (u8)((value & 31) * 255 / 31);        // bit[4:0]  → R
    color.g = (u8)(((value >> 5) & 31) * 255 / 31); // bit[9:5]  → G
    color.b = (u8)(((value >> 10) & 31) * 255 / 31);// bit[14:10]→ B
    return color;
}

Alpha 通道固定为 255(不透明),在 PutPixel() 写入时填充。

特效系统

Window 遮罩

GBA 支持 Window 0、Window 1 和 Object Window 三种窗口,可以对不同区域启用/禁用 BG 层和精灵的渲染。WindowMask(x, y) 根据当前坐标判断所在窗口并返回一个 6 位的层遮罩(哪些层允许绘制)。PutPixel() 在写像素前先检查 mask & layer,被屏蔽的层直接跳过。

Alpha 混合与亮度

ActiveBlendColor() 实现了三种半透明特效,由 BLDCNT 的高 2 位(effect)选择:

效果effect计算方式
Alpha 混合1color = src * EVA + dst(已绘) * EVB(>> 4)
亮度增加2color = color + (255 - color) * EVY
亮度减少3color = color - color * EVY

Alpha 混合时,源层必须是 BLDCNT 的目标层之一,且当前像素的「已绘制层」(来自 sLayerData)必须是混合目标层,才会把新颜色与缓冲区里的旧颜色按 EVA/EVB 加权混合。半透明精灵(objMode == 1)会强制触发 Alpha 混合。

HBlank DMA 扫描线寄存器

上游新增能力

真实 GBA 上,游戏常用 HBlank DMA 在每条扫描线开始时改写 GPU 寄存器(如逐行滚屏 BGxHOFS、逐行变色 BLDY),实现水面波动、扭曲、分屏等扫描线特效。wasm_display.c 还原了这一机制。

RefreshHblankDmaGpuRegs() 在每帧渲染前扫描 4 个 DMA 通道,挑出「HBlank 触发 + 重复 + 16 位 + 计数=1 + 目标为 GPU 寄存器」的通道,记录其源地址和步进到 sHblankDmaGpuRegs[]。随后所有读取 GPU 寄存器的地方都改用 ScanlineGpuReg(offset, y)

  • 若该寄存器有活跃的 HBlank DMA,返回 src + stride * (y - 1)——即第 y 条扫描线应该看到的寄存器值;
  • 否则返回寄存器当前值。

因此 TextBgPixelhofs/vofsWindowMaskWIN0H/WIN1HActiveBlendColorBLDY 时,拿到的都是逐扫描线变化的值,从而正确重现扫描线特效。宿主端(app.js)在自动化回放时还会调用导出的 WasmRefreshHblankDmaGpuRegs() / WasmWindowMask() 来探测 DMA 状态。

相关概念

  • VRAM — 视频内存,存储 Tile 数据和位图数据
  • OAM — 精灵属性内存,存储 128 个精灵的位置/属性
  • Tile — 8×8 像素的图块,BG 和精灵的基本渲染单元
  • Sprite — 可移动的图形对象,用于角色、NPC、特效等
  • Palette — 调色板,将颜色索引映射为 RGB 颜色值
  • WASM 内存模型 — 内存地址布局
  • GBA 硬件模拟 — I/O 寄存器功能
  • 源码兼容与适配#if WASM 守卫如何隔离 wasm_display.c