每当绘制到主上下文时,PGraphics 似乎都会被清除或冻结
PGraphics seems to get cleared or frozen whenever drawn to main context
我的最终目标是创建一个 "tunnel effect",因为我将一个矩形绘制到缓冲区,将缓冲区复制到另一个缓冲区,然后在随后的 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
,将 buffer
复制回 canvas
缩小比例然后输出到主要上下文。
但看看当我尝试在 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);
}
这应该不会改变什么,但结果是屏幕上的图像 "freezes" 有两个矩形。出于某种原因,它似乎一遍又一遍地绘制相同的东西,即使 canvas
每次都被重写。
将最后一行改为阅读
image(buffer, 0, 0);
相反,返回到 "freezing" 缓冲区的先前行为,但每次都在其顶部绘制一个新的矩形。
任何人都可以阐明正在发生的事情吗?
想一想你在每张 PGraphics
图像中的确切内容。
每个 PGraphics
是一张 500x500 的图片,背景为白色,上面有一个黑色矩形。
然后你拍摄一张图片并将其绘制在另一张图片之上。它们仍然是白色图像,上面有一个黑色矩形。需要注意的重要一点是,由于它们都有白色背景,您将无法看到 "old" 图像 "through" 新图像。所以你只是来回绘制同一个矩形。
您可以通过在第二个代码块中删除对 canvas.background()
的调用来证明这一点。然后您会看到矩形堆叠在一起。它仍然不是隧道效应,因为您每次都只是绘制 same-ish 矩形,但这是一个单独的问题。
因此,要解决您的问题,您需要注意每张图片中的确切内容。特别注意背景是否透明
我还会注意到,您可以只使用一个缓冲区图像来实现这种效果,您只需绘制越来越小的缓冲区图像,或者甚至根本不使用缓冲区图像,方法是对主 canvas.
查看源代码,问题出在 image()
。 image()
通过调用 imageImpl()
设置 PGraphics 纹理,这恰好在 PGraphics
中被覆盖。通过设置纹理,而不是直接设置像素,纹理参考被保留和缓存,这解释了(至少在某种程度上满足我的好奇心)使用 PGraphics.image()
的原因(至少在主绘图上下文中) 似乎 "lock" 缓冲区 PGraphics 对象达到了对后续 draw() 操作无用的程度。
有两种解决方案可以避免这种情况:
仍然使用两个屏幕外缓冲区(在我的示例中canvas
和buffer
),继续使用canvas.image()
以便能够将缓冲区写入图像并可能对其进行缩放;但就将 canvas 写入主绘图上下文而言,请改用 set(x, y, canvas)
。 PGraphics.set()
继承自PImage.set()
,没有被覆盖,直接设置像素pixel-by-pixel,所以没有引用原始对象。它在 Java2D 上下文中也更快(尽管在 GL 上下文中可能更慢)因为它不绘制纹理。
另一种选择(至少在我的情况下)是完全绕过 canvas
对象,而是使用 g.copy()
直接处理主绘图上下文的像素,其中 returns 一个新的 PImage
对象,包含主绘图 canvas 的完整副本(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();
}
以上是"two buffer"版本。请注意使用 set()
而不是 image()
。这里与我的原始示例的唯一区别是,我对 image()
应用了一些缩放比例以获得我最初寻找的 "warp" 效果。
第二个(更短的)示例仅使用一个 off-screen 缓冲区并通过 g.copy()
:
复制主绘图上下文的 PGraphics 对象
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();
}
我非常喜欢后者,原因有二:第一,代码明显更少,内存可能更少,因此性能应该更高(不过我还没有测试过这个假设),第二,它允许你继续使用主绘图上下文,它更清晰、更惯用,让您可以轻松地将技术应用于任何现有草图,而无需重写每个图形调用,在它前面加上 beginDraw()
和屏幕外缓冲区的名称。
我的最终目标是创建一个 "tunnel effect",因为我将一个矩形绘制到缓冲区,将缓冲区复制到另一个缓冲区,然后在随后的 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
,将 buffer
复制回 canvas
缩小比例然后输出到主要上下文。
但看看当我尝试在 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);
}
这应该不会改变什么,但结果是屏幕上的图像 "freezes" 有两个矩形。出于某种原因,它似乎一遍又一遍地绘制相同的东西,即使 canvas
每次都被重写。
将最后一行改为阅读
image(buffer, 0, 0);
相反,返回到 "freezing" 缓冲区的先前行为,但每次都在其顶部绘制一个新的矩形。
任何人都可以阐明正在发生的事情吗?
想一想你在每张 PGraphics
图像中的确切内容。
每个 PGraphics
是一张 500x500 的图片,背景为白色,上面有一个黑色矩形。
然后你拍摄一张图片并将其绘制在另一张图片之上。它们仍然是白色图像,上面有一个黑色矩形。需要注意的重要一点是,由于它们都有白色背景,您将无法看到 "old" 图像 "through" 新图像。所以你只是来回绘制同一个矩形。
您可以通过在第二个代码块中删除对 canvas.background()
的调用来证明这一点。然后您会看到矩形堆叠在一起。它仍然不是隧道效应,因为您每次都只是绘制 same-ish 矩形,但这是一个单独的问题。
因此,要解决您的问题,您需要注意每张图片中的确切内容。特别注意背景是否透明
我还会注意到,您可以只使用一个缓冲区图像来实现这种效果,您只需绘制越来越小的缓冲区图像,或者甚至根本不使用缓冲区图像,方法是对主 canvas.
查看源代码,问题出在 image()
。 image()
通过调用 imageImpl()
设置 PGraphics 纹理,这恰好在 PGraphics
中被覆盖。通过设置纹理,而不是直接设置像素,纹理参考被保留和缓存,这解释了(至少在某种程度上满足我的好奇心)使用 PGraphics.image()
的原因(至少在主绘图上下文中) 似乎 "lock" 缓冲区 PGraphics 对象达到了对后续 draw() 操作无用的程度。
有两种解决方案可以避免这种情况:
仍然使用两个屏幕外缓冲区(在我的示例中
canvas
和buffer
),继续使用canvas.image()
以便能够将缓冲区写入图像并可能对其进行缩放;但就将 canvas 写入主绘图上下文而言,请改用set(x, y, canvas)
。PGraphics.set()
继承自PImage.set()
,没有被覆盖,直接设置像素pixel-by-pixel,所以没有引用原始对象。它在 Java2D 上下文中也更快(尽管在 GL 上下文中可能更慢)因为它不绘制纹理。另一种选择(至少在我的情况下)是完全绕过
canvas
对象,而是使用g.copy()
直接处理主绘图上下文的像素,其中 returns 一个新的PImage
对象,包含主绘图 canvas 的完整副本(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();
}
以上是"two buffer"版本。请注意使用 set()
而不是 image()
。这里与我的原始示例的唯一区别是,我对 image()
应用了一些缩放比例以获得我最初寻找的 "warp" 效果。
第二个(更短的)示例仅使用一个 off-screen 缓冲区并通过 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();
}
我非常喜欢后者,原因有二:第一,代码明显更少,内存可能更少,因此性能应该更高(不过我还没有测试过这个假设),第二,它允许你继续使用主绘图上下文,它更清晰、更惯用,让您可以轻松地将技术应用于任何现有草图,而无需重写每个图形调用,在它前面加上 beginDraw()
和屏幕外缓冲区的名称。