Files
SergObsidian/PERSONAL PROJECTS/PS1 DOCS/PS1 Ordering table.md
2025-04-19 01:11:07 +05:00

11 KiB
Raw Blame History

Сортировка полигонов на PlayStation 1 (PS1)

PS1 не имеет аппаратной поддержки Z-буфера (depth buffer), поэтому правильный порядок отрисовки полигонов (сортировка) критически важен для корректного отображения 3D-сцен. Вот как это реализовано:


1. Основные методы сортировки

1.1. Ordering Table (OT)

Ordering Table — это главный механизм сортировки на PS1.
Работает по принципу связного списка в VRAM, где полигоны группируются по расстоянию.

Как это работает?

  1. Инициализация OT:

    • Создаётся массив указателей (например, на 256 или 1024 элемента).
    • Каждый элемент соответствует диапазону глубин (Z-значений).
  2. Добавление примитивов:

    • Для каждого полигона вычисляется Z-координата (например, средняя точка).
    • По Z-значению выбирается ячейка OT (чем дальше объект, тем меньше индекс).
    • Полигон добавляется в список для этой ячейки.
  3. Рендеринг:

    • OT обрабатывается от дальних к ближним ячейкам (от 0 до N).
    • Все примитивы в одной ячейке рисуются в порядке добавления.

Пример кода (PsyQ SDK)

#include <libgpu.h>

#define OT_LEN 256  // Размер Ordering Table
uint32_t ot[OT_LEN]; // Сама таблица

void init_ot() {
    ClearOTagR(ot, OT_LEN);  // Очистка OT
}

void add_polygon(POLY_F4 *poly, int z) {
    // Вычисляем индекс в OT (0 = дальний, OT_LEN-1 = ближний)
    int ot_entry = (z >> 8) & (OT_LEN - 1);  // Простейшее масштабирование Z
    
    // Добавляем полигон в OT
    addPrim(ot + ot_entry, poly);
}

void render_frame() {
    DrawOTag(ot + (OT_LEN - 1));  // Рендеринг OT (от ближних к дальним)
}

1.2. Сортировка по Bounding Box

Для сложных объектов (например, моделей) используется сортировка по ограничивающему объёму:

  1. Вычисляется сфера или AABB (коробка), охватывающая объект.
  2. Сортируются центры сфер по Z-координате.

Псевдокод

struct Object {
    int center_z;
    POLY_F4 *polys;
};

void sort_objects(Object *objs, int count) {
    qsort(objs, count, sizeof(Object), compare_by_z);
}

int compare_by_z(const void *a, const void *b) {
    return ((Object*)a)->center_z - ((Object*)b)->center_z;
}

1.3. Priority Groups

Некоторые игры (например, Crash Bandicoot) используют приоритетные группы:

  • Полигоны делятся на статические (уровень) и динамические (персонажи).
  • Статические рисуются через OT, динамические — поверх них.

2. Оптимизации

2.1. Z-Сортировка только для прозрачных объектов

  • Непрозрачные полигоны рисуются без сортировки (через алгоритм художника).
  • Прозрачные (с полупрозрачностью) сортируются строго от дальних к ближним.

2.2. Двойная OT

  • Одна OT для непрозрачных, вторая — для прозрачных полигонов.
  • Позволяет уменьшить накладные расходы.

2.3. Субпиксельная корректировка

  • Для борьбы с Z-файтингом (мерцание полигонов) используется:
    • Случайное смещение Z (z += rand() % 4).
    • Принудительный порядок для мелких объектов.

3. Пример для игры

Допустим, у нас есть:

  • 10 коробок на разных расстояниях.
  • 1 полупрозрачный полигон (вода).

Код рендеринга

// 1. Инициализация
init_ot();

// 2. Добавление непрозрачных полигонов (коробки)
for (int i = 0; i < 10; i++) {
    add_polygon(&boxes[i], boxes[i].z);
}

// 3. Добавление прозрачного полигона (вода)
add_polygon(&water_poly, water_z);

// 4. Рендеринг
render_frame();

4. Проблемы и решения

Проблема Решение
Мерцание полигонов Субпиксельная корректировка Z.
Низкая производительность Уменьшение размера OT.
Неправильный порядок Ручная настройка приоритетов.

5. Итог

  • Ordering Table — основа сортировки на PS1.
  • Для сложных сцен комбинируют OT, Bounding Box и приоритетные группы.
  • Оптимизации нужны для борьбы с артефактами.

Этот подход использовался в играх типа Tekken 3 и Resident Evil. Для глубокого понимания изучите документацию PsyQ.

Функция DrawOTag в библиотеке PsyQ для PlayStation 1

Функция DrawOTag (Draw Ordering Table Tag) является ключевой функцией рендеринга в графической подсистеме PlayStation 1. Она отвечает за отправку списка графических команд (Ordering Table) в графический процессор (GPU) для отрисовки.

Основное назначение

DrawOTag выполняет:

  1. Обработку списка команд OT (Ordering Table)
  2. Отправку команд рендеринга в GPU
  3. Управление приоритетом отрисовки объектов (Z-sorting через OT)

Синтаксис

void DrawOTag(unsigned long *ot);

где ot - указатель на Ordering Table (таблицу упорядочивания)

Как работает Ordering Table (OT)

OT представляет собой массив указателей, где:

  • Каждый элемент соответствует определенному уровню глубины (Z-буфер в упрощенной форме)
  • Элементы содержат связанные списки графических примитивов (POLY_F3, SPRT и т.д.)
  • Примитивы в одном слое OT рисуются в порядке добавления

Пример использования

#include <libgte.h>
#include <libgpu.h>
#include <libetc.h>

#define OT_LENGTH 10    // Количество Z-уровней
#define PACKETMAX 1000  // Максимальное количество примитивов

typedef struct {
    DISPENV disp;      // Display environment
    DRAWENV draw;       // Drawing environment
    unsigned long ot[OT_LENGTH];  // Ordering Table
    char p[PACKETMAX];  // Буфер для пакетов примитивов
} DB;

DB db[2];               // Double buffer
int active_buffer = 0;  // Текущий буфер

void init() {
    // Инициализация графики
    ResetGraph(0);
    SetDefDispEnv(&db[0].disp, 0, 0, 320, 240);
    SetDefDrawEnv(&db[0].draw, 0, 0, 320, 240);
    SetDefDispEnv(&db[1].disp, 0, 0, 320, 240);
    SetDefDrawEnv(&db[1].draw, 0, 0, 320, 240);
    
    // Включение отображения
    SetDispMask(1);
}

void render() {
    DB *cdb = &db[active_buffer];
    
    // Очистка OT
    ClearOTagR(cdb->ot, OT_LENGTH);
    
    // Добавление примитивов в OT (пример)
    add_primitive_to_ot(cdb);
    
    // Рендеринг OT
    DrawOTag(cdb->ot);
    
    // Смена буферов
    FntFlush(-1);
    VSync(0);
    PutDispEnv(&cdb->disp);
    PutDrawEnv(&cdb->draw);
    active_buffer ^= 1;
}

Особенности работы

  1. Двойная буферизация - обычно используют два набора OT и буферов примитивов
  2. Сортировка по глубине - OT автоматически сортирует примитивы по Z-координате
  3. Аппаратное ускорение - функция использует возможности GPU PS1
  4. Минимизация перерисовки - только измененные примитивы нужно добавлять каждый кадр

Оптимизация производительности

  1. Размер OT - должен соответствовать количеству уровней глубины в сцене
  2. Распределение примитивов - равномерное по слоям OT улучшает сортировку
  3. Локальность данных - последовательное расположение пакетов в памяти
  4. Очистка OT - обязательна перед началом нового кадра (ClearOTag или ClearOTagR)

Варианты функции

В PsyQ существуют несколько вариантов:

  • DrawOTag - базовый вариант
  • DrawOTagEnv - с применением DRAWENV
  • DrawOTagDMA - использует DMA для передачи данных

Функция DrawOTag является фундаментальной для графического рендеринга на PS1 и понимание её работы необходимо для создания эффективных графических приложений на этой платформе.