Здійснення ефекту SNES Mode 7 (афінне перетворення) в пігамі


19

Чи є така річ, як коротка відповідь про те, як зробити ефект типу Mode 7 / mario kart у pygame?

Я широко розібрався в Google, всі документи, які я можу придумати, - це десятки сторінок на інших мовах (asm, c) з безліччю дивно виглядаючих рівнянь тощо.

В ідеалі я хотів би знайти щось пояснене англійською, ніж математичним.

Я можу використовувати PIL або пігаму для маніпулювання зображенням / текстурою або будь-яким іншим необхідним.

Мені дуже хотілося б досягти ефекту режиму 7 у пігграмі, але мені здається близьким до кінця моєї дотепності. Допомога буде дуже вдячна. Будь-які ресурси та пояснення, які ви можете надати, були б фантастичними, навіть якщо вони не такі прості, як хотілося б.

Якщо я можу це зрозуміти, я напишу остаточно, як зробити режим 7 для сторінки для новачків.

редагувати: режим 7 doc: http://www.coranac.com/tonc/text/mode7.htm


5
тут, здається, є рівняння: en.wikipedia.org/wiki/Mode_7 Хоча в наші дні у нас є 3D-прискорення, такі речі, як Режим 7, або хитрий спосіб роботи дум - це більше цікавість, ніж рішення.
лосось

3
@ 2D_Додати цю сторінку, мені дуже добре поясніть алгоритм. Ви хочете знати, як це зробити, або ви хочете, щоб це вже було реалізовано для вас?
Густаво Масель

1
@stephelton У системах SNES єдиним шаром, який можна було б спотворити, повернути .. (застосовано афінними перетвореннями з матрицями) - сьомий шар. Фоновий шар. Всі інші шари були використані для простих спрайтів, тому, якщо ви хотіли 3D-ефект, вам довелося б використовувати цей шар, ось звідки і пішла назва :)
Gustavo Maciel

3
@GustavoMaciel: Це трохи неточно. SNES мав 8 різних режимів (0-7), у яких до 4 фонових шарів мали різну функціональність, але лише один режим (режим 7, звідси назва) підтримував обертання та масштабування (а також обмежував вас одним шаром). Ви не могли реально поєднувати режими.
Майкл Мадсен

1
@Michael: я також додам: SNES була однією з перших популярних консолей, яка застосувала цей ефект у 90-х (із грою F-Zero), і саме тому після цього люди починають посилатись на всі 2D горизонтальні текстуровані площинні ефекти, що спостерігаються в інших ігри як "режим 7". Насправді подібний ефект не був новим і існував давно в аркадах, пор. Космічний Harrier / Hang-On (1985).
тигру

Відповіді:


45

Режим 7 - це дуже простий ефект. Він проектує 2D x / y текстури (або плитки) на деякий підлогу / стелю. Старі SNES використовують для цього обладнання, але сучасні комп’ютери настільки потужні, що ви можете це робити в режимі реального часу (і немає потреби в ASM, як ви згадуєте).

Основна 3D-математична формула для проектування 3D-точки (x, y, z) на 2D-точку (x, y):

x' = x / z;
y' = y / z; 

Коли ви думаєте про це, це має сенс. Об'єкти, які знаходяться на відстані, менші, ніж об'єкти поблизу. Подумайте про залізничні колії, які йдуть нікуди:

введіть тут опис зображення

Якщо ми поглянемо на вхідні значення формули: xі yбуде поточним пікселем, який ми обробляємо, і zбуде інформація про відстань про те, наскільки далеко знаходиться точка. Щоб зрозуміти, що zмає бути, подивіться на цю картинку, вона показує zзначення для зображення вище:

введіть тут опис зображення

фіолетовий = близька відстань, червоний = далеко

Отже, у цьому прикладі zзначення є y - horizon(припустимо (x:0, y:0), що в центрі екрана)

Якщо ми зведемо все разом, це стане: (псевдокод)

for (y = -yres/2 ; y < yres/2 ; y++)
  for (x = -xres/2 ; x < xres/2 ; x++)
  {
     horizon = 20; //adjust if needed
     fov = 200; 

     px = x;
     py = fov; 
     pz = y + horizon;      

     //projection 
     sx = px / pz;
     sy = py / pz; 

     scaling = 100; //adjust if needed, depends of texture size
     color = get2DTexture(sx * scaling, sy * scaling);  

     //put (color) at (x, y) on screen
     ...
  }

Останнє: якщо ви хочете зробити маріо-гру, я маю на увазі, що ви також хочете повернути карту. Ну це також дуже просто: обертайте sxі syперш ніж отримувати значення текстури. Ось формула:

  x' = x * cos(angle) - y * sin(angle);
  y' = x * sin(angle) + y * cos(angle);

і якщо ви хочете перейти через карту, просто додайте зміщення, перш ніж отримувати значення текстури:

  get2DTexture(sx * scaling + xOffset, sy * scaling + yOffset);

ПРИМІТКА: я перевірив алгоритм (майже копіювати-вставити), і він працює. Ось приклад: http://glslsandbox.com/e#26532.3 (потрібні останній веб-переглядач та увімкнено WebGL)

введіть тут опис зображення

ПРИМІТКА2: Я використовую просту математику, тому що ви сказали, що хочете чогось простого (і не здається знайомим з векторною математикою). Ви можете домогтися тих самих речей, використовуючи формулу wikipedia або підручники, які ви даєте. Те, як вони це зробили, набагато складніше, але у вас є набагато більше можливостей для налаштування ефекту (врешті-решт, він працює так само ...).

Для отримання додаткової інформації пропоную прочитати: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection


Варто додати ще одне, оскільки sin та cos кута здебільшого постійні на кадр, не забудьте обчислити їх поза петлею для з'ясування всіх x, y позицій.
hobberwickey

1

Ось код для його виготовлення. Я той самий код підручника, який я зробив у своєму блозі . Перевірте там, щоб вивчити метод Mode 7 та RayCasting.

В основному, це псевдокод:

//This is the pseudo-code to generate the basic mode7

for each y in the view do
    y' <- y / z
    for each x in the view do
        x' <- x / z
        put x',y' texture pixel value in x,y view pixel
    end for
    z <- z + 1
end for

Ось код, який я зробив у JAVA, дотримуючись мого підручника.

package mode7;

import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JFrame;

/**
 * Mode 7 - Basic Implementation
 * This code will map a texture to create a pseudo-3d perspective.
 * This is an infinite render mode. The texture will be repeated without bounds.
 * @author VINICIUS
 */
public class BasicModeSeven {

    //Sizes
    public static final int WIDTH = 800;
    public static final int WIDTH_CENTER = WIDTH/2;
    public static final int HEIGHT = 600;
    public static final int HEIGHT_CENTER = HEIGHT/2;

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) throws IOException {

        //Create Frame
        JFrame frame = new JFrame("Mode 7");
        frame.setSize(WIDTH, HEIGHT);
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setLocationRelativeTo(null);
        frame.setVisible(true);

        //Create Buffered Images:
        //image - This is the image that will be printed in the render view
        //texture - This is the image that will be mapped to the render view
        BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
        BufferedImage texture = ImageIO.read(new File("src/mode7/texture.png"));

        //The new coords that will be used to get the pixel on the texture
        double _x, _y;

        //z - the incrementable variable that beggins at -300 and go to 300, because 
        //the depth will be in the center of the HEIGHT
        double z =  HEIGHT_CENTER * -1;

        //Scales just to control de scale of the printed pixel. It is not necessary
        double scaleX = 16.0;
        double scaleY = 16.0; 

        //Mode 7 - loop (Left Top to Down)
        for(int y = 0; y < HEIGHT; y++){

            _y = y / z; //The new _y coord generated
            if(_y < 0)_y *= -1; //Control the _y because the z starting with a negative number
            _y *= scaleY; //Increase the size using scale
            _y %= texture.getHeight(); //Repeat the pixel avoiding get texture out of bounds 

            for(int x = 0; x < WIDTH; x++){

                _x = (WIDTH_CENTER - x) / z; //The new _x coord generated
                if(_x < 0)_x *= -1; //Control the _x to dont be negative
                _x *= scaleX; //Increase the size using scale
                _x %= texture.getWidth(); //Repeat the pixel avoiding get texture out of bounds 

                //Set x,y of the view image with the _x,_y pixel in the texture
                image.setRGB(x, y, texture.getRGB((int)_x, (int)_y));
            }

            //Increment depth
            z++;
        }

        //Loop to render the generated image
        while(true){
            frame.getGraphics().drawImage(image, 0, 0, null);
        }
    }
}

Результат:

введіть тут опис зображення


Пояснення тут програмаndocoisas.blogspot.com.br . Ви можете знайти підручник покроково, щоб досягти цього ефекту. Але я оновлю свій пост, щоб коментарі стали кращими;).
Vinícius Biavatti
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.