313. 由 PPU 背景生成不透明掩码

根据当前 PPU 背景内存构建背景不透明度掩码。

313 / 356 · tests/chapter_09_sprite_rendering/test_313_ppu_background_to_opaque_mask.py

需要更新的文件

emulator/rendering/ppu_background_renderer.py

为什么需要这一步

步骤 310 添加了纯函数辅助工具

build_background_opaque_mask(pattern_table, nametable)

该辅助函数基于原始字节工作。这一步添加与之对应的 PPU 层级版本:

ppu_background_to_framebuffer(ppu)

新的辅助函数应当

ppu_background_to_opaque_mask(ppu)

它与 ppu_background_to_framebuffer() 非常相似,但所需的信息更少。

ppu_background_to_framebuffer() 需要:

  • 可见的名称表字节
  • 属性表字节
  • 选定的背景图案表字节
  • 调色板 RAM 字节

因为它要生成 RGB 像素。

ppu_background_to_opaque_mask() 只需要:

  • 可见的名称表字节
  • 选定的背景图案表字节

因为不透明度只取决于:

CHR color index != 0

属性表和调色板 RAM 不影响不透明度。

示例实现

def ppu_background_to_opaque_mask(ppu: PPU) -> BackgroundOpaqueMask:
    nametable_bytes = bytes(
        ppu.ppu_bus.read(BASE_NAMETABLE_ADDR + offset)
        for offset in range(NAMETABLE_SIZE)
    )

    pattern_table_base = (
        PATTERN_TABLE_1_ADDR
        if ppu.ctrl & CTRL_BACKGROUND_PATTERN_TABLE
        else PATTERN_TABLE_0_ADDR
    )

    pattern_table_bytes = bytes(
        ppu.ppu_bus.read(pattern_table_base + offset)
        for offset in range(PATTERN_TABLE_SIZE)
    )

    return build_background_opaque_mask(
        pattern_table=pattern_table_bytes,
        nametable=nametable_bytes,
    )

重要提示

这一步还不会修改 Console.render_framebuffer()。下一步会让 Console 调用这个辅助函数,并将掩码传递给合成器。

本步骤范围之外

  • 更改 emulator/console.py
  • 将掩码传递给 composite_background_and_sprites()
  • 精灵 0 命中
  • 精灵溢出
  • pygame

运行本课

uv run pytest tests/chapter_09_sprite_rendering/test_313_ppu_background_to_opaque_mask.py -v