파이 게임에서 SNES 모드 7 (affine transform) 효과 수행


19

파이 게임에서 모드 7 / 마리오 카트 유형 효과를 수행하는 방법에 대한 짧은 대답과 같은 것이 있습니까?

나는 광범위하게 봤다. 내가 생각해 낼 수있는 모든 문서는 많은 이상한 모양의 방정식과 함께 다른 언어 (asm, c)의 수십 페이지입니다.

이상적으로는 수학 용어보다 영어로 더 많은 것을 설명하고 싶습니다.

PIL 또는 파이 게임을 사용하여 이미지 / 텍스처 또는 기타 필요한 것을 조작 할 수 있습니다.

나는 파이 게임에서 모드 7 효과를 정말로 얻고 싶지만 내 지혜의 끝에 가까워 보입니다. 도움을 주시면 감사하겠습니다. 내가 원하는만큼 간단하지 않더라도 제공 할 수있는 모든 자료 나 설명은 환상적입니다.

내가 알아낼 수 있다면 초보자 페이지에 모드 7을 수행하는 방법을 결정합니다.

편집 : 모드 7 문서 : http://www.coranac.com/tonc/text/mode7.htm


5
여기에는 방정식이있는 것 같습니다. en.wikipedia.org/wiki/Mode_7 요즘에는 3D 가속 기능이 있지만 모드 7 또는 해킹 방식의 파멸이 작동하는 것은 솔루션보다 호기심이 많습니다.
salmonmoose

3
@ 2D_Guy이 페이지는 알고리즘을 잘 설명해줍니다. 어떻게해야하는지 알고 싶거나 이미 구현하고 싶습니까?
Gustavo Maciel

1
@stephelton SNES 시스템에서 왜곡되고 회전 할 수있는 유일한 레이어는 (행렬로 적용된 아핀 변환) 일곱 번째 레이어입니다. 배경 레이어. 다른 모든 레이어는 간단한 스프라이트에 사용되었으므로 3D 효과를 원한다면이 레이어를 사용해야했습니다. 이름은 다음과 같습니다.
Gustavo Maciel

3
@GustavoMaciel : 조금 부정확합니다. SNES에는 8 개의 서로 다른 모드 (0-7)가 있으며 최대 4 개의 배경 레이어가 서로 다른 기능을 갖지만 하나의 모드 (모드 7, 따라서 이름) 만 회전 및 크기 조정을 지원했습니다 (또한 단일 레이어로 제한). 실제로 모드를 결합 할 수 없습니다.
Michael Madsen

1
@Michael : 나는 또한 덧붙일 것입니다 : SNES는 90 년대 에이 효과를 사용하는 최초의 인기있는 콘솔 중 하나였습니다 (게임 F-Zero 사용). 그런 다음 사람들이 다른 2D 가로 텍스처 매핑 평면 효과를 참조하기 시작한 이유입니다. "모드 7"로 게임. 실제로 이런 종류의 효과는 새로운 것이 아니며 오래 전에 아케이드에서 존재했습니다. 우주 해리어 / 행온 (1985).
tigrou

답변:


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위의 이미지 값 이 표시 됩니다.

여기에 이미지 설명을 입력하십시오

자주색 = 근거리, 적색 = 멀리

따라서이 예에서 zvalue는 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 사용 가능)

여기에 이미지 설명을 입력하십시오

NOTE2 : 나는 당신이 간단한 것을 원한다고 말했기 때문에 간단한 수학을 사용합니다 (벡터 수학에 익숙하지 않은 것 같습니다). 당신이 제공하는 위키피디아 공식이나 튜토리얼을 사용하여 같은 것을 달성 할 수 있습니다. 그들이했던 방식은 훨씬 더 복잡하지만 효과를 구성 할 수있는 가능성이 훨씬 더 많습니다 (결국 동일하게 작동합니다 ...).

자세한 내용은 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);
        }
    }
}

결과는 다음과 같습니다.

여기에 이미지 설명을 입력하십시오


설명은 여기 programandocoisas.blogspot.com.br 입니다. 이 효과를 내기 위해 단계별로 튜토리얼을 찾을 수 있습니다. 그러나 의견을 더 좋게하기 위해 게시물을 업데이트 할 것입니다.).
Vinícius Biavatti
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.