如何使用openGL和贝塞尔曲线创建拼图块?

时间:2015-03-16 21:44:05

标签: java android opengl-es bezier

我正在尝试创建一个拼图游戏演示,我想知道在不使用蒙版的情况下创建拼图的替代方法。目前我通过拍摄完整的图像拼图碎片,将图像分成四个部分(假设拼图是2x2),然后存储并应用每个部分的遮罩。它看起来像下面的

    // create standard puzzle pieces
    arryPieceEndPos = new int[mCols][mRows];
    arryPieceImg = new Bitmap[mCols * mRows];
    arryIsPieceLocked = new boolean[mCols * mRows];

    int pos = 0;
    for (int c = 0; c < mCols; c++) {
        for (int r = 0; r < mRows; r++) {
            arryPieceImg[pos] = Bitmap.createBitmap(mBitmap,
            c * mPieceWidth, r * mPieceHeight,
            mPieceWidth, mPieceHeight);

            arryIsPieceLocked[pos] = false;
            arryPieceEndPos[c][r] = pos;
            pos++;
        }
    }

然后我使用辅助方法将遮罩应用于每个部分

private Bitmap maskMethod(Bitmap bmpOriginal, Bitmap bmpMask) {

    // adjust mask bitmap if size is not the size of the puzzle piece
    if (bmpMask.getHeight() != mPieceHeight ||
        bmpMask.getWidth() != mPieceWidth) {
        Log.e("TEST", "Resize Error :: H (mask): " + bmpMask.getHeight() + " // W (mask): " +
            bmpMask.getWidth());
        Log.d("TEST", "Resize Error :: H (norm): " + mPieceHeight + " // W (norm): " +
            mPieceWidth);

    }

    Canvas canvas = new Canvas();
    Bitmap combine = Bitmap.createBitmap(bmpOriginal.getWidth(), bmpOriginal.getHeight(), Bitmap.Config.ARGB_8888);
    canvas.setBitmap(combine);
    Paint paint = new Paint();
    paint.setFilterBitmap(false);

    canvas.drawBitmap(bmpOriginal, 0, 0, paint);
    paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
    canvas.drawBitmap(bmpMask, 0, 0, paint);
    paint.setXfermode(null);

    return combine;
}

我一直在阅读贝塞尔曲线和openGL。这些都不是我非常熟悉的,而且非常复杂。我想帮助形成一个拼图拼图,这样我就可以有一个如何完成它的例子。

1 个答案:

答案 0 :(得分:6)

贝塞尔曲线可能很复杂,但我们可以通过首先使用Catmul-Rom曲线定义拼图头来“欺骗”(它具有实际通过其控制点的良好属性),然后将它们简单地转换为贝塞尔曲线(因为它们都是普通的Hermite样条。)

所以:让我们这样做。示例图片:enter image description here

我们所做的就是使用相当简单的规则将其拆分为目前为止。在排序代码中(技术上:Processing):

int hx = width/4;
int hy = height/4;
for(int x = hx; x<width; x+=hx) {
  line(x,0,x,height);
  for(int y = hy; y<height; y+=hy) {
    line(0,y,width,y);
  }
}

除了一个有趣的形象,它不是很令人兴奋,所以让我们发明一些拼图加入。首先,我们标记这些削减的中心:

enter image description here

再次不是真的令人兴奋:

for(int x = hx/2; x<width; x+=hx) {
  for(int y = hy/2; y<height; y+=hy) {
    ellipse(x + hx/2,y,5,5);
    ellipse(x,y + hy/2,5,5);
  }
}

但是,我们可以让它变得令人兴奋。对于每个中心,我们可以选择向左/向右或向上/向下(取决于边缘)的点,并决定是否让棋子向左或向右延伸,然后在“围绕中心”发明一些点到给我们Catmull-Rom曲线:

enter image description here

for(int x = hx/2; x<width; x+=hx) {
  for(int y = hy/2; y<height; y+=hy) {
    // horizontal
    ellipse(x-5, y+hy/2, 2,2);
    ellipse(x+5, y+hy/2, 2,2);

    boolean up = random(1) < 0.5;
    if(up) {
      ellipse(x-random(5,10), y+hy/2 - random(10,20), 2,2);
      ellipse(x+random(5,10), y+hy/2 - random(10,20), 2,2);
    } else {
      ellipse(x-random(5,10), y+hy/2 + random(10,20), 2,2);
      ellipse(x+random(5,10), y+hy/2 + random(10,20), 2,2);
    } 

    // vertical
    ellipse(x+hx/2, y-5, 2,2);
    ellipse(x+hx/2, y+5, 2,2);
    boolean left = random(1) < 0.5;
    if(left) {
      ellipse(x+hx/2-random(10,20), y-random(5,10), 2,2);
      ellipse(x+hx/2-random(10,20), y+random(5,10), 2,2);
    } else {
      ellipse(x+hx/2+random(10,20), y-random(5,10), 2,2);
      ellipse(x+hx/2+random(10,20), y+random(5,10), 2,2);
    }       
  }
}

我们在这里过度生成,所以我会留给你弄清楚如何防止对最右边和最边缘边缘计算“块连接器”坐标(这应该相当容易)。

现在:让我们把它变成一个坐标网格,因为它看起来非常好,我们应该使用Catmull-Rom获得相当不错的连接器:

enter image description here

美容。

for (int x = hx/2; x<width; x+=hx) {
  for (int y = hy/2; y<height; y+=hy) {
    // horizontal
    int xs = x-hx/2, 
        ym = y+hy/2, 
        xe = x+hx/2;
    float x3, x4, y1, y2,
          x1 = x-5, 
          x2 = x+5;

    boolean up = random(1) < 0.5;
    x3 = x - random(5, 10);
    x4 = x + random(5, 10);
    if (up) {
      y1 = y+hy/2 - random(10, 20);
      y2 = y+hy/2 - random(10, 20);
    } else {
      y1 = y+hy/2 + random(10, 20);
      y2 = y+hy/2 + random(10, 20);
    } 

    curve(xs, ym, x1, ym, x3, y1, x4, y2);         
    curve(x1, ym, x3, y1, x4, y2, x2, ym);         
    curve(x3, y1, x4, y2, x2, ym, xe, ym);

    // vertical
    int ys = y-hy/2, 
        xm = x+hx/2, 
        ye = y+hy/2;

    y1 = y-5; 
    y2 = y+5; 

    float y3, y4;

    boolean left = random(1) < 0.5;
    y3 = y - random(5, 10);
    y4 = y + random(5, 10);
    if (left) {
      x1 = x+hx/2 - random(10, 20);
      x2 = x+hx/2 - random(10, 20);
    } else {
      x1 = x+hx/2 + random(10, 20);
      x2 = x+hx/2 + random(10, 20);
    }

    curve(xm, ys, xm, y1, x1, y3, x2, y4);         
    curve(xm, y1, x1, y3, x2, y4, xm, y2);         
    curve(x1, y3, x2, y4, xm, y2, xm, ye);         
  }
}

现在应该相对明显地执行切割以最终得到这些碎片,但如果您使用的系统不能做Catmull-Rom但只能做Bezier曲线,则转换真的很直白。上面的代码一直在使用

curve(x1,x2,y1,y2,x3,y3,x4,y4);

但这是Catmull-Rom曲线。要获得等效曲线we can use a Bezier segment of the form

bezier(
  x2, y2,
  x2 - (x3-x1)/6, y2 - (y3-y1)/6, 
  x3 + (x4-x2)/6, y3 + (y4-y2)/6, 
  x3, y3
)

让我们感到困惑。