Перспективное зрение на холсте

Сегодня я привожу тему о Pseudo 3D и перспективах.

Я просматривал видео # 1 Учебное пособие по классическому 3D-визуализации Java: создание 3D-мира, где он использовал метод рендеринга псевдо-3D-потолка и этаж. Я попытался найти учебник или имя метода, который он использовал, но я не нашел. Я видел алгоритм, но понять его неясно. Я начал искать перспективную графику (точки схода, горизонт...), но уникальная вещь, которую я получил, - статический рисунок. Я хочу применить иллюзию, перемещая камеру внутри плана и перемещая ее. В приведенном ниже примере мы рассмотрим перспективный пол и потолок, которые я хочу сделать.

Статическое изображение перспективы Статическое изображение перспективы

Это только изображение, но мой первый вопрос: "Я действительно могу сделать движение камеры в этом окружении, например, вращение и перемещение x и ось y?". Я попытался сделать 2 точки схождения на холсте, создавая линии для каждой степени 15º, и у меня появилась иллюзия перспективы, но я не мог найти способ сделать поворот или движение. В этом видео я видел пиксели, создающие 2 измерения, используя только зеленые и синие цвета, но я хочу сделать это с помощью строк, чтобы понять, как это работает.

введите описание изображения здесь

Нет места, которое учит шаг за шагом, как сделать перспективу с движениями. Я не нашел. Я проверил видеоролики разработчиков 3D-игр на Java и персонажа Markus, создавшего игру под названием "Прелюдия камерного", используя метод видео, но я не нашел объяснения для этого короля рендеринга.

введите описание изображения здесь

Позволяет предположить, что мне нужно создать план, используя сетку. как логика, которую я должен применять в строках, чтобы создать движение? Я действительно хочу понять логику создания такого псевдо-3D, без использования фреймворков или подобной вещи. Спасибо, что помогли мне! Я буду ждать вашего ответа.

Я проверил что-то о MODE 7 SNES. Это хороший способ сделать это, я думаю. Я должен просто понять, как это работает, и как сделать поворот.

введите описание изображения здесь

** Примечание: я не могу использовать raycasting для этого. Raycasting, я буду использовать, чтобы создать стены.

2
задан Vinícius Biavatti 13 сент. '17 в 13:59
источник поделиться
2 ответов

Интересная проблема. Я не сопротивлялся и кодировал его для удовольствия, так что вот некоторые идеи... Ну, есть два основных подхода к этому. Одна из них - растровая подделка, а вторая - векторная. Я опишу последний, поскольку вы можете сделать с ним гораздо больше.

Векторный подход

Этот подход не притворяется чем-то действительно 3D. Остальное зависит от рендеринга, который вы хотите использовать для этого... На данный момент я предполагаю, что вы можете отображать строки 2D. Все куски кода находятся в С++.

  • Transformations

    Вам нужна векторная математика для преобразования точек между миром и пространством камеры и обратно. В 3D обычно используются матрицы гомогенного преобразования 4x4, и многие программы API поддерживают их изначально. Я буду основывать свою математику на макете макетов OpenGL, которые определяют порядок использования умножения. Для получения дополнительной информации я настоятельно рекомендую прочитать следующее:

    Как я много использую. Связанные ответы также полезны особенно для графического конвейера 3D и полной псевдо-обратной матрицы. Сам ответ - это базовые знания, необходимые для рендеринга 3D в двух словах (низкий уровень без необходимости в какой-либо lib отдельно от материала рендеринга).

    Для этого также существуют libs, такие как GLM, поэтому, если вы хотите, вы можете использовать любую линейную алгебру, поддерживающую матрицы 4x4 и 4D, вместо мой код.

    Итак, пусть две матрицы 4x4 1 (camera), представляющие нашу систему координат камеры, и вторую (icamera), которая является ее обратной. Теперь, если мы хотим трансформироваться между миром и пространством экрана, мы просто делаем это:

    P = camera*Q
    Q = icamera*P
    

    где P(x,y,z,1) - точка в системе координат камеры, а Q(x,y,z,1) - одна и та же точка глобальной глобальной системы координат.

  • перспектива

    Это делается просто путем деления P на его координату z. Это будет масштабировать объекты вокруг (0,0), поэтому чем меньше объект будет меньше, тем меньше будет. Если мы добавим некоторое разрешение экрана и коррекцию оси, мы можем использовать это:

    void perspective(double *P) // apply perspective transform on P
        {
        // perspectve division
        P[0]*=znear/P[2];
        P[1]*=znear/P[2];
        // screen coordinate system
        P[0]=xs2+P[0];          // move (0,0) to screen center
        P[1]=ys2-P[1];          // axises: x=right, y=up
        }
    

    поэтому точка 0,0 является центром экрана. xs2,ys2 - половина разрешения экрана, а znear - фокусное расстояние проекции. Поэтому прямоугольник плоскости XY с разрешением экрана и центром в (0,0,znear) будет точно экранировать экран.

  • рендеринг 3D-линии

    Мы можем использовать любые примитивы для рендеринга. Я выбрал линию, поскольку она очень проста и может многое достичь. Итак, мы хотим отобразить строку 3D, используя 2D рендеринг строк API (любого рода). Я VCL, поэтому я выбрал VCL/GDI Canvas, который должен быть очень похож на ваш Canvas.

    В качестве ввода мы получили две 3D точки в глобальной мировой системе координат. Чтобы сделать его строкой 2D, нам нужно преобразовать позицию 3D в пространство 2D. Это делается с помощью matrix*vector умножения.

    Из этого мы получаем две точки 3D, но в системе координат камеры. Теперь нам нужно закрепить линию по нашей области просмотра (Frustrum). Мы можем игнорировать оси x,y, поскольку 2D линия api обычно делает это для нас в любом случае. Итак, осталось только клип z. Frustrum в z определяется как znear и zfar. Где zfar - наше максимальное расстояние видимости от фокальной точки камеры. Поэтому, если наша линия полностью до или после нашего z-range, мы игнорируем ее и не визуализируем. Если он внутри, мы его визуализируем. Если он пересекает znear или zfar, мы отключим внешнюю часть (путем линейной интерполяции координат x,y).

    Теперь мы просто применяем взгляд на обе точки и делаем строку 2D, используя их координаты x,y.

    Мой код для этого выглядит следующим образом:

    void draw_line(TCanvas *can,double *pA,double *pB)  // draw 3D line
        {
        int i;
        double D[3],A[3],B[3],t;
        // transform to camera coordinate system
        matrix_mul_vector(A,icamera,pA);
        matrix_mul_vector(B,icamera,pB);
        // sort points so A.z<B.z
        if (A[2]>B[2]) for (i=0;i<3;i++) { D[i]=A[i]; A[i]=B[i]; B[i]=D[i]; }
        // D = B-A
        for (i=0;i<3;i++) D[i]=B[i]-A[i];
        // ignore out of Z view lines
        if (A[2]>zfar) return;
        if (B[2]<znear) return;
        // cut line to view if needed
        if (A[2]<znear)
            {
            t=(znear-A[2])/D[2];
            A[0]+=D[0]*t;
            A[1]+=D[1]*t;
            A[2]=znear;
            }
        if (B[2]>zfar)
            {
            t=(zfar-B[2])/D[2];
            B[0]+=D[0]*t;
            B[1]+=D[1]*t;
            B[2]=zfar;
            }
        // apply perspective
        perspective(A);
        perspective(B);
        // render
        can->MoveTo(A[0],A[1]);
        can->LineTo(B[0],B[1]);
        }
    
  • Отображение XZ плоскости

    Мы можем визуализировать плоскость земли и небо, используя нашу линию 3D как сетку квадратов. Таким образом, мы просто создаем циклы for, отображая выравниваемые линии x с осевой ориентацией и y - выравниваемые по оси линии, покрывающие некоторый квадрат некоторого size вокруг некоторой исходной позиции O. Строки должны быть рядом step между собой, равным размеру ячейки сетки.

    Место происхождения O должно быть около нашего центра frustrun. Если бы он был постоянным, мы могли бы выйти из плоских краев, чтобы он не покрывал весь (полу) экран. Мы можем использовать наше положение камеры и добавить к нему 0.5*(zfar+znear)*camera_z_axis. Чтобы сохранить иллюзию движения, нам нужно выровнять размер O с step. Мы можем использовать floor, round или целое число для этого.

    Полученный код плоскости выглядит следующим образом:

    void draw_plane_xz(TCanvas *can,double y,double step) // draw 3D plane
        {
        int i;
        double A[3],B[3],t,size;
        double U[3]={1.0,0.0,0.0};  // U = X
        double V[3]={0.0,0.0,1.0};  // V = Z
        double O[3]={0.0,0.0,0.0};  // Origin
        // compute origin near view center but align to step
        i=0; O[i]=floor(camera[12+i]/step)*step;
        i=2; O[i]=floor(camera[12+i]/step)*step;
        O[1]=y;
        // set size so plane safely covers whole view
        t=xs2*zfar/znear;               size=t; // x that will convert to xs2 at zfar
        t=0.5*(zfar+znear); if (size<t) size=t; // half of depth range
        t+=step;                                // + one grid cell beacuse O is off up to 1 grid cell
        t*=sqrt(2);                             // diagonal so no matter how are we rotate in Yaw
        // U lines
        for (i=0;i<3;i++)
            {
            A[i]=O[i]+(size*U[i])-((step+size)*V[i]);
            B[i]=O[i]-(size*U[i])-((step+size)*V[i]);
            }
        for (t=-size;t<=size;t+=step)
            {
            for (i=0;i<3;i++)
                {
                A[i]+=step*V[i];
                B[i]+=step*V[i];
                }
            draw_line(can,A,B);
            }
        // V lines
        for (i=0;i<3;i++)
            {
            A[i]=O[i]-((step+size)*U[i])+(size*V[i]);
            B[i]=O[i]-((step+size)*U[i])-(size*V[i]);
            }
        for (t=-size;t<=size;t+=step)
            {
            for (i=0;i<3;i++)
                {
                A[i]+=step*U[i];
                B[i]+=step*U[i];
                }
            draw_line(can,A,B);
            }
        matrix_mul_vector(A,icamera,A);
        }
    

Теперь, если я поместил все это в небольшое приложение VCL/GDI/Canvas, я получил следующее:

//---------------------------------------------------------------------------
#include <vcl.h> // you can ignore these lines
#include <math.h>
#pragma hdrstop
#include "win_main.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
#pragma resource "*.dfm" // up to here.
TMain *Main; // this is pointer to my VCL window (you do not need it)
//--- Here starts the important stuff: --------------------------------------
// perspective
double znear= 100.0;    // focal length for perspective
double zfar = 2100.0;   // visibility
// view
double xs2=0.0;         // screen half resolution
double ys2=0.0;
// camera
double yaw=0.0;         // euler yaw angle [rad]
double camera[16];      // camera direct transform matrix
double icamera[16];     // camera inverse transform matrix
// keyboard bools
bool _forw=false,_back=false,_right=false,_left=false;
//---------------------------------------------------------------------------
void matrix_inv(double *a,double *b) // a[16] = Inverse(b[16])
    {
    double x,y,z;
    // transpose of rotation matrix
    a[ 0]=b[ 0];
    a[ 5]=b[ 5];
    a[10]=b[10];
    x=b[1]; a[1]=b[4]; a[4]=x;
    x=b[2]; a[2]=b[8]; a[8]=x;
    x=b[6]; a[6]=b[9]; a[9]=x;
    // copy projection part
    a[ 3]=b[ 3];
    a[ 7]=b[ 7];
    a[11]=b[11];
    a[15]=b[15];
    // convert origin: new_pos = - new_rotation_matrix * old_pos
    x=(a[ 0]*b[12])+(a[ 4]*b[13])+(a[ 8]*b[14]);
    y=(a[ 1]*b[12])+(a[ 5]*b[13])+(a[ 9]*b[14]);
    z=(a[ 2]*b[12])+(a[ 6]*b[13])+(a[10]*b[14]);
    a[12]=-x;
    a[13]=-y;
    a[14]=-z;
    }
//---------------------------------------------------------------------------
void  matrix_mul_vector(double *c,double *a,double *b) // c[3] = a[16]*b[3]
    {
    double q[3];
    q[0]=(a[ 0]*b[0])+(a[ 4]*b[1])+(a[ 8]*b[2])+(a[12]);
    q[1]=(a[ 1]*b[0])+(a[ 5]*b[1])+(a[ 9]*b[2])+(a[13]);
    q[2]=(a[ 2]*b[0])+(a[ 6]*b[1])+(a[10]*b[2])+(a[14]);
    for(int i=0;i<3;i++) c[i]=q[i];
    }
//---------------------------------------------------------------------------
void compute_matrices() // recompute camera,icamera after camera position or yaw change
    {
    // bound angle
    while (yaw>2.0*M_PI) yaw-=2.0*M_PI;
    while (yaw<0.0     ) yaw+=2.0*M_PI;
    // X = right
    camera[ 0]= cos(yaw);
    camera[ 1]=     0.0 ;
    camera[ 2]= sin(yaw);
    // Y = up
    camera[ 4]=     0.0 ;
    camera[ 5]=     1.0 ;
    camera[ 6]=     0.0 ;
    // Z = forward
    camera[ 8]=-sin(yaw);
    camera[ 9]=     0.0 ;
    camera[10]= cos(yaw);
    // no projection
    camera[ 3]=     0.0 ;
    camera[ 7]=     0.0 ;
    camera[11]=     0.0 ;
    camera[15]=     1.0 ;
    // compute the inverse matrix
    matrix_inv(icamera,camera);
    }
//---------------------------------------------------------------------------
void perspective(double *P) // apply perspective transform
    {
    // perspectve division
    P[0]*=znear/P[2];
    P[1]*=znear/P[2];
    // screen coordinate system
    P[0]=xs2+P[0];          // move (0,0) to screen center
    P[1]=ys2-P[1];          // axises: x=right, y=up
    }
//---------------------------------------------------------------------------
void draw_line(TCanvas *can,double *pA,double *pB)  // draw 3D line
    {
    int i;
    double D[3],A[3],B[3],t;
    // transform to camera coordinate system
    matrix_mul_vector(A,icamera,pA);
    matrix_mul_vector(B,icamera,pB);
    // sort points so A.z<B.z
    if (A[2]>B[2]) for (i=0;i<3;i++) { D[i]=A[i]; A[i]=B[i]; B[i]=D[i]; }
    // D = B-A
    for (i=0;i<3;i++) D[i]=B[i]-A[i];
    // ignore out of Z view lines
    if (A[2]>zfar) return;
    if (B[2]<znear) return;
    // cut line to view if needed
    if (A[2]<znear)
        {
        t=(znear-A[2])/D[2];
        A[0]+=D[0]*t;
        A[1]+=D[1]*t;
        A[2]=znear;
        }
    if (B[2]>zfar)
        {
        t=(zfar-B[2])/D[2];
        B[0]+=D[0]*t;
        B[1]+=D[1]*t;
        B[2]=zfar;
        }
    // apply perspective
    perspective(A);
    perspective(B);
    // render
    can->MoveTo(A[0],A[1]);
    can->LineTo(B[0],B[1]);
    }
//---------------------------------------------------------------------------
void draw_plane_xz(TCanvas *can,double y,double step) // draw 3D plane
    {
    int i;
    double A[3],B[3],t,size;
    double U[3]={1.0,0.0,0.0};  // U = X
    double V[3]={0.0,0.0,1.0};  // V = Z
    double O[3]={0.0,0.0,0.0};  // Origin
    // compute origin near view center but align to step
    i=0; O[i]=floor(camera[12+i]/step)*step;
    i=2; O[i]=floor(camera[12+i]/step)*step;
    O[1]=y;
    // set size so plane safely covers whole view
    t=xs2*zfar/znear;               size=t; // x that will convert to xs2 at zfar
    t=0.5*(zfar+znear); if (size<t) size=t; // half of depth range
    t+=step;                                // + one grid cell beacuse O is off up to 1 grid cell
    t*=sqrt(2);                             // diagonal so no matter how are we rotate in Yaw
    // U lines
    for (i=0;i<3;i++)
        {
        A[i]=O[i]+(size*U[i])-((step+size)*V[i]);
        B[i]=O[i]-(size*U[i])-((step+size)*V[i]);
        }
    for (t=-size;t<=size;t+=step)
        {
        for (i=0;i<3;i++)
            {
            A[i]+=step*V[i];
            B[i]+=step*V[i];
            }
        draw_line(can,A,B);
        }
    // V lines
    for (i=0;i<3;i++)
        {
        A[i]=O[i]-((step+size)*U[i])+(size*V[i]);
        B[i]=O[i]-((step+size)*U[i])-(size*V[i]);
        }
    for (t=-size;t<=size;t+=step)
        {
        for (i=0;i<3;i++)
            {
            A[i]+=step*U[i];
            B[i]+=step*U[i];
            }
        draw_line(can,A,B);
        }
    matrix_mul_vector(A,icamera,A);
    }
//---------------------------------------------------------------------------
void TMain::draw() // this is my main rendering routine
    {
    // clear buffer
    bmp->Canvas->Brush->Color=clWhite;
    bmp->Canvas->FillRect(TRect(0,0,xs,ys));
    // init/update variables
    double step= 50.0;                              // plane grid size
    ::xs2=Main->xs2;                                // update actual screen half resolution
    ::ys2=Main->ys2;
    // sky
    bmp->Canvas->Pen->Color=clBlue;
    draw_plane_xz(bmp->Canvas,+200.0,step);
    // terrain
    bmp->Canvas->Pen->Color=clGreen;
    draw_plane_xz(bmp->Canvas,-200.0,step);
    // render backbuffer
    Main->Canvas->Draw(0,0,bmp);
    _redraw=false;
    }
//---------------------------------------------------------------------------
__fastcall TMain::TMain(TComponent* Owner) : TForm(Owner) // this is initialization
    {
    bmp=new Graphics::TBitmap;
    bmp->HandleType=bmDIB;
    bmp->PixelFormat=pf32bit;
    pyx=NULL;
    _redraw=true;


    // camera start position
    camera[12]=0.0;
    camera[13]=0.0;
    camera[14]=0.0;
    compute_matrices();
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormDestroy(TObject *Sender) // this is exit
    {
    if (pyx) delete[] pyx;
    delete bmp;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormResize(TObject *Sender) // this is called on resize
    {
    xs=ClientWidth;  xs2=xs>>1;
    ys=ClientHeight; ys2=ys>>1;
    bmp->Width=xs;
    bmp->Height=ys;
    if (pyx) delete[] pyx;
    pyx=new int*[ys];
    for (int y=0;y<ys;y++) pyx[y]=(int*) bmp->ScanLine[y];
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormPaint(TObject *Sender) // this is called on forced repaint
    {
    _redraw=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::tim_redrawTimer(TObject *Sender) // this is called periodically by my timer
    {
    double da=5.0*M_PI/180.0;   // turn speed
    double dl=15.0;             // movement speed
    bool _recompute=false;
    if (_left ) { _redraw=true; _recompute=true; yaw+=da; }
    if (_right) { _redraw=true; _recompute=true; yaw-=da; }
    if (_forw ) { _redraw=true; _recompute=true; for (int i=0;i<3;i++) camera[12+i]+=dl*camera[8+i]; }
    if (_back ) { _redraw=true; _recompute=true; for (int i=0;i<3;i++) camera[12+i]-=dl*camera[8+i]; }
    if (_recompute) compute_matrices();
    if (_redraw) draw();
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormKeyDown(TObject *Sender, WORD &Key,TShiftState Shift) // this is called when key is pushed
    {
    //Caption=Key;
    if (Key==104) _left=true;
    if (Key==105) _right=true;
    if (Key==100) _forw=true;
    if (Key== 97) _back=true;
    }
//---------------------------------------------------------------------------
void __fastcall TMain::FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift) // this is called when key is released
    {
    if (Key==104) _left=false;
    if (Key==105) _right=false;
    if (Key==100) _forw=false;
    if (Key== 97) _back=false;
    }
//---------------------------------------------------------------------------

Здесь заголовок файла формы (вам это действительно не нужно, если вы не восстановите мое приложение VCL)

//---------------------------------------------------------------------------

#ifndef win_mainH
#define win_mainH
//---------------------------------------------------------------------------
#include <Classes.hpp>
#include <Controls.hpp>
#include <StdCtrls.hpp>
#include <Forms.hpp>
#include <ComCtrls.hpp>
#include <ExtCtrls.hpp>
//---------------------------------------------------------------------------
class TMain : public TForm
{
__published:    // IDE-managed Components
    TTimer *tim_redraw;
    void __fastcall FormResize(TObject *Sender);
    void __fastcall FormPaint(TObject *Sender);
    void __fastcall FormDestroy(TObject *Sender);
    void __fastcall tim_redrawTimer(TObject *Sender);
    void __fastcall FormKeyDown(TObject *Sender, WORD &Key, TShiftState Shift);
    void __fastcall FormKeyUp(TObject *Sender, WORD &Key, TShiftState Shift);
private:    // User declarations
public:     // User declarations
    __fastcall TMain(TComponent* Owner);
    void draw();

    int xs,ys,xs2,ys2,**pyx;
    Graphics::TBitmap *bmp;
    bool _redraw;
};
//---------------------------------------------------------------------------
extern PACKAGE TMain *Main;
//---------------------------------------------------------------------------
#endif

Приложение VCL представляет собой только одну форму с одним таймером (100ms) на нем и другими компонентами VCL. bmp - это только мой растровый файл backbuffer, чтобы избежать мерцания. События клавиатуры предназначены только для поворота и перемещения (с помощью numpad 8,9,4,1).

Здесь предварительный просмотр вышеуказанного кода:

preview

Теперь, если вы хотите добавить ограничитель видимости белого, который выполняется с помощью Fog или Volumetric fog. Вы просто интерполируете между отображаемым цветом и белым на основе параметра t:

t = (z-znear)/(zfar-znear); // t = <0,1>

где z - координата пикселя в пространстве камеры, поэтому:

color = color*(1.0-t) + White*t;

Но для применения этого здесь нам нужно будет закодировать растеризатор строк 2D или иметь строку 2D line api с цветом вершин (например, OpenGL > ). Другой вариант - подделать его, смешав изображение Fog, которое полностью сплошное около центральной линии и полностью прозрачно на верхнем и нижнем краях.

2
ответ дан Spektre 14 сент. '17 в 16:01
источник поделиться

Я нашел метод, который использовался для создания перспективного представления в старых играх. Проверьте мой учебник здесь: http://programandocoisas.blogspot.com.br/2017/09/mode-7.html. Имя метода - MODE 7. Я сделал учебник, который поможет wo реализовать и понять его. Формула для создания режима 7 на текстуре:

_X = X / Z
_Y = Y / Z

Z вы можете использовать для создания глубины. Эта переменная представляет собой только увеличенную переменную в координате Y. После получения _X и _Y новых коордов, просто используйте эти координаты, чтобы получить пиксель в текстуре, которая будет отображаться, и вставьте этот пиксель в координате X Y в представлении рендеринга.

Вот псевдокод: В принципе, псевдокод:

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

Вот код:

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

Это результат:

введите описание изображения здесь

1
ответ дан Vinícius Biavatti 17 сент. '17 в 23:01
источник поделиться

Другие вопросы по меткам