### **Сортировка полигонов на 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)** ```c #include #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-координате. #### **Псевдокод** ```c 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 полупрозрачный полигон** (вода). #### **Код рендеринга** ```c // 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](http://psx.arthus.net/sdk/PsyQ/DOCS/). # Функция `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) ## Синтаксис ```c void DrawOTag(unsigned long *ot); ``` где `ot` - указатель на Ordering Table (таблицу упорядочивания) ## Как работает Ordering Table (OT) OT представляет собой массив указателей, где: - Каждый элемент соответствует определенному уровню глубины (Z-буфер в упрощенной форме) - Элементы содержат связанные списки графических примитивов (POLY_F3, SPRT и т.д.) - Примитивы в одном слое OT рисуются в порядке добавления ## Пример использования ```c #include #include #include #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 и понимание её работы необходимо для создания эффективных графических приложений на этой платформе.