Как стать автором
Обновить

Правильная передача цветов ретроконсолей в эмуляторах

Время на прочтение4 мин
Количество просмотров8.1K
Автор оригинала: byuu.net


Почти все игровые ретроконсоли генерируют цвета в каком-нибудь из вариантов RGB-кодирования.

Но эти цвета пикселей часто предназначены для экранов, совершенно отличающихся от тех, на которых обычно работают эмуляторы. В этой статье я расскажу о важности эмуляции цветов, приведу несколько примеров кода и скриншоты.

Самым распространённым сегодня видом дисплеев являются ЖК-панели (LCD). Они известны тем, что имеют очень плохие уровни чёрного. Различия между TN, PVA и IPS не слишком на это влияют.

Отдельные фанаты играют на ЭЛТ-мониторах, и всё большую популярность набирают OLED-экраны, особенно на телефонах и планшетах. Но в этой статье мы в основном будем рассматривать ЖК-экраны, хотя данная методика важна для дисплеев любого типа.

Точность цвета


Первая важная деталь: большинство компьютеров работает в режиме 24-битного цвета, обеспечивающем 8-битную детализацию цветов для каналов красного, зелёного и синего. Но в большинстве старых игровых систем цвета не задаются с такой точностью.

Например, Sega Genesis кодирует 9-битные цвета, что даёт по 3 бита на канал.

Самым наивным решением было бы поместить 3 бита в самые старшие 3 бита вывода, а младшие 5 бит оставить пустыми, но при этом белый цвет становится немного серым.

Пример:

000 000 000 -> 000'00000 000'00000 000'00000
111 111 111 -> 111'00000 111'00000 111'00000



Если же заполнить их единицами, то слишком светлым становится чёрный.

Пример:

000 000 000 -> 000'11111 000'11111 000'11111
111 111 111 -> 111'11111 111'11111 111'11111



Решение заключается в том, чтобы повторять исходные биты, чтобы они заполнили все выходные биты.

Пример:

000 -> 000 000 00...
010 -> 010 010 01...
011 -> 011 011 01...
111 -> 111 111 11...

В виде кода:

uint8 red = r << 5 | r << 2 | r >> 1
//rrr00000 | 000rrr00 | 000000rr -> rrrrrrrr

Эмуляция экрана


Игровые ретросистемы не были предназначены для работы на современных ЖК-мониторах компьютеров. Обычно домашние консоли были рассчитаны на ЭЛТ-экраны, а в портативных консолях применялись гораздо более старые и менее точные ЖК-панели.

В этой статье мы не будем рассматривать артефакты экранов, такие как кривизна экрана, строки развёртки, хроматическая аберрация, межкадровое смешение, апертурные решётки и т.д.: мы сосредоточимся пока только на цветах отдельных пикселей.

Мониторы PC


В мониторах существует довольно широкий диапазон цветов, потому что только некоторые из них профессионально калибруются по стандартам наподобие SRGB, но в общем случае, лучшее, чего мы можем добиться — попытаться эмулировать цвета так, как будто мы используем правильно калиброванный SRGB-монитор.

Эмуляция ЭЛТ: Super Nintendo


Основное различие между ЭЛТ-экранами и ЖК-мониторами компьютеров заключается значительно сниженных уровнях чёрного, что можно только немного компенсировать при помощи кривой гамма-коррекции:

//SNES colors are in RGB555 format, so there are 32 levels for each channel
static const uint8 gammaRamp[32] = {
  0x00, 0x01, 0x03, 0x06, 0x0a, 0x0f, 0x15, 0x1c,
  0x24, 0x2d, 0x37, 0x42, 0x4e, 0x5b, 0x69, 0x78,
  0x88, 0x90, 0x98, 0xa0, 0xa8, 0xb0, 0xb8, 0xc0,
  0xc8, 0xd0, 0xd8, 0xe0, 0xe8, 0xf0, 0xf8, 0xff,
};

Эта таблица позаимствована у Overload of Super Sleuth / Kindred. Она затеняет нижнюю половину цветовой палитры, оставляя верхнюю часть неизменной.

Это оказывает потрясающее воздействие на изображение при эмуляции: слева показан оригинал, справа — изображение с применённой гамма-коррекцией:





Эмуляция ЖК: Game Boy Advance


Game Boy Advance имел один из худших ЖК-экранов с совершенно блеклыми цветами. Хитрые разработчики поняли, что значительно преувеличив цвета, можно получить на реальном оборудовании более приятные результаты.

Разумеется, если использовать эти цвета на стандартном ЖК-мониторе, то результат окажется пёстрым кошмаром. К счастью, мы можем компенсировать и это, создав достаточно естественные цвета:

double lcdGamma = 4.0, outGamma = 2.2;
double lb = pow(B / 31.0, lcdGamma);
double lg = pow(G / 31.0, lcdGamma);
double lr = pow(R / 31.0, lcdGamma);
r = pow((  0 * lb +  50 * lg + 255 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
g = pow(( 30 * lb + 230 * lg +  10 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);
b = pow((220 * lb +  10 * lg +  50 * lr) / 255, 1 / outGamma) * (0xffff * 255 / 280);

Этот фрагмент кода написан Talarubi.

Намного более разительный контраст по сравнению с ЭЛТ — сверху оригинал, снизу версия с цветокоррекцией:





Эмуляция ЖК: Game Boy Color


Экран Game Boy Color был на удивление лучше в воспроизведении цвета и конечной картинке может присутствовать только незначительное размытие цветов.

В эмуляторах Game Boy Color достаточно популярен такой алгоритм:

R = (r * 26 + g *  4 + b *  2);
G = (         g * 24 + b *  8);
B = (r *  6 + g *  4 + b * 22);
R = min(960, R) >> 2;
G = min(960, G) >> 2;
B = min(960, B) >> 2;

К сожалению, я не знаю, кто его написал алгоритм. Если вы знаете, то сообщите мне, чтобы я мог указать авторство!

Как и раньше, оригинал слева, версия с цветокоррекцией — справа:



Этот пример был выбран специально: хотя оригинал выглядит живее и предпочительнее, если приглядеться, то заметен шахматный паттерн вокруг персонажа, который светлее фона.

Скорее всего, это был недосмотр со стороны разработчиков, потому что на реальном Game
Boy Color оттенки белого размыты и два отличающихся оттенка цвета сливаются друг с другом почти безупречно.

В заключение


Существует ещё много систем, которым пока не хватает хороших фильтров эмуляции цветов.
Их очень трудно настраивать. Из самых важных примеров можно указать WonderSwan и Neo Geo Pocket, у которых на момент написания статьи не было хороших фильтров аппроксимации цветов.

С портативными консолями всё ещё сложнее, потому что в них часто отсутствует задняя подсветка (а иногда и передняя подсветка!) и есть способы изменения контраста, благодаря чему нет какого-то истинного значения «цвета» для конкретного значения RGB.

Особо интересным пограничным случаем является WonderSwan Color, в котором есть программно устанавливаемый флаг для повышения контраста выводимого изображения.
Пока мы не знаем, как достоверно эмулировать такое поведение, и непонятно, сможем ли вообще.

Эмуляция цветов — это область, требующая больше внимания, поэтому если вы специалист в математике и анализе цветов, то ваша помощь очень бы пригодилась сцене эмуляции!
Теги:
Хабы:
Если эта публикация вас вдохновила и вы хотите поддержать автора — не стесняйтесь нажать на кнопку
Всего голосов 36: ↑33 и ↓3+30
Комментарии17

Публикации

Истории

Работа

Unity разработчик
10 вакансий

Ближайшие события

15 – 16 ноября
IT-конференция Merge Skolkovo
Москва
22 – 24 ноября
Хакатон «AgroCode Hack Genetics'24»
Онлайн
28 ноября
Конференция «TechRec: ITHR CAMPUS»
МоскваОнлайн
25 – 26 апреля
IT-конференция Merge Tatarstan 2025
Казань