2014-06-21 44 views
3

我正在用Java编写简单的3D游戏,我需要用于纹理变形的代码/库。我不想使用OpenGL。纹理变形,4分

例子:

我有质感:

Texture

,我需要它变形为:

Deformed

我希望大家能理解我。谢谢!

+0

你有什么试过的?你想写的方法的输入和输出是什么?你想如何指定转换?你至少需要一个3x3的矩阵(因为一个2x2矩阵只适用于仿射变换,而你的看起来像我的透视图)... – tucuxi

+0

我需要变形BufferedImage或像素数组(int [] rgbs) –

+0

有什么方法可以指定应该如何转化?你*必须考虑这些细节。 – tucuxi

回答

8

好的,我们走吧。实现为小型,独立的例子就可以在拐角处使用鼠标拖动:

Pseudo3D

+1到...

下面的代码:

import java.awt.Color; 
import java.awt.Graphics; 
import java.awt.GridLayout; 
import java.awt.Point; 
import java.awt.event.MouseEvent; 
import java.awt.event.MouseListener; 
import java.awt.event.MouseMotionListener; 
import java.awt.geom.Point2D; 
import java.awt.image.BufferedImage; 
import java.io.File; 
import java.io.IOException; 

import javax.imageio.ImageIO; 
import javax.swing.ImageIcon; 
import javax.swing.JFrame; 
import javax.swing.JLabel; 
import javax.swing.JPanel; 
import javax.swing.SwingUtilities; 

public class Pseudo3DTest 
{ 
    public static void main(String[] args) 
    { 
     SwingUtilities.invokeLater(new Runnable() 
     { 
      @Override 
      public void run() 
      { 
       createAndShowGUI(); 
      } 
     }); 
    } 

    private static void createAndShowGUI() 
    { 
     JFrame f = new JFrame(); 
     f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); 

     BufferedImage image = null; 
     try 
     { 
      image = ImageIO.read(new File("lena512color.png")); 
     } 
     catch (IOException e) 
     { 
      e.printStackTrace(); 
      return; 
     } 
     f.getContentPane().setLayout(new GridLayout(1,2)); 
     f.getContentPane().add(new JLabel(new ImageIcon(image))); 
     f.getContentPane().add(new Pseudo3DImagePanel(image)); 
     f.pack(); 
     f.setLocationRelativeTo(null); 
     f.setVisible(true); 
    } 
} 

class Pseudo3DImagePanel extends JPanel 
    implements MouseListener, MouseMotionListener 
{ 
    private final BufferedImage inputImage; 
    private final Point2D p0; 
    private final Point2D p1; 
    private final Point2D p2; 
    private final Point2D p3; 
    private Point2D draggedPoint; 

    Pseudo3DImagePanel(BufferedImage inputImage) 
    { 
     this.inputImage = inputImage; 
     this.p0 = new Point2D.Double(30,20); 
     this.p1 = new Point2D.Double(50,400); 
     this.p2 = new Point2D.Double(450,300); 
     this.p3 = new Point2D.Double(430,100); 
     addMouseListener(this); 
     addMouseMotionListener(this); 
    } 

    @Override 
    protected void paintComponent(Graphics g) 
    { 
     super.paintComponent(g); 

     BufferedImage image = Pseudo3D.computeImage(inputImage, p0, p1, p2, p3); 
     g.drawImage(image, 0, 0, null); 

     int r = 8; 
     g.setColor(Color.RED); 
     g.fillOval((int)p0.getX()-r, (int)p0.getY()-r, r+r, r+r); 
     g.fillOval((int)p1.getX()-r, (int)p1.getY()-r, r+r, r+r); 
     g.fillOval((int)p2.getX()-r, (int)p2.getY()-r, r+r, r+r); 
     g.fillOval((int)p3.getX()-r, (int)p3.getY()-r, r+r, r+r); 
    } 



    @Override 
    public void mousePressed(MouseEvent e) 
    { 
     Point p = e.getPoint(); 
     int r = 8; 
     if (p.distance(p0) < r) draggedPoint = p0; 
     if (p.distance(p1) < r) draggedPoint = p1; 
     if (p.distance(p2) < r) draggedPoint = p2; 
     if (p.distance(p3) < r) draggedPoint = p3; 
    } 

    @Override 
    public void mouseDragged(MouseEvent e) 
    { 
     if (draggedPoint != null) 
     { 
      draggedPoint.setLocation(e.getX(), e.getY()); 
      repaint(); 
     } 
    } 

    @Override 
    public void mouseReleased(MouseEvent e) 
    { 
     draggedPoint = null; 
    } 

    @Override 
    public void mouseMoved(MouseEvent e) {} 
    @Override 
    public void mouseClicked(MouseEvent e) {} 
    @Override 
    public void mouseEntered(MouseEvent e) {} 
    @Override 
    public void mouseExited(MouseEvent e) {} 
} 



class Pseudo3D 
{ 
    static BufferedImage computeImage(
     BufferedImage image, 
     Point2D p0, Point2D p1, Point2D p2, Point2D p3) 
    { 
     int w = image.getWidth(); 
     int h = image.getHeight(); 

     BufferedImage result = 
      new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB); 

     Point2D ip0 = new Point2D.Double(0,0); 
     Point2D ip1 = new Point2D.Double(0,h); 
     Point2D ip2 = new Point2D.Double(w,h); 
     Point2D ip3 = new Point2D.Double(w,0); 

     Matrix3D m = computeProjectionMatrix(
      new Point2D[] { p0, p1, p2, p3 }, 
      new Point2D[] { ip0, ip1, ip2, ip3 }); 
     Matrix3D mInv = new Matrix3D(m); 
     mInv.invert(); 

     for (int y = 0; y < h; y++) 
     { 
      for (int x = 0; x < w; x++) 
      { 
       Point2D p = new Point2D.Double(x,y); 
       mInv.transform(p); 
       int ix = (int)p.getX(); 
       int iy = (int)p.getY(); 
       if (ix >= 0 && ix < w && iy >= 0 && iy < h) 
       { 
        int rgb = image.getRGB(ix, iy); 
        result.setRGB(x, y, rgb); 
       } 
      } 
     } 
     return result; 
    } 

    // From https://math.stackexchange.com/questions/296794 
    private static Matrix3D computeProjectionMatrix(Point2D p0[], Point2D p1[]) 
    { 
     Matrix3D m0 = computeProjectionMatrix(p0); 
     Matrix3D m1 = computeProjectionMatrix(p1); 
     m1.invert(); 
     m0.mul(m1); 
     return m0; 
    } 

    // From https://math.stackexchange.com/questions/296794 
    private static Matrix3D computeProjectionMatrix(Point2D p[]) 
    { 
     Matrix3D m = new Matrix3D(
      p[0].getX(), p[1].getX(), p[2].getX(), 
      p[0].getY(), p[1].getY(), p[2].getY(), 
      1, 1, 1); 
     Point3D p3 = new Point3D(p[3].getX(), p[3].getY(), 1); 
     Matrix3D mInv = new Matrix3D(m); 
     mInv.invert(); 
     mInv.transform(p3); 
     m.m00 *= p3.x; 
     m.m01 *= p3.y; 
     m.m02 *= p3.z; 
     m.m10 *= p3.x; 
     m.m11 *= p3.y; 
     m.m12 *= p3.z; 
     m.m20 *= p3.x; 
     m.m21 *= p3.y; 
     m.m22 *= p3.z; 
     return m; 
    } 

    private static class Point3D 
    { 
     double x; 
     double y; 
     double z; 

     Point3D(double x, double y, double z) 
     { 
      this.x = x; 
      this.y = y; 
      this.z = z; 
     } 
    } 

    private static class Matrix3D 
    { 
     double m00; 
     double m01; 
     double m02; 
     double m10; 
     double m11; 
     double m12; 
     double m20; 
     double m21; 
     double m22; 

     Matrix3D(
      double m00, double m01, double m02, 
      double m10, double m11, double m12, 
      double m20, double m21, double m22) 
     { 
      this.m00 = m00; 
      this.m01 = m01; 
      this.m02 = m02; 
      this.m10 = m10; 
      this.m11 = m11; 
      this.m12 = m12; 
      this.m20 = m20; 
      this.m21 = m21; 
      this.m22 = m22; 
     } 

     Matrix3D(Matrix3D m) 
     { 
      this.m00 = m.m00; 
      this.m01 = m.m01; 
      this.m02 = m.m02; 
      this.m10 = m.m10; 
      this.m11 = m.m11; 
      this.m12 = m.m12; 
      this.m20 = m.m20; 
      this.m21 = m.m21; 
      this.m22 = m.m22; 
     } 

     // From http://www.dr-lex.be/random/matrix_inv.html 
     void invert() 
     { 
      double invDet = 1.0/determinant(); 
      double nm00 = m22 * m11 - m21 * m12; 
      double nm01 = -(m22 * m01 - m21 * m02); 
      double nm02 = m12 * m01 - m11 * m02; 
      double nm10 = -(m22 * m10 - m20 * m12); 
      double nm11 = m22 * m00 - m20 * m02; 
      double nm12 = -(m12 * m00 - m10 * m02); 
      double nm20 = m21 * m10 - m20 * m11; 
      double nm21 = -(m21 * m00 - m20 * m01); 
      double nm22 = m11 * m00 - m10 * m01; 
      m00 = nm00 * invDet; 
      m01 = nm01 * invDet; 
      m02 = nm02 * invDet; 
      m10 = nm10 * invDet; 
      m11 = nm11 * invDet; 
      m12 = nm12 * invDet; 
      m20 = nm20 * invDet; 
      m21 = nm21 * invDet; 
      m22 = nm22 * invDet; 
     } 

     // From http://www.dr-lex.be/random/matrix_inv.html 
     double determinant() 
     { 
      return 
       m00 * (m11 * m22 - m12 * m21) + 
       m01 * (m12 * m20 - m10 * m22) + 
       m02 * (m10 * m21 - m11 * m20); 
     } 

     final void mul(double factor) 
     { 
      m00 *= factor; 
      m01 *= factor; 
      m02 *= factor; 

      m10 *= factor; 
      m11 *= factor; 
      m12 *= factor; 

      m20 *= factor; 
      m21 *= factor; 
      m22 *= factor; 
     } 

     void transform(Point3D p) 
     { 
      double x = m00 * p.x + m01 * p.y + m02 * p.z; 
      double y = m10 * p.x + m11 * p.y + m12 * p.z; 
      double z = m20 * p.x + m21 * p.y + m22 * p.z; 
      p.x = x; 
      p.y = y; 
      p.z = z; 
     } 

     void transform(Point2D pp) 
     { 
      Point3D p = new Point3D(pp.getX(), pp.getY(), 1.0); 
      transform(p); 
      pp.setLocation(p.x/p.z, p.y/p.z); 
     } 

     void mul(Matrix3D m) 
     { 
      double nm00 = m00 * m.m00 + m01 * m.m10 + m02 * m.m20; 
      double nm01 = m00 * m.m01 + m01 * m.m11 + m02 * m.m21; 
      double nm02 = m00 * m.m02 + m01 * m.m12 + m02 * m.m22; 

      double nm10 = m10 * m.m00 + m11 * m.m10 + m12 * m.m20; 
      double nm11 = m10 * m.m01 + m11 * m.m11 + m12 * m.m21; 
      double nm12 = m10 * m.m02 + m11 * m.m12 + m12 * m.m22; 

      double nm20 = m20 * m.m00 + m21 * m.m10 + m22 * m.m20; 
      double nm21 = m20 * m.m01 + m21 * m.m11 + m22 * m.m21; 
      double nm22 = m20 * m.m02 + m21 * m.m12 + m22 * m.m22; 

      m00 = nm00; 
      m01 = nm01; 
      m02 = nm02; 
      m10 = nm10; 
      m11 = nm11; 
      m12 = nm12; 
      m20 = nm20; 
      m21 = nm21; 
      m22 = nm22; 
     } 
    } 

} 
+0

令人印象深刻的答案 - 剩下的就是某种反锯齿:-) – tucuxi

+0

非常有用的答案,确切的。 –

2

我不知道任何标准的Java实现,但下面的代码(使用LibGDX提供点和矩阵操作)适用于我。要获得转换矩阵,我使用getProjectionMatrix;一旦我有了它,我可以使用类似于上面的transformPoints的东西来转换点。您可以在第一张图像中为每个“方形像素”调用transformPoints,并在第二张图像中获取相应“投影像素”的角点。这是缓慢的,但嘿,没有人强迫你使用OpenGL ...

/** 
* Transforms all points in a polygon using a 3x3 projection matrix. 
* 
* @param t 
*   the 3x3 transform matrix 
* @param points 
*   the points to transform. Coordinates will be overwritten 
*/ 
public static void transformPoints(Matrix3 t, Vector2... points) { 
    Vector3 v3 = new Vector3(); 
    for (Vector2 v : points) { 
     v3.set(v.x, v.y, 1); 
     v3.mul(t); 
     v.set(v3.x/v3.z, v3.y/v3.z); 
    } 
} 

private static Matrix3 mapBasisToImage(Vector2[] v) { 
    // solve (v1 v2 v3) * <a b c> = v4 using <a b c> = (v1 v2 v3)^-1 * v4 
    Matrix3 v123 = new Matrix3(new float[] { v[0].x, v[0].y, 1, v[1].x, 
      v[1].y, 1, v[2].x, v[2].y, 1 }); 
    Vector3 v4 = new Vector3(new float[] { v[3].x, v[3].y, 1 }); 
    float[] M = v123.val.clone(); 
    v4.mul(v123.inv()); 
    // scale by solutions 
    for (int i = 0; i < 3; i++) { 
     M[i + 0] *= v4.x; 
     M[i + 3] *= v4.y; 
     M[i + 6] *= v4.z; 
    } 
    Matrix3 bti = new Matrix3(M); 
    return bti; 
} 

/** 
* Calculates a projection matrix from two corresponding sets of four 
* non-collinear 2d vertices. Notice that "view" and "world" are actually 
* interchangeable. Use sparingly: several intermediate matrices are 
* required, and performance may suffer if called in a tight loop. 
* 
* @param view 
*   four points in the "view" (a camera pointed at the world, 
*   probably with some kind of perspective distortion) 
* @param world 
*   four corresponding points in the "world" (where distances and 
*   angles are correct) 
* @return a 3d projection that can be used to transform any view-based 2d 
*   vector into 'real space', and inverted to undo the process. 
* @see <a href="http://jsfiddle.net/dFrHS/1/">this example</a>, referenced 
*  and described an excellent @see <a 
*  href="http://math.stackexchange.com/questions/296794/"> 
*  math.stackexchange.com answer</a>. 
*/ 
public static Matrix3 getProjectionMatrix(Vector2[] view, Vector2[] world) { 

    Matrix3 A = mapBasisToImage(view); 
    Matrix3 B = mapBasisToImage(world); 

    // combined - translates any vector from v-space to w-space 
    return B.mul(A.inv()); 
} 

将其转换为标准的Java类应该很容易(虽然标准Java没有三维点,你只需要X,Y和z;并且有很多简单的矩阵求逆运算库)。