1

私の最終的な目標は、四角形をバッファーに描画し、そのバッファーを別のバッファーにコピーし、その後の draw() で 2 番目のバッファーを最初のバッファーにコピーし、わずかに小さくしてから、「トンネル効果」を作成することです。その上で繰り返します。

ここで何が起こっているのか、私は完全に困惑しています。

まず、次のコードを考えてみましょう。これは 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ます。bufferbuffercanvas

しかし、これを 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() で作成された元の rect がフレームごとに にコピーされることになりcanvasます。その結果、移動しない rect が存在し、次に 2 つ目の rect がフレームごとに描画されて置き換えられます。

それは奇妙になります。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);
}

これで何かが変わるわけではありませんが、その結果、画像が「フリーズ」して画面上に 2 つの四角形が表示されます。canvas毎回書き直されているのに、なぜか同じものを何度も描いているように見える。

その最後の行を読み取るように変更する

image(buffer, 0, 0);

代わりに、バッファを「フリーズ」する以前の動作に戻りますが、毎回その上に新しい四角形を描画します。

何が起こっているのか、誰かが光を当てることができますか?

4

2 に答える 2

1

PGraphicsそれぞれの画像に何が含まれているかを正確に考えてください。

それぞれPGraphicsが 500x500 の画像で、背景が白で四角形が黒です。

次に、一方の画像を取得して、もう一方の画像の上に描画します。どちらも白い画像で、その上に黒い四角形があります。注意すべき重要なことは、どちらも背景が白いため、「古い」画像が新しい画像から「透けて」見えないことです。つまり、同じ長方形を前後にペイントしているだけです。

canvas.background()これは、2 番目のコード ブロックでへの呼び出しを削除することで証明できます。次に、長方形が互いに重なり合っているのがわかります。毎回同じような長方形を描いているだけなので、これはまだトンネリング効果ではありませんが、それは別の問題です。

したがって、問題を解決するには、各画像の内容を正確に把握する必要があります。特に背景が透過しているかどうかに注意してください。

また、メイン キャンバスで同じことを行うことで、バッファ イメージを 1 つだけ描画するだけでこの効果を実現できることにも注意してください。

于 2017-01-19T15:59:08.683 に答える
0

ソースを見ると、問題はimage(). image()を呼び出して PGraphics テクスチャを設定しますがimageImpl()、これはたまたま でオーバーライドされPGraphicsます。ピクセルを直接設定するのではなく、テクスチャを設定することにより、テクスチャ参照が保持され、キャッシュされPGraphics.image()ます。 " バッファーの PGraphics オブジェクトを、後続の draw() 操作で使用できなくなるまで。

これを回避するには、次の 2 つの解決策があります。

  1. 引き続き2 つのオフスクリーン バッファ (私の例ではcanvasと) を使用していますが、バッファを画像に書き込み、場合によってはスケーリングできるようにするためにbuffer、引き続き使用します。canvas.image()ただし、キャンバスをメインの描画コンテキストに書き出すという点では、set(x, y, canvas)代わりに使用してください。PGraphics.set()から継承されPImage.set()、オーバーライドされず、ピクセルをピクセルごとに直接設定するため、元のオブジェクトへの参照はありません。テクスチャを描画しないため、Java2D コンテキストでも高速です (ただし、GL コンテキストでは遅くなる可能性があります)。

  2. 他のオプション (少なくとも私の場合) は、canvasオブジェクトを完全にバイパスし、代わりに を使用してメイン描画コンテキストのピクセルを直接操作することです。これは、メイン描画キャンバスの完全なコピーを含むg.copy()新しいオブジェクトを返します(実際には、または、すべての描画関数が影響するメインの PGraphics コンテキスト)。これはピクセルのコピーであり、PGraphics オブジェクトではないため、その関数が許可するスケーリングを利用して、問題なく使用できます。PImagegthis.gPApplet.gimage()

ここではいくつかの例を示します。

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();
}

上は「2 バッファ」バージョンです。set()の代わりに を使用していることに注意してくださいimage()。ここでの元の例との唯一の違いはimage()、最初に探していた「ワープ」効果を得るためにスケーリングを適用したことです。

この 2 番目の (はるかに短い) 例では、1 つのオフスクリーン バッファーのみを使用し、メインの描画コンテキストの 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();
}

私は 2 つの理由で後者を非常に好みます。これは、よりクリーンで慣用的であり、すべてのグラフィックス呼び出しを書き直しbeginDraw()たり、オフスクリーン バッファーの名前を前に付けたりすることなく、この手法を既存のスケッチに簡単に適応させることができます。

于 2017-01-20T21:12:16.877 に答える