Melakukan efek SNES Mode 7 (affine transform) di pygame


19

Apakah ada hal seperti jawaban singkat tentang cara melakukan efek tipe Mode 7 / mario kart di pygame?

Saya telah melakukan pencarian di Google secara ekstensif, semua dokumen yang dapat saya buat adalah lusinan halaman dalam bahasa lain (asm, c) dengan banyak persamaan yang tampak aneh dan semacamnya.

Idealnya, saya ingin menemukan sesuatu yang lebih banyak dijelaskan dalam bahasa Inggris daripada dalam istilah matematika.

Saya dapat menggunakan PIL atau pygame untuk memanipulasi gambar / tekstur, atau apa pun yang diperlukan.

Saya benar-benar ingin mencapai efek mode 7 di pygame, tapi saya sepertinya dekat dengan kecerdasan saya. Bantuan akan sangat dihargai. Setiap dan semua sumber daya atau penjelasan yang dapat Anda berikan akan menjadi luar biasa, meskipun tidak semudah yang saya inginkan.

Jika saya bisa mengetahuinya, saya akan menulis definitif bagaimana melakukan mode 7 untuk halaman pemula.

edit: mode 7 doc: http://www.coranac.com/tonc/text/mode7.htm


5
tampaknya ada persamaan di sini: en.wikipedia.org/wiki/Mode_7 Meskipun, saat ini kita memiliki akselerasi 3D, hal-hal seperti Mode 7, atau cara doom bekerja lebih merupakan keingintahuan daripada solusi.
salmonmoose

3
@ 2D_Beli halaman ini menjelaskan algoritma dengan sangat baik untuk saya. Anda ingin tahu bagaimana melakukannya, atau Anda ingin itu sudah diterapkan untuk Anda?
Gustavo Maciel

1
@stephelton Pada sistem SNES, satu-satunya lapisan yang dapat terdistorsi, diputar .. (diterapkan transformasi affine dengan matriks) adalah lapisan ketujuh. Lapisan Background. Semua layer lain digunakan untuk sprite sederhana, Jadi jika Anda menginginkan efek 3D, Anda harus menggunakan layer ini, dari sinilah namanya berasal :)
Gustavo Maciel

3
@ GustavoMaciel: Itu agak tidak akurat. SNES memiliki 8 mode yang berbeda (0-7), di mana hingga 4 lapisan latar belakang memiliki fungsi yang berbeda, tetapi hanya satu mode (mode 7, maka namanya) yang mendukung rotasi dan penskalaan (dan juga membatasi Anda ke satu lapisan). Anda tidak dapat benar-benar menggabungkan mode.
Michael Madsen

1
@Michael: saya juga akan menambahkan: SNES adalah salah satu konsol populer pertama yang menggunakan efek ini di tahun 90-an (dengan game F-Zero), dan itulah sebabnya setelah itu orang-orang mulai merujuk semua efek bidang 2D yang dipetakan tekstur horisontal yang terlihat di lain game sebagai "mode 7". Pada kenyataannya, efek semacam ini bukanlah hal baru dan sudah ada sejak lama di arcade, lih. Space Harrier / Hang-On (1985).
tigrou

Jawaban:


45

Mode 7 adalah efek yang sangat sederhana. Ini memproyeksikan tekstur x / y 2D (atau ubin) ke beberapa lantai / langit-langit. SNES lama menggunakan perangkat keras untuk melakukan ini, tetapi komputer modern sangat kuat sehingga Anda dapat melakukan ini secara realtime (dan tidak perlu ASM seperti yang Anda sebutkan).

Rumus matematika 3D dasar untuk memproyeksikan titik 3D (x, y, z) ke titik 2D (x, y) adalah:

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

Ketika Anda memikirkannya, itu masuk akal. Objek yang jauh jaraknya lebih kecil dari objek di dekat Anda. Pikirkan tentang rel kereta api yang menuju ke mana-mana:

masukkan deskripsi gambar di sini

Jika kita melihat kembali nilai input rumus: xdan yakan menjadi piksel saat ini kami sedang memproses, dan zakan menjadi informasi jarak tentang seberapa jauh titik tersebut. Untuk memahami apa yang zseharusnya, lihat gambar itu, itu menunjukkan znilai untuk gambar di atas:

masukkan deskripsi gambar di sini

ungu = jarak dekat, merah = jauh

Jadi dalam contoh ini, znilainya adalah y - horizon(dengan asumsi (x:0, y:0)berada di tengah layar)

Jika kita menyatukan semuanya, itu menjadi: (pseudocode)

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
     ...
  }

Satu hal terakhir: jika Anda ingin membuat game mario kart, saya kira Anda juga ingin memutar peta. Yah itu juga sangat mudah: memutar sxdan sysebelum mendapatkan nilai tekstur. Berikut ini rumusnya:

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

dan jika Anda ingin memindahkan melalui peta, cukup tambahkan beberapa offset sebelum mendapatkan nilai tekstur:

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

CATATAN: Saya menguji algoritme (hampir salin-tempel) dan berfungsi. Berikut ini contohnya: http://glslsandbox.com/e#26532.3 (memerlukan browser terbaru dan WebGL diaktifkan)

masukkan deskripsi gambar di sini

NOTE2: saya menggunakan matematika sederhana karena Anda mengatakan Anda menginginkan sesuatu yang sederhana (dan sepertinya tidak terbiasa dengan matematika vektor). Anda dapat mencapai hal yang sama menggunakan rumus wikipedia atau tutorial yang Anda berikan. Cara mereka melakukannya jauh lebih kompleks tetapi Anda memiliki lebih banyak kemungkinan untuk mengkonfigurasi efeknya (pada akhirnya itu berfungsi sama ...).

Untuk informasi lebih lanjut, saya sarankan membaca: http://en.wikipedia.org/wiki/3D_projection#Perspective_projection


Satu hal yang perlu ditambahkan, karena dosa dan cos sudut sebagian besar konstan per frame, pastikan untuk menghitungnya di luar loop untuk mencari tahu semua posisi x, y.
hobberwickey

1

Ini kode untuk membuatnya. Saya adalah kode yang sama dengan tutorial yang saya buat di blog saya . Periksa di sana untuk mempelajari metode Mode 7 dan RayCasting.

Pada dasarnya, kode semu itu:

//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

Berikut adalah kode yang saya buat di JAWA, mengikuti tutorial saya.

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);
        }
    }
}

Hasilnya adalah:

masukkan deskripsi gambar di sini


Penjelasannya ada di sini programandocoisas.blogspot.com.br . Anda dapat menemukan di sana tutorial langkah demi langkah untuk membuat efek ini. Tetapi saya akan memperbarui posting saya agar komentar menjadi lebih baik;).
Vinícius Biavatti
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.