การทำเอฟเฟกต์ SNES Mode 7 (เลียนแบบการแปลง) ใน pygame


19

มีคำตอบสั้น ๆ เกี่ยวกับการทำเอฟเฟกต์โหมด 7 / mario kart ใน pygame หรือไม่?

ฉันได้ทำ Google อย่างครอบคลุมเอกสารทั้งหมดที่ฉันสามารถหาได้มีหลายสิบหน้าในภาษาอื่น (asm, c) ที่มีสมการที่ดูแปลก ๆ มากมาย

เป็นการดีที่ฉันต้องการค้นหาสิ่งที่อธิบายเพิ่มเติมเป็นภาษาอังกฤษมากกว่าในแง่คณิตศาสตร์

ฉันสามารถใช้ PIL หรือ pygame เพื่อจัดการภาพ / พื้นผิวหรืออะไรก็ตามที่จำเป็น

ฉันต้องการบรรลุผลโหมด 7 ใน pygame แต่ฉันดูเหมือนใกล้ถึงจุดสิ้นสุดของปัญญา ความช่วยเหลือจะได้รับการชื่นชมอย่างมาก แหล่งข้อมูลหรือคำอธิบายใด ๆ ก็ตามที่คุณสามารถจัดหาได้นั้นยอดเยี่ยมแม้ว่ามันจะไม่ง่ายอย่างที่ฉันต้องการ

ถ้าฉันสามารถหาได้ฉันจะเขียนวิธีการทำโหมด 7 สำหรับหน้ามือใหม่

แก้ไข: โหมด 7 doc: http://www.coranac.com/tonc/text/mode7.htm


5
ดูเหมือนว่าจะมีสมการอยู่ที่นี่: en.wikipedia.org/wiki/Mode_7 แม้ว่าทุกวันนี้เรามีการเร่งความเร็วแบบ 3 มิติสิ่งต่าง ๆ เช่นโหมด 7 หรือวิธีการทำงานที่แปลกประหลาดนั้นเป็นสิ่งที่อยากรู้มากกว่าโซลูชัน
salmonmoose

3
@ 2D_ ซื้อหน้านี้อธิบายอัลกอริทึมเป็นอย่างดีสำหรับฉัน คุณต้องการที่จะรู้วิธีที่จะทำหรือคุณต้องการที่จะใช้งานได้แล้วสำหรับคุณ?
Gustavo Maciel

1
@stephelton บนระบบ SNES เลเยอร์เดียวที่สามารถบิดเบี้ยวหมุนได้ .. (การแปลงเลียนแบบที่ใช้กับเมทริกซ์) คือชั้นที่เจ็ด เลเยอร์พื้นหลัง ชั้นอื่น ๆ ทั้งหมดถูกนำมาใช้เพื่อสไปรท์ง่ายดังนั้นหากคุณต้องการผล 3D คุณจะต้องใช้ชั้นนี้นี้เป็นที่ที่ชื่อมาจาก :)
Gustavo Maciel

3
@GustavoMaciel: มันไม่ถูกต้องสักหน่อย SNES มี 8 โหมดที่แตกต่างกัน (0-7) ซึ่งมีเลเยอร์พื้นหลังมากถึง 4 ชั้นมีฟังก์ชั่นที่แตกต่างกัน แต่มีเพียงโหมดเดียว (โหมด 7 ดังนั้นชื่อ) รองรับการหมุนและการปรับขนาด (และ จำกัด คุณไว้ที่เลเยอร์เดียว) คุณไม่สามารถรวมโหมดต่างๆได้
Michael Madsen

1
@Michael: ฉันยังจะเพิ่ม: SNES เป็นหนึ่งในคอนโซลยอดนิยมแรกที่ใช้เอฟเฟกต์นี้ใน 90's (กับเกม F-Zero) และนั่นเป็นเหตุผลว่าทำไมหลังจากนั้นผู้คนเริ่มอ้างถึงเอฟเฟกต์ระนาบพื้นผิวแนวนอนทั้งหมด เกมเป็น "โหมด 7" ในความเป็นจริงเอฟเฟกต์แบบนี้ไม่ใช่เรื่องใหม่และมีมานานแล้วในอาร์เคด Space Harrier / Hang-On (1985)
tigrou

คำตอบ:


45

โหมด 7เป็นเอฟเฟกต์ที่ง่ายมาก มันฉายภาพพื้นผิว 2 มิติ 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)เป็นศูนย์กลางของหน้าจอ)

ถ้าเรารวมทุกอย่างเข้าด้วยกันมันจะกลายเป็น: (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
     ...
  }

สิ่งหนึ่งที่สุดท้าย: ถ้าคุณต้องการสร้างเกมมาริโอคาร์ทฉันคิดว่าคุณต้องการหมุนแผนที่ด้วย มันยังง่ายมาก: หมุน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)

ป้อนคำอธิบายรูปภาพที่นี่

NOTE2: ฉันใช้คณิตศาสตร์อย่างง่ายเพราะคุณบอกว่าคุณต้องการบางสิ่งที่เรียบง่าย (และดูเหมือนจะไม่คุ้นเคยกับคณิตศาสตร์เวกเตอร์) คุณสามารถทำสิ่งเดียวกันโดยใช้สูตรหรือแบบฝึกหัดที่คุณให้ วิธีที่พวกเขาทำมันมีความซับซ้อนมากขึ้น แต่คุณมีความเป็นไปได้มากขึ้นสำหรับการกำหนดค่าเอฟเฟกต์ (ในที่สุดมันก็ใช้งานได้เหมือนกัน ... )

สำหรับข้อมูลเพิ่มเติมฉันขอแนะนำให้อ่าน: http://en.wikipedia.org/wiki/3D_project#Perspective_project


สิ่งหนึ่งที่จะเพิ่มเนื่องจากความบาปและมุมของมุมมักจะมีค่าคงที่ต่อเฟรมให้แน่ใจว่าได้คำนวณพวกมันนอกวงเพื่อหาตำแหน่ง x, y ทั้งหมด
hobberwickey

1

นี่คือรหัสที่จะทำให้มัน ฉันเป็นรหัสเดียวกันของการกวดวิชาที่ฉันทำในบล็อกของฉัน ตรวจสอบที่นั่นเพื่อเรียนรู้วิธีการโหมด 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);
        }
    }
}

ผลลัพธ์คือ:

ป้อนคำอธิบายรูปภาพที่นี่


คำอธิบายอยู่ที่นี่programandocoisas.blogspot.com.br คุณสามารถหาขั้นตอนการสอนได้ทีละขั้นตอนเพื่อสร้างเอฟเฟกต์นี้ แต่ฉันจะอัปเดตโพสต์ของฉันเพื่อให้ความคิดเห็นดีกว่า;)
Vinícius Biavatti
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.