11 KiB
Сортировка полигонов на PlayStation 1 (PS1)
PS1 не имеет аппаратной поддержки Z-буфера (depth buffer), поэтому правильный порядок отрисовки полигонов (сортировка) критически важен для корректного отображения 3D-сцен. Вот как это реализовано:
1. Основные методы сортировки
1.1. Ordering Table (OT)
Ordering Table — это главный механизм сортировки на PS1.
Работает по принципу связного списка в VRAM, где полигоны группируются по расстоянию.
Как это работает?
-
Инициализация OT:
- Создаётся массив указателей (например, на 256 или 1024 элемента).
- Каждый элемент соответствует диапазону глубин (Z-значений).
-
Добавление примитивов:
- Для каждого полигона вычисляется Z-координата (например, средняя точка).
- По Z-значению выбирается ячейка OT (чем дальше объект, тем меньше индекс).
- Полигон добавляется в список для этой ячейки.
-
Рендеринг:
- 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
Для сложных объектов (например, моделей) используется сортировка по ограничивающему объёму:
- Вычисляется сфера или AABB (коробка), охватывающая объект.
- Сортируются центры сфер по 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). - Принудительный порядок для мелких объектов.
- Случайное смещение Z (
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 выполняет:
- Обработку списка команд OT (Ordering Table)
- Отправку команд рендеринга в GPU
- Управление приоритетом отрисовки объектов (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;
}
Особенности работы
- Двойная буферизация - обычно используют два набора OT и буферов примитивов
- Сортировка по глубине - OT автоматически сортирует примитивы по Z-координате
- Аппаратное ускорение - функция использует возможности GPU PS1
- Минимизация перерисовки - только измененные примитивы нужно добавлять каждый кадр
Оптимизация производительности
- Размер OT - должен соответствовать количеству уровней глубины в сцене
- Распределение примитивов - равномерное по слоям OT улучшает сортировку
- Локальность данных - последовательное расположение пакетов в памяти
- Очистка OT - обязательна перед началом нового кадра (
ClearOTagилиClearOTagR)
Варианты функции
В PsyQ существуют несколько вариантов:
DrawOTag- базовый вариантDrawOTagEnv- с применением DRAWENVDrawOTagDMA- использует DMA для передачи данных
Функция DrawOTag является фундаментальной для графического рендеринга на PS1 и понимание её работы необходимо для создания эффективных графических приложений на этой платформе.