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

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

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

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

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

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

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

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

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

2 ответа

Решение

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

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

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

  1. Трансформации

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

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

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

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

    P = camera*Q
    Q = icamera*P
    

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

  2. перспективы

    Это делается просто путем деления 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) покроет экран точно.

  3. Рендеринг 3D-линии

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

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

    Из этого мы получаем две трехмерные точки, но в системе координат камеры. Теперь нам нужно обрезать линию по нашей области просмотра (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]);
        }
    
  4. Rendering 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 это просто мое растровое изображение, чтобы избежать мерцания. События клавиатуры предназначены только для включения поворота и движения (с помощью цифровой клавиатуры). 8,9,4,1).

Вот предварительный просмотр кода выше:

предварительный просмотр

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

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

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

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

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

Я нашел метод, который использовался для создания перспективы в старых играх. Проверьте мой учебник здесь: http://programandocoisas.blogspot.com.br/2017/09/mode-7.html. Метод называется MODE 7. Я сделал учебное пособие, которое поможет мне реализовать и понять его. Формула для создания режима 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);
        }
    }
}

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

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