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

241 lines
11 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

### **Сортировка полигонов на 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 <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-координате.
#### **Псевдокод**
```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 <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 и понимание её работы необходимо для создания эффективных графических приложений на этой платформе.