我的 AffineTransformation 在像素之间有间隙

My AffineTransformation has gaps between pixels

首先我想说的是,我没有使用Oracle制作的AffineTransform class。我正在做我“自己的”仿射变换。为什么?我正在制作像素引擎,但找不到任何其他解决方案。所以我开始浏览 Wikipedia 以及矩阵的工作原理。然后我观看了 javidx9 的 video 如何实现它。我让它工作了。旋转、反射、剪切等效果很好,但我有一个像素间隙。 Javidx9 在他的视频中提到了这个问题,他解决了这个问题。我尝试了他的解决方案,但在进行反射、旋转和剪切时出现 "ArrayOutOfIndex 错误"。我一直在努力解决这个问题,但由于代码复杂,我无法理解我在做什么。这就是为什么我在这里!我需要你的帮助!

我是这样工作的(有间隙):

GIF

我用于间隙转换的代码如下:

用矩阵渲染图像

        for (int x = 0; x < image2.getWidth(); x++)
    {
        for (int y = 0; y < image2.getHeight(); y++)
        {
            
            
            matrixLast.forward(x,y);
            renderer.setPixel((int)matrixLast.getNx(),(int) matrixLast.getNy(), image2.getPixels()[x+y*image2.getWidth()], null, 255);
        }
    }

然而,如您所见,您需要的信息远不止于此。 我也做了一个“Matrix3x3”class,这里是重要的methods/functions:

    public static void Identity(Matrix3x3 mat)
{
    mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = 0.0f;
    mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
    mat.matrix[0][2] = 0.0f; mat.matrix[1][2] = 0.0f; mat.matrix[2][2] = 1.0f;
}

public static void Translate(Matrix3x3 mat, float ox, float oy)
{
    mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = ox;
    mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = oy;
    mat.matrix[0][2] = 0.0f;    mat.matrix[1][2] = 0.0f;    mat.matrix[2][2] = 1.0f;
}

public static void Rotate(Matrix3x3 mat, float degrees)
{
    double fTheta = Math.toRadians(degrees);
    mat.matrix[0][0] = (float)Math.cos(fTheta);  mat.matrix[1][0] = (float)-Math.sin(fTheta); mat.matrix[2][0] = 0.0f;
    mat.matrix[0][1] = (float)Math.sin(fTheta); mat.matrix[1][1] = (float)Math.cos(fTheta); mat.matrix[2][1] = 0.0f;
    mat.matrix[0][2] = 0.0f;                     mat.matrix[1][2] = 0.0f;        mat.matrix[2][2] = 1.0f;
}

public static void Scale(Matrix3x3 mat, float sx, float sy)
{
    mat.matrix[0][0] = sx;   mat.matrix[1][0] = 0.0f; mat.matrix[2][0] = 0.0f;
    mat.matrix[0][1] = 0.0f; mat.matrix[1][1] = sy;   mat.matrix[2][1] = 0.0f;
    mat.matrix[0][2] = 0.0f;    mat.matrix[1][2] = 0.0f;    mat.matrix[2][2] = 1.0f;
}

public static void Shear(Matrix3x3 mat, float sx, float sy)
{   
    mat.matrix[0][0] = 1.0f; mat.matrix[1][0] = sx;   mat.matrix[2][0] = 0.0f;
    mat.matrix[0][1] = sy;   mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
    mat.matrix[0][2] = 0.0f;    mat.matrix[1][2] = 0.0f;    mat.matrix[2][2] = 1.0f;
}

public static void Reflection(Matrix3x3 mat) {
    
    mat.matrix[0][0] = -1.0f; mat.matrix[1][0] = 0;   mat.matrix[2][0] = 0.0f;
    mat.matrix[0][1] = 0;   mat.matrix[1][1] = 1.0f; mat.matrix[2][1] = 0.0f;
    mat.matrix[0][2] = 0.0f;    mat.matrix[1][2] = 0.0f;    mat.matrix[2][2] = 1.0f;
}

public void forward(float inX, float inY) { //Ingen aning vad denna gör
    float outX,outY;
    
    outX = (inX * this.matrix[0][0]) + (inY * this.matrix[1][0]) + this.matrix[2][0];   
    outY = (inX * this.matrix[0][1]) + (inY * this.matrix[1][1]) + this.matrix[2][1];
    
    this.ny = outY;
    this.nx = outX;
}

在上面看到的“渲染”代码中,您会看到其中有一个“matrixLast”。 这是我得到那个的方法(它们在别处定义为例如:“Matrix3x3 名称;”):

    Matrix3x3.Translate(matrixFinal3, -image2.getWidth() / 2, -image2.getHeight() / 2);
    
    Matrix3x3.Rotate(matrixFinal2,rotation);            //Rotation och shear är det något fel på.
    
    matrixSum = Matrix3x3.multiplyMatrix(matrixFinal2, matrixFinal3);
    Matrix3x3.Translate(matrixFinal, 700, 600);
    
    matrixLast = Matrix3x3.multiplyMatrix(matrixFinal, matrixSum);
    
    matrixInv = Matrix3x3.invert(matrixLast);

我遇到的问题基本上是我正在尝试修复“差距”。我尝试了 Javidx9 的解决方案,它将渲染代码更改为以下内容(Utils.getMaximum() returns 最大的数字,而 Utils.getMinimum() 的相反) :

    float ex,ey,sx,sy;
    
    
    
    matrixLast.forward(0f, 0f); 
    sx = matrixLast.getNx();
    ex = matrixLast.getNx();
    sy = matrixLast.getNy();
    ey = matrixLast.getNy();
            
    matrixLast.forward((float) image2.getWidth(), (float) image2.getHeight());      
    sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
    ex  = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
    
    matrixLast.forward(0f, (float) image2.getHeight());     
    sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
    ex  = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
    
    matrixLast.forward((float) image2.getWidth(), 0f);      
    sx = Utils.getMinimum(sx, matrixLast.getNx()); sy = Utils.getMinimum(sy, matrixLast.getNy());
    ex  = Utils.getMaximum(ex, matrixLast.getNx()); ey = Utils.getMaximum(ey, matrixLast.getNy());
    
    for(int x = (int)sx; x < (int)ex; x++) {
        
        for(int y = (int)sy; y < (int)ey; y++) {
            
            matrixInv.forward(x,y);
            
            
            
                    
            
            renderer.setPixel(x, y, image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()], null, 255);
            
        }
    }

他还添加了一个invert方法:

    public static Matrix3x3 invert(Matrix3x3 matIn) {
    
    float det = matIn.matrix[0][0] * (matIn.matrix[1][1] * matIn.matrix[2][2] - matIn.matrix[1][2] * matIn.matrix[2][1]) -
            matIn.matrix[1][0] * (matIn.matrix[0][1] * matIn.matrix[2][2] - matIn.matrix[2][1] * matIn.matrix[0][2]) +
            matIn.matrix[2][0] * (matIn.matrix[0][1] * matIn.matrix[1][2] - matIn.matrix[1][1] * matIn.matrix[0][2]);

        float idet = 1.0f / det;
        Matrix3x3 matOut = new Matrix3x3();
        
        matOut.matrix[0][0] = (matIn.matrix[1][1] * matIn.matrix[2][2] - matIn.matrix[1][2] * matIn.matrix[2][1]) * idet;
        matOut.matrix[1][0] = (matIn.matrix[2][0] * matIn.matrix[1][2] - matIn.matrix[1][0] * matIn.matrix[2][2]) * idet;
        matOut.matrix[2][0] = (matIn.matrix[1][0] * matIn.matrix[2][1] - matIn.matrix[2][0] * matIn.matrix[1][1]) * idet;
        matOut.matrix[0][1] = (matIn.matrix[2][1] * matIn.matrix[0][2] - matIn.matrix[0][1] * matIn.matrix[2][2]) * idet;
        matOut.matrix[1][1] = (matIn.matrix[0][0] * matIn.matrix[2][2] - matIn.matrix[2][0] * matIn.matrix[0][2]) * idet;
        matOut.matrix[2][1] = (matIn.matrix[0][1] * matIn.matrix[2][0] - matIn.matrix[0][0] * matIn.matrix[2][1]) * idet;
        matOut.matrix[0][2] = (matIn.matrix[0][1] * matIn.matrix[1][2] - matIn.matrix[0][2] * matIn.matrix[1][1]) * idet;
        matOut.matrix[1][2] = (matIn.matrix[0][2] * matIn.matrix[1][0] - matIn.matrix[0][0] * matIn.matrix[1][2]) * idet;
        matOut.matrix[2][2] = (matIn.matrix[0][0] * matIn.matrix[1][1] - matIn.matrix[0][1] * matIn.matrix[1][0]) * idet;
    
    return matOut;
    
}

这是他完全迷失了我的时候,它变得太复杂了,由于我不知道代码的确切作用,我不知道如何解决它。他的解决方案给了我一个 ArrayOutOfIndex 错误,但仅在进行反射、旋转和剪切时出现:

Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index -9 out of bounds for length 589824
at com.dubstepzedd.gameengine.gamemanager.GameManager.render(GameManager.java:112)
at com.dubstepzedd.gameengine.GameContainer.loop(GameContainer.java:94)
at com.dubstepzedd.gameengine.GameContainer.run(GameContainer.java:48)
at java.base/java.lang.Thread.run(Thread.java:832)
at com.dubstepzedd.gameengine.GameContainer.start(GameContainer.java:40)
at com.dubstepzedd.gameengine.gamemanager.GameManager.main(GameManager.java:178)

我不知道为什么会这样。如果有人对如何以不同的方式处理“差距”情况或代码有什么问题有任何建议,请发表评论或post提问

更新

这是我添加 getPixel() 时的行为:

Link to GIF

添加到图片的代码class:

public int getPixel(int x, int y) {
    
    if(0 <= x && x < this.width && 0 <= y && y < this.height) {
        return pixels[x+y*this.width];
    }
    else {
        return Color.black.getRGB();    
    }
}

您正在尝试访问原始图像之外的像素:

image2.getPixels()[(int)(matrixInv.getNx() + 0.5) + (int)(matrixInv.getNy() + 0.5) * image2.getWidth()]

我建议为您的图像添加一种方法 class 如果 x 和 y 在范围内,returns 像素位于 x/y 位置,如果不在范围内,则使用默认颜色:

// add to image class:
int getPixel(int x, int y) {
    if (0 <= x && x < width && 0 <= y && y < height) {
        return pixelData[x + width*y];
    } else {
        return backgroundColor;
    }
}

使用此方法代替数组查找 - 例如

for(int x = (int)sx; x < (int)ex; x++) {
    for(int y = (int)sy; y < (int)ey; y++) {
        matrixInv.forward(x,y);
        int pixel = image2.getPixel((int)(matrixInv.getNx() + 0.5, (int)(matrixInv.getNy() + 0.5))
        renderer.setPixel(x, y, pixel);
    }
}