Здійснення ефекту 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.