Mode 7 в SNES

По сравнению с 8–битной Famicom/NES/Денди в 16–битной Super Famicom/SNES графические возможности сильно расширились. Так, например, появились разные видеорежимы с возможностью отображения до четырёх независимо скроллящихся задних слоёв, просвечивающих друг сквозь друга. Однако единственный режим с четырьмя слоями обладал такой же как в Денди цветностью (4 цвета на тайл один из который — прозрачный), что было маловато для 16–битной консоли, так что чаще всего там использовались трёхслойные и двухслойные задние фоны. Однако подлинной вершиной двумерной графики в этой консоли стал однослойный видеорежим номер 7, так называемый Mode 7 (при этом он восьмой и последний по счёту — нумерация идёт с нуля) в котором единственный фон мог быть произвольно масштабирован и повёрнут.


(Второй уровень Contra III: Alien Wars на SNES)

Что вообще тут происходило с точки зрения вычислительных процессов — когда видеочип в очередном сканлайне рисовал задний фон, он брал координаты текущего пикселя на экране (x,y) (как обычно в 2D–графике верхний–левый угол экрана это начало координат) и прогонял их через через несложную формулу:
px = A * x + B * y + U,
py = C * x + D * y + V,

где A,B,C,D,U,V — настраиваемые на кадр коэффициенты. Получающиеся координаты (px,py) использовались как координаты для выборки результирующего пикселя из заднего фона. Так, если U=V=B=C=0, а A=D=1, то задний фон будет отображен без искажений с началом в левом–верхнем углу экрана, потому что формула выродится в px=x, py=y. Люди знакомые с 3D–графикой могут не без веских на то оснований усмотреть здесь аффинное преобразование матрицей 3x3 с последней строкой равной (0,0,1). Действительно в этих шести настраиваемых коэффициентах можно «зашить» перемножением матриц любые комбинации из сдвигов, масштабов и поворотов. Кроме масштабирования и поворота в аффинное преобразование можно так же заложить еще «косые сдвиги». И вообще математическое определение аффинных преобразований — это такие геометрические искажения пространства в которых, однако, параллельные до преобразования прямые остаются параллельными и после преобразования.
Некоторые могли бы заметить, что для 16–битной консоли было бы жирновато выполнять по 4 умножения и 4 сложения вещественных чисел на каждый пиксель экрана. Но на деле всё было довольно таки просто и разумно.
Во первых — использовались вещественные числа с фиксированной запятой, скорее всего как и в вышедшем позднее Game Boy Advance — 8 бит после запятой, так что и сложения и умножения были целочисленными.
Во вторых, нетрудно заметить, что «сканирующий» по строкам и рядам пикселей экрана алгоритм отрисовки тайлового видеочипа не нуждается в умножениях вообще — если удерживать в памяти текущие значения px и py. Так px начинает счёт с начального значения U, увеличиваясь на B с каждой строкой и на A с каждым столбцом пикселей экрана. Так что теоретически на всю математику Mode 7 хватает в среднем всего два сложения на пиксель, а это для 16–битной консоли было вполне по силам. Сам я к демосцене прикасался только в качестве зрителя, но по моему очевидно, что этот нехитрый и эффективный вычислительный трюк используется в ней часто и плодотворно.
Как я понял, начальные значения по каждой строке всё–таки вычислялись одним честным умножением, чтобы стала возможной техника, которую я опишу чуть ниже. Проход же по сканлайну действительно обходился только сложениями.

Не чужда для Mode 7 была так же техника HBlank–отсечения так что в той же Contra III был сплит–скрин на двух игроков с двумя независимо вращающимися фонами:

(Сплит–скрин в Contra III)

Однако поистине Mode 7 раскрылся при имитации вращающейся в 3D плоскости. Причём одними только аффинными коэффициентами этого добиться было нельзя — как было сказано выше от них параллельные прямые остаются параллельными. Но если при перехвате HBlank подменять эти коэффициенты для каждой строки, то можно исказить и параллельность прямых создав эффект перспективы, что использовалось, например, в игре Mario Cart:


Имитация перспективы и неких зачатков трёхмерности таким образом использовалась в SNES не в одной игре:


Но игравшие в вышеупомянутую Contra III: Alien Wars могут заметить одну странность на втором боссе этой игры:


Здесь очевидно, что босс реализован через задний фон Mode 7 — ибо он огромен, изменяет размер на экране и вращается. Но помимо босса на экране явственно видно асфальт как второй независимо вращающийся от него слой, но ведь в Mode 7 слой только один, если не считать отсечение по HBlank!
Разгадка забавна, а трюк лежащий в её основании изобретателен — на самом деле асфальт реализован как серый цвет «бэкграунда» — цвет пикселей которые не попали ни в один слой (ни фонов ни спрайтов). Подозрительно же одинаковые линии разметки на видимости асфальта на самом деле являются спрайтами — игра перерисовывает их небольшой набор тайлов на каждом кадре, и подставляет подвижные спрайты с ними в нужные места, имитируя текстуру асфальта спрайтами за неимением второго фона.
Таким образом эта игра не используя чипов расширения, честно «выходила за рамки» запланированных возможностей консоли, что было в те времена не редкостью.

8 комментариев

avatar
Очень круто Mode 7 был использован в первом уровне R-Type 3.
youtu.be/djW3WTEU-LE?t=3m — часть уровня вращается, верхняя часть получается отсечена по HBlank? Как тогда реализованы звезды? Спрайтами?
Еще любопытно, как сделан collision detection для вращающихся стен.

youtu.be/djW3WTEU-LE?t=4m48s — чуть позднее там же псевдо-3d на заднем фоне, с какой-то еще подкруткой палитры? Звезды все же сделаны спрайтами, походу — когда из-за трубы вылетает 4 робота, звезды гасятся.
avatar
Звёзды — да, спрайтами. Для коллизий можно считать, что поле неподвижно и ортогонально осям, а меняется лишь ориентация и направление движение в нём корабля. Физическая модель гораздо проще получается, а то как она связана с визуальным рядом дело тех же формул.
Эффект трубы проще и не требует Mode 7 — это просто HBlank-отсечение на каждой строке по формуле так что расстояние между «выборками текстуры» сохраняется. Эффекта перспективы же нет — вертикальные линии остаются вертикальными по всей длине трубы.
avatar
Надо отметить, что на SNES не практикуется перехват HBlank в том виде, как это делается на всех остальных консолях (по прерыванию или задержкой в коде), хотя он и возможен. Вместо этого там сделана специальная система HDMA, которая, я бы сказал, является самой главной и мощной железной фишкой платформы, и которой очень активно пользуется каждая игра — она позволяет менять значения регистров, заданных посредством восьми списков, строго в начале нужных строк. И это даже не 8 регистров за строку, в одном списке может быть до 4 записей. Поменять таким образом можно практически что угодно, хоть ABCD Mode7, хоть параметры смещения, хоть видеорежим. Списки заполняются в любой удобный момент и назначаются в начале кадра, дальше железо работает само, а процессор может спокойно заниматься другими вещами.
  • Shiru
  • +2
avatar
Ох ты, не знал про эту фичу, хотя делал обзор на видеосистему SNES и его собирался сюда тоже запостить. Хотя понятно — это видимо надо читать раздел про DMA. Хм, надо тогда почитать.
avatar
P.S.
Прочитал. Да, прикольно. В целом понятно — любой из 8-ми DMA-каналов можно перевести в режим автоматической записи в порты видеочипа по HBlank. Надо будет дополнить статью про видеочип SNES этим прежде чем сюда постить.
avatar
Собственно 8 каналов DMA там именно для этого, для обычного DMA в видеопамять в начале кадра достаточно одного канала. И надо уточнить, что там именно списки, а не просто однократное DMA. У списка есть адрес, куда ему надо будет писать значения, формат записи (1-4 байт в разных схемах, типа два соседних адреса или два раза один и тот же), и далее в самом списке пары — сколько строк ждать и какое значение записать, следующая пара, и так до конца кадра. Таким образом всего одним списком можно сделать, например, цветовой градиент на экране, или, скажем, эффект copper bars, или искажение слоя фона по синусоиде (типа горячий воздух).
avatar
Собственно 8 каналов DMA там именно для этого, для обычного DMA в видеопамять в начале кадра достаточно одного канала.
Согласен, если взять другую их консоль, которую я лучше всего изучил — GBA, то там всего 4 DMA-канала, при том что два предназначены для запитки каналов PCM-звука, а еще один полезен, например, для автоматического копирования данных с опциональной камеры на экран, то под хоз-нужды оставался по большому счёту только один канал. Хотя там даже его польза наверное сомнительна — или он создавал бы щелчки в звуке или DMA-каналы звука тормозили бы процессор, поэтому там только с хитрым расчётом можно было им пользоваться.
avatar
На SNES и Genesis обычное DMA очень полезно. Там отдельное видео-ОЗУ ограниченного объёма, с последовательным доступом к видеопамяти через порт обмена, и доступ возможен только во время VBlank. Большая часть игр подкачивает часть графики в видео-ОЗУ 'на лету', в частности, анимацию героя (некоторые и врагов тоже), чтобы оставить побольше места для графики фона. Процессор через последовательный порт пишет в видеопамять очень медленно, DMA в разы быстрее, значит можно передать больше за кадр — около 5 КБ на SNES и 6 КБ на Genesis. И всё равно приходится изворачиваться, передавать половинками через кадр и.т.д.

На SNES побочных эффектов от DMA нет, просто замедляется выполнение кода, но это не проблема, т.к. наличие HDMA позволяет избежать игр с точными таймингами выполнения. На Genesis на время передачи тормозится Z80, а на нём любят играть сэмплы, поэтому звук очень сильно портится (выпадения с частотой 50/60 гц), характерные искажения голосов возникают во многих играх по этой причине. Не так давно придумали обход этой проблемы: буферизировать звук из ПЗУ в ОЗУ Z80 во время прохода луча по видимой части кадра, а во время VBlank играть буфер из ОЗУ. При отсутствии обращений к ПЗУ во время VBlank Z80 продолжает работать (остановка активируется только в момент обращения к ПЗУ). К сожалению, во времена расцвета платформы до этого не додумались.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.