Mode 7 в SNES
По сравнению с 8–битной Famicom/NES/Денди в 16–битной Super Famicom/SNES графические возможности сильно расширились. Так, например, появились разные видеорежимы с возможностью отображения до четырёх независимо скроллящихся задних слоёв, просвечивающих друг сквозь друга. Однако единственный режим с четырьмя слоями обладал такой же как в Денди цветностью (4 цвета на тайл один из который — прозрачный), что было маловато для 16–битной консоли, так что чаще всего там использовались трёхслойные и двухслойные задние фоны. Однако подлинной вершиной двумерной графики в этой консоли стал однослойный видеорежим номер 7, так называемый Mode 7 (при этом он восьмой и последний по счёту — нумерация идёт с нуля) в котором единственный фон мог быть произвольно масштабирован и повёрнут.
(Второй уровень Contra III: Alien Wars на SNES)
Что вообще тут происходило с точки зрения вычислительных процессов — когда видеочип в очередном сканлайне рисовал задний фон, он брал координаты текущего пикселя на экране (x,y) (как обычно в 2D–графике верхний–левый угол экрана это начало координат) и прогонял их через через несложную формулу:
где 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!
Разгадка забавна, а трюк лежащий в её основании изобретателен — на самом деле асфальт реализован как серый цвет «бэкграунда» — цвет пикселей которые не попали ни в один слой (ни фонов ни спрайтов). Подозрительно же одинаковые линии разметки на видимости асфальта на самом деле являются спрайтами — игра перерисовывает их небольшой набор тайлов на каждом кадре, и подставляет подвижные спрайты с ними в нужные места, имитируя текстуру асфальта спрайтами за неимением второго фона.
Таким образом эта игра не используя чипов расширения, честно «выходила за рамки» запланированных возможностей консоли, что было в те времена не редкостью.
(Второй уровень 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 комментариев
youtu.be/djW3WTEU-LE?t=3m — часть уровня вращается, верхняя часть получается отсечена по HBlank? Как тогда реализованы звезды? Спрайтами?
Еще любопытно, как сделан collision detection для вращающихся стен.
youtu.be/djW3WTEU-LE?t=4m48s — чуть позднее там же псевдо-3d на заднем фоне, с какой-то еще подкруткой палитры? Звезды все же сделаны спрайтами, походу — когда из-за трубы вылетает 4 робота, звезды гасятся.
Эффект трубы проще и не требует Mode 7 — это просто HBlank-отсечение на каждой строке по формуле так что расстояние между «выборками текстуры» сохраняется. Эффекта перспективы же нет — вертикальные линии остаются вертикальными по всей длине трубы.
Прочитал. Да, прикольно. В целом понятно — любой из 8-ми DMA-каналов можно перевести в режим автоматической записи в порты видеочипа по HBlank. Надо будет дополнить статью про видеочип SNES этим прежде чем сюда постить.
На SNES побочных эффектов от DMA нет, просто замедляется выполнение кода, но это не проблема, т.к. наличие HDMA позволяет избежать игр с точными таймингами выполнения. На Genesis на время передачи тормозится Z80, а на нём любят играть сэмплы, поэтому звук очень сильно портится (выпадения с частотой 50/60 гц), характерные искажения голосов возникают во многих играх по этой причине. Не так давно придумали обход этой проблемы: буферизировать звук из ПЗУ в ОЗУ Z80 во время прохода луча по видимой части кадра, а во время VBlank играть буфер из ОЗУ. При отсутствии обращений к ПЗУ во время VBlank Z80 продолжает работать (остановка активируется только в момент обращения к ПЗУ). К сожалению, во времена расцвета платформы до этого не додумались.