画布上的透视视觉

时间:2017-09-13 10:59:27

标签: java canvas graphics 3d 2.5d

今天我带来了一个关于伪3D和透视的主题。

我正在查看视频#1 Java Classical 3D Rendering Tutorial : Creating 3D World,在那里他使用了渲染伪3D细胞和地板的方法。我试图找到一些教程或他使用的方法的名称,但我没有找到。我看到了算法,但目前尚不清楚。我开始搜索透视图形(消失点,地平线......),但我得到的独特之处在于静态绘图。我想应用一种幻觉,将相机放入计划并移动它。下面是一个关于我想要的透视地板和天花板的例子。

Static Perspective Image Static Perspective Image

这只是一个图像,但我的第一个问题是:“我真的可以在这个环境中进行相机的移动,比如旋转和移动x和y轴?”。我试图在画布上制作2个消失点,为每个15度的角度创建线条,我有一个透视错觉,但我找不到一种方法来进行旋转或运动。在那个视频中,我看到像素只使用绿色和蓝色来创建2个维度,但我想用线条来制作它,以了解它是如何工作的。

enter image description here

没有一个地方一步一步地教导如何用动作制作视角。我没找到。我查看了Java游戏制作者的视频和马库斯人使用视频方法创建了由“光环的前奏”调用的游戏,但我没有找到这个渲染之王的解释。

enter image description here

让我知道我必须使用网格创建一个计划。我必须在线条中应用逻辑来创建运动吗?我真的想要理解制作这种伪3D的逻辑,而不使用框架或类似的东西。谢谢你的帮助!我会等你的回答。

我检查了SNES的MODE 7。这是我认为的好方法。我只是要了解它是如何工作的,以及如何进行旋转。

enter image description here

**注意:我不知道如何使用光线投射。 Raycasting我将用它来创建墙壁。

2 个答案:

答案 0 :(得分:2)

有趣的问题。我没有反对并编写它以获得乐趣,所以这里有一些见解......那么有两种基本方法。一个是光栅假,第二个是基于矢量。我将描述后者,因为你可以用它做更多的事情。

矢量方法

这种方法并不伪造任何它真正 3D 。其余的取决于你想要使用它的渲染...现在我假设你可以渲染 2D 线。所有代码块都在 C ++

  1. <强>转换

    你需要矢量数学来在世界和相机空间之间转换点,然后再转发。在 3D 图形中通常使用 4x4均匀变换矩阵,许多编程 API 本地支持它们。我将基于 OpenGL 矩阵布局来确定所使用的乘法顺序。有关详细信息,我强烈建议您阅读:

    因为我从中使用了很多。链接的答案也很有用,尤其是 3D 图形管道和Full伪逆矩阵。答案本身就是 3D 渲染所需的基本知识(低级别,不需要渲染内容的任何lib)。

    还有像 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线

    我们可以使用任何基元进行渲染。我选择了line因为它非常简单并且可以实现很多。所以我们想要的是使用 2D 线渲染 API (任何类型)渲染 3D 线。我基于 VCL ,因此我选择的VCL/GDI Canvas应该与您的Canvas非常相似。

    因此,作为输入,我们在全球世界坐标系中得到了两个 3D 点。为了使用 2D 线进行渲染,我们需要将 3D 位置转换为 2D 屏幕空间。这是通过matrix*vector乘法来完成的。

    从中我们获得两个 3D 点但在相机坐标系中。现在我们需要按视图区域( Frustrum )剪切线条。我们可以忽略x,y轴,因为 2D 行api通常会对我们这样做。所以唯一剩下的就是剪辑z轴。 z轴中的 Frustrum znearzfar定义。其中zfar是我们距离相机焦点的最大可见距离。因此,如果我们的行完全位于z-range之前或之后,我们会忽略它并且不会渲染。如果它在里面我们渲染它。如果它越过znearzfar,我们会关闭外部部分(通过x,y坐标的线性插值)。

    现在我们只对两个点应用透视,并使用x,y坐标渲染 2D 线。

    我的代码如下:

    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. 渲染XZ飞机

    我们可以使用 3D 线作为方格网格来显示地面和天空平面。因此,我们只需创建for循环,渲染x - 轴对齐线和y - 轴对齐线,覆盖某些原点位置size周围某些O的正方形。这些线应该是彼此之间的一些step,等于网格单元大小。

    原点位置O应位于我们的 frustrun 中心附近。如果它是恒定的那么我们可以走出飞机边缘,因此它不会覆盖整个(半)屏幕。我们可以使用我们的相机位置并添加0.5*(zfar+znear)*camera_z_axis。为了保持运动的错觉,我们需要将Ostep大小对齐。我们可以为此利用floorround或整数。

    生成的平面代码如下所示:

    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);
        }
    
  5. 现在如果我把所有这些放在小的 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;
        }
    //---------------------------------------------------------------------------
    

    这里是Form头文件(除非你重构我的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)。

    上面代码的预览:

    preview

    现在,如果要添加由体积雾完成的白化可见性限制器。您只需根据参数t

    在渲染颜色和白色之间进行插值
    t = (z-znear)/(zfar-znear); // t = <0,1>
    

    其中z是相机空间中的像素坐标,所以:

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

    但是要在这里应用它,我们需要编码 2D 线光栅化器或者每个顶点颜色都有 2D 线api(比如 OpenGL )。另一个选择是通过混合雾图像伪造它,该图像在中心线附近是完全实心的,在顶部和底部边缘是完全透明的。

答案 1 :(得分:2)

我找到了用于在旧游戏中创建透视图的方法。在这里查看我的教程: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);
        }
    }
}

结果如下:

enter image description here