从4个角创建一个透视网格

时间:2017-09-26 15:41:59

标签: java processing grid-layout

我正试图从4个角落生成一个点网格。由于这些角落可以自由放置,因此它看起来像网格有透视。

我在Processing中编写了以下代码,其中角是按顺时针顺序(从左上角开始)

PVector[][] setGrid(PVector[] corners, int cols, int rows) {
    PVector[][] grid = new PVector[rows][cols];
    for(int y = 0; y < rows; y++) {
        float fY = (float)y / (rows - 1);
        PVector p1 = PVector.lerp(corners[0], corners[3], fY);
        PVector p2 = PVector.lerp(corners[1], corners[2], fY);
        for(int x = 0; x < cols; x++) {
            grid[y][x] = PVector.lerp(p1, p2, (float)x / (cols-1));
        }
    }
    return grid;
}

这将生成带有插值点的网格,但它不对应于透视网格。所有直线点都是等距的,而在透视中,最近点应该比最远点更分开。

如果可能的话,我希望在Java / Processing

中有一些方向

修改

澄清我的答案。我定义了4个随机角点,我想得到所有创建perspective deformed grid的点。请注意,因为透视dX1!= dX2以及dY1!= dY2。我编写的代码没有这种效果(我知道这一点,但我不知道如何做我需要的)因为插值得到dX1 = dX2 = ... = dXi和dY1 = dY2 = ... = dYi

我读过有关透视变换的内容,但我不需要变换图像,我只需要获取grid points coordinates

2 个答案:

答案 0 :(得分:0)

在示例图像中,透视效果是通过沿不同长度的边保持不变的线数来实现的。这就是你的实施所做的,所以老实说,我没有看到问题。

这是一个调用你的setGrid()的草图:

PVector[] corners;

void setup(){
  size(150,100);
  corners = new PVector[4];
  corners[0] = new PVector(35,20);
  corners[1] = new PVector(15,height-30);
  corners[2] = new PVector(width-10,height-10);
  corners[3] = new PVector(width-30,10);
  noLoop();
}
void draw(){
  background(255);
  PVector[][] results = setGrid(corners, 9, 9);
  for(PVector[] pvs : results){
    for(PVector pv : pvs){
      ellipse(pv.x,pv.y,5,5);
    }
  }
}

PVector[][] setGrid(PVector[] corners, int cols, int rows) {
    PVector[][] grid = new PVector[rows][cols];
    for(int y = 0; y < rows; y++) {
        float fY = (float)y / (rows - 1);
        PVector p1 = PVector.lerp(corners[0], corners[3], fY);
        PVector p2 = PVector.lerp(corners[1], corners[2], fY);
        for(int x = 0; x < cols; x++) {
            grid[y][x] = PVector.lerp(p1, p2, (float)x / (cols-1));
        }
    }
    return grid;
}

...结果看起来几乎与目标图像完全一样。如果您看到不同的东西,也许您正在创建边长非常相似的网格?

如果你想在一个普通的梯形上投射透视图 - 就像人行道后退到远处 - 那么请考虑这种方法:

答案 1 :(得分:0)

我用几何方法解决了它:从角点识别网格消失点,并从平移的水平线插值。我为这个GridPerspective创建了一个类。

只有两个要求:

  1. 角落必须按顺时针顺序排列。

  2. 网格边不能平行(消失点无限)

  3. 处理代码:

    GridPerspective grid;
    
    void setup() {
        size(600, 600, P2D);
        grid = new GridPerspective(10, 10);
    }
    
    
    void draw() {
        background(0);
        grid.draw();
    }
    
    void mouseClicked() {
        grid.addCorner(new PVector(mouseX, mouseY));
    }
    
    public class GridPerspective {
    
        int cols, rows;
        PVector[] corners = new PVector[4];
        int selC;
        PVector[][] points;
    
        public GridPerspective(int cols, int rows) {
            this.cols = cols;
            this.rows = rows;
        }
    
        public void addCorner(PVector corner) {
            if(selC < 4) {
                corners[selC++] = corner;
                if(selC == 4) update();
            }
        }
    
        public void update() {
            if(corners[0] == null || corners[1] == null || corners[2] == null || corners[3] == null) return;
            PVector[] vanishing = new PVector[] {
                linesIntersection(corners[0], corners[3], corners[1], corners[2]),
                linesIntersection(corners[0], corners[1], corners[3], corners[2])
            };
            PVector topHorizon = PVector.sub(vanishing[1], vanishing[0]);
            PVector bottomHorizon = PVector.add(corners[3], topHorizon);
            PVector[] bottomLimits = new PVector[] {
                linesIntersection(corners[3], bottomHorizon, vanishing[0], corners[1]),
                linesIntersection(corners[3], bottomHorizon, vanishing[1], corners[1])
        };
            points = new PVector[rows][cols];
            for(int r = 0; r < rows; r++) {
                PVector bpr = PVector.lerp(corners[3], bottomLimits[0], (float)r / (rows-1));
                for(int c = 0; c < cols; c++) {
                    PVector bpc = PVector.lerp(corners[3], bottomLimits[1], (float)c / (cols-1));
                points[r][c] = linesIntersection(bpr, vanishing[0], bpc, vanishing[1]);
                }
            }
        }
    
        public void draw() {
            if(points != null) {    
                fill(255);
                for(int r = 0; r < rows; r++) {
                    for(int c = 0; c < cols; c++) {
                        ellipse(points[r][c].x, points[r][c].y, 4, 4);
                    }
                }   
            }
        }
    
        private PVector linesIntersection(PVector p1, PVector p2, PVector p3, PVector p4) {
            float d = (p2.x-p1.x) * (p4.y - p3.y) - (p2.y-p1.y) * (p4.x - p3.x);
            if(d == 0) return null;
            return new PVector(p1.x+(((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / d)*(p2.x-p1.x), p1.y+(((p3.x - p1.x) * (p4.y - p3.y) - (p3.y - p1.y) * (p4.x - p3.x)) / d)*(p2.y-p1.y));
        }
    
    }