1

我的最终目标是创建一个“隧道效应”,因为我将矩形绘制到缓冲区,将缓冲区复制到另一个缓冲区,然后在随后的 draw() 中,将第二个缓冲区复制回第一个缓冲区,只是稍微小一点,然后绘制最重要的是并重复。

我完全被这里发生的事情难住了。

首先,考虑这段代码,它完全按预期工作了 1 次(没有绘制循环):

PGraphics canvas;
PGraphics buffer;

void setup(){
  size(500, 500);
  canvas = createGraphics(width, height);
  buffer = createGraphics(canvas.width, canvas.height);

  canvas.beginDraw();
  canvas.background(255);
  canvas.noFill();
  canvas.stroke(0);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();

  canvas.beginDraw();
  canvas.image(buffer, 100, 100, width-200, height-200);
  canvas.endDraw();

  image(canvas, 0, 0);

  noLoop();
}

这是一个非常愚蠢的例子,但它证明了这个概念是正确的:我绘制到canvas,复制到buffer,复制buffercanvas缩小比例然后输出到主要上下文。

但是看看当我尝试在 draw() 循环中执行此操作时会发生什么:

PGraphics canvas;
PGraphics buffer;

void setup(){
  size(500, 500);
  canvas = createGraphics(width, height);
  buffer = createGraphics(canvas.width, canvas.height);

  canvas.beginDraw();
  canvas.background(255);
  canvas.noFill();
  canvas.stroke(0);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();
}

void draw(){
  canvas.beginDraw();
  canvas.image(buffer, 0, 0);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  image(canvas, 0, 0);

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();
}

在这里,最终发生的事情是在 setup() 中创建的原始矩形每帧都被复制到canvas. 所以效果是有一个不移动的矩形,然后是第二个矩形,每帧都被绘制和替换。

它变得更奇怪了。观察当我简单地将image()绘制到主上下文的函数时会发生什么:

PGraphics canvas;
PGraphics buffer;

void setup(){
  size(500, 500);
  canvas = createGraphics(width, height);
  buffer = createGraphics(canvas.width, canvas.height);

  canvas.beginDraw();
  canvas.background(255);
  canvas.noFill();
  canvas.stroke(0);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();
}

void draw(){
  canvas.beginDraw();
  canvas.image(buffer, 0, 0);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();

  image(canvas, 0, 0);
}

这不应该改变任何事情,但结果是图像“冻结”了屏幕上的两个矩形。出于某种原因,它似乎只是一遍又一遍地画同样的东西,即使canvas每次都被重写。

将最后一行更改为阅读

image(buffer, 0, 0);

相反,回到“冻结”缓冲区的先前行为,但每次都在其顶部绘制一个新矩形。

任何人都可以对正在发生的事情有所了解吗?

4

2 回答 2

1

想想你在每张PGraphics图片中都有什么。

每个PGraphics都是一个 500x500 的图像,上面有一个白色的背景和一个黑色的矩形。

然后您拍摄一张图像并将其绘制在另一张图像之上。它们仍然是带有黑色矩形的白色图像。需要注意的重要一点是,由于它们都有白色背景,因此您将无法“通过”新图像看到“旧”图像。所以你只是来回绘制同一个矩形。

canvas.background()您可以通过删除第二个代码块中的调用来证明这一点。然后你会看到矩形堆叠在一起。它仍然不是隧道效应,因为您每次都只是绘制相同的矩形,但这是一个单独的问题。

因此,要解决您的问题,您需要注意每张图像中的确切内容。尤其要注意背景是否透明。

我还要注意,您可以只使用一个缓冲区图像来实现此效果,您只需绘制越来越小,甚至根本没有缓冲区图像,通过对主画布执行相同的操作。

于 2017-01-19T15:59:08.683 回答
0

查看源代码,问题出在image(). image()通过调用 设置 PGraphics 纹理,该纹理imageImpl()恰好在PGraphics. 通过设置纹理,而不是直接设置像素,纹理参考被保留和缓存,这解释了(至少在一定程度上满足我的好奇心)使用PGraphics.image()(至少在主绘图上下文中)似乎“锁定" 缓冲 PGraphics 对象,使其对后续的 draw() 操作无用。

有两种解决方案可以避免这种情况:

  1. 仍然使用两个屏幕外缓冲区(在我的示例canvas和中buffer),继续使用canvas.image()以便能够将缓冲区写入图像并可能对其进行缩放;但就将画布写入主绘图上下文而言,请set(x, y, canvas)改用。PGraphics.set()继承自PImage.set()并且不被覆盖,并且直接逐像素设置像素,因此没有对原始对象的引用。它在 Java2D 上下文中也更快(尽管在 GL 上下文中可能更慢),因为它没有绘制纹理。

  2. 另一种选择(至少在我的情况下)是canvas完全绕过该对象,而是直接使用主绘图上下文的像素使用g.copy(),它返回一个PImage包含主绘图画布的完整副本的新对象(g实际上是this.g,或者PApplet.g,所有绘图函数都会影响的主要 PGraphics 上下文)。因为这是像素的副本,而不是 PGraphics 对象,所以您可以image()不受惩罚地使用,利用该函数允许的缩放。

这里有些例子:

PGraphics canvas;
PGraphics buffer;

void setup(){
  size(500, 500);
  canvas = createGraphics(width, height);
  buffer = createGraphics(canvas.width, canvas.height);

  canvas.beginDraw();
  canvas.background(255);
  canvas.noFill();
  canvas.stroke(0);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();
}

void draw(){
  canvas.beginDraw();
  canvas.background(255);
  canvas.image(buffer, 10, 20, width-20, width-20);
  canvas.rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));
  canvas.endDraw();

  set(0, 0, canvas);

  buffer.beginDraw();
  buffer.image(canvas, 0, 0);
  buffer.endDraw();
}

以上是“双缓冲”版本。注意使用set()代替image()。这里与我原来的例子的唯一区别是我已经应用了一些缩放来image()获得我首先要寻找的“扭曲”效果。

第二个(短得多)示例仅使用一个屏幕外缓冲区,并通过以下方式复制主绘图上下文的 PGraphics 对象g.copy()

PImage buffer;

void setup(){
  size(500, 500);

  background(255);
  noFill();
  stroke(0);

  buffer = g.copy();

}

void draw(){
  background(255);
  image(buffer, 10, 20, width-20, width-20);
  rect(100 + random(-50, 50), 100 + random(-50, 50), 350 + random(-50, 50), 350 + random(-50, 50));

  buffer = g.copy();
}

我非常喜欢后者,原因有两个:第一,它显然更少的代码,并且可能更少的内存,所以应该更高性能(虽然我还没有测试过这个假设),第二,它允许你继续使用主图context,它更简洁、更惯用,让您可以轻松地将该技术应用于任何现有草图,而无需重写每个图形调用,在其前面加上beginDraw()屏幕外缓冲区的名称。

于 2017-01-20T21:12:16.877 回答