PygameでSNESモード7(アフィン変換)効果を実行する


19

pygameでモード7 /マリオカートタイプの効果を行う方法についての簡単な答えなどはありますか?

私は広範囲にグーグルで調べましたが、私が思いつくことのできるドキュメントはすべて、他の言語(asm、c)の多数のページであり、多くの奇妙に見える方程式などがあります。

理想的には、数学的な用語よりも英語で説明されたものを見つけたいです。

PILまたはpygameを使用して、画像/テクスチャ、またはその他必要なものを操作できます。

私は本当にpygameでモード7の効果を達成したいのですが、私はウィットの終わりに近いようです。ヘルプは大歓迎です。あなたが提供できるすべてのリソースや説明は、たとえそれらが私が望んでいるほど単純ではない場合でも、素晴らしいでしょう。

私がそれを理解できるなら、私は初心者ページのためにモード7をする決定的な方法を書きます。

編集:モード7 doc:http : //www.coranac.com/tonc/text/mode7.htm


5
en.wikipedia.org/wiki/Mode_7ここには方程式があるように思われます が、最近では3Dアクセラレーションがありますが、Mode 7や破滅的な方法のようなものは解決策というよりも好奇心が強いものです。
サーモンムース

3
@ 2D_Guyこのページでは、アルゴリズムを非常によく説明しています。あなたはそれを行う方法を知りたいですか、それともあなたのために既に実装されていますか?
グスタボマシエル

1
@stephelton SNESシステムでは、歪み、回転が可能な唯一のレイヤー(マトリックスを使用したアフィン変換の適用)は7番目のレイヤーです。背景レイヤー。他のすべてのレイヤーは単純なスプライトに使用されていたため、3D効果が必要な場合は、このレイヤーを使用する必要がありました。これは名前の由来です:)
Gustavo Maciel

3
@GustavoMaciel:それは少し不正確です。SNESには8つの異なるモード(0-7)があり、最大4つのバックグラウンドレイヤーが異なる機能を持ちましたが、1つのモード(モード7、したがって名前)のみが回転とスケーリングをサポートしました(また、単一レイヤーに制限されました)。モードを実際に組み合わせることができませんでした。
マイケルマドセン

1
@Michael:私も付け加えます:SNESは90年代(ゲームF-Zeroで)にこのエフェクトを使用した最初の人気のあるコンソールの1つであり、そのため人々は他の「モード7」としてのゲーム。実際、この種の効果は新しいものではなく、昔からアーケードに存在していました。スペースハリアー/ハングオン(1985)。
ティグロー

回答:


45

モード7は非常にシンプルな効果です。2D x / yテクスチャ(またはタイル)を床/天井に投影します。古いSNESはこれを行うためにハードウェアを使用しますが、最新のコンピューターは非常に強力であるため、このリアルタイムを実行できます(言及したようにASMは必要ありません)。

3Dポイント(x、y、z)を2Dポイント(x、y)に射影するための基本的な3D数式は次のとおりです。

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

あなたがそれについて考えるとき、それは理にかなっています。遠くにあるオブジェクトは、近くのオブジェクトよりも小さくなります。どこにも行かない線路について考えてみましょう:

ここに画像の説明を入力してください

我々は、式の入力値に戻って見れば:xy私たちが処理している現在のピクセルになり、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
     ...
  }

最後に、マリオカートゲームを作成する場合は、マップも回転させたいと思います。まあその非常に簡単:回転しsxsyテクスチャ値を取得する前に。ここに式があります:

  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:私はあなたが単純なものが欲しいと言ったので単純な数学を使用します(そしてベクトル数学に精通していないようです)。ウィキペディアの公式やチュートリアルを使用して、同じことを達成できます。彼らがそれをした方法ははるかに複雑ですが、エフェクトを設定するためのはるかに多くの可能性があります(最終的には同じように動作します...)。

詳細については、読むことをお勧めします:http : //en.wikipedia.org/wiki/3D_projection#Perspective_projection


角度のsinとcosはほとんどフレームごとに一定であるため、追加する1つのことは、すべての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です。この効果を実現するために、チュートリアルを段階的に見つけることができます。しかし、コメントを改善するために投稿を更新します;)。
ビニキアスビアヴァッティ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.