orangeProse別館

orangeProse(本ブログ)の補助

openFrameworksで軌道の残像を描画しつつ画像保存する方法

f:id:hidex7777:20171216232057p:plain

Processingでやってきたことを、 さいきんopenFrameworksに移植しています。

きのう、次の問題に出くわしました:

  • Processingではdraw()関数で、フレームごとに半透明の四角形で覆えば、画面上を移動する描画の軌道を、残像として描写することができる。
  • が、openFrameworksでは、同じことをやろうとしても、勝手に前回のフレームで描画したものが消されてしまう。
  • この問題は、ofSetBackgroundAuto()メソッドがデフォルトではtrueに設定されているため生じる。
  • ゆえに、ofSetBackgroundAuto(false)と命令してやれば、解決する。
  • ところが、この状態のまま、ofImageクラスのメソッドgrabScreen()を使い、save()でファイルに保存しようとすると、真っ黒な画像が生成されるだけである。

再現コード:

#include "ofApp.h"

ofVec2f pos;
ofImage img;

void ofApp::setup(){
  ofSetFrameRate(60);
  ofSetBackgroundAuto(false);//←ここでfalse設定
  ofBackground(0);
  pos = ofVec2f(0, 0);
}

void ofApp::update(){
  pos += ofVec2f(1, 1);
  if (pos.x > ofGetWidth()) {
    pos.x = 0;
  }
  if (pos.y > ofGetHeight()) {
    pos.y = 0;
  }
}

void ofApp::draw(){
  ofSetColor(0, 0, 0, 30);
  ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight());
  ofSetColor(255);
  ofDrawEllipse(pos, 10, 10);
}

void ofApp::keyPressed(int key){
  if (key == 'x'){
    img.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
    img.save("captured.jpg");
  }
}

まあ、見ての通り、左上から右下にひたすらひとつの円が落下していく、というだけのもの。

これを実行すると、画面上は、残像を軌道に残しながら、描画されていく。Windowsのスクショを使って保存した画面:

f:id:hidex7777:20171216232137j:plain

で、このコードだとxキーを押すと、その時点の画面をキャプチャして保存してくれるのだけど、こうなる:

f:id:hidex7777:20171216232157j:plain

こう解決した:

  • ofApp::update()メソッドのなかで、ofFboクラス(フレームバッファオブジェクト)をbegin()end()させる。
  • このbegin()end()のなかで、ofSetBackgroundAuto(false)を命令する。バッファされるフレームのなかでは、残像が描写される。
  • ofApp::draw()メソッドの冒頭で、ofSetBackgroundAuto(true)を命令する。残像は許さない画面の上に、Fboにバッファされたフレームを描くイメージ。
  • ofSetBackgroundAuto(true)が効いているうちに画像を保存する。

上のコードを修正すると:

#include "ofApp.h"

ofVec2f pos;
ofImage img;
ofFbo fbo;
bool bTake;

void ofApp::setup(){
  ofSetFrameRate(60);
  ofBackground(0);
  pos = ofVec2f(0, 0);
}

void ofApp::update(){
  fbo.begin();
  ofSetBackgroundAuto(false);//←ここでfalse設定
  ofClear(0);
  pos += ofVec2f(1, 1);
  if (pos.x > ofGetWidth()) {
    pos.x = 0;
  }
  if (pos.y > ofGetHeight()) {
    pos.y = 0;
  }
  ofSetColor(0, 0, 0, 30);
  ofDrawRectangle(0, 0, ofGetWidth(), ofGetHeight());
  ofSetColor(255);
  ofDrawEllipse(pos, 10, 10);
  fbo.end();
}

void ofApp::draw(){
  ofSetBackgroundAuto(true);//←ここでtrue設定
  fbo.draw(0, 0, ofGetWidth(), ofGetHeight());
  if (bTake == true) {
    img.grabScreen(0, 0, ofGetWidth(), ofGetHeight());
    img.save("captured.jpg");
    bTake = false;
  }
}

void ofApp::keyPressed(int key){
  if (key == 'x'){
    bTake = true;
  }
}

この記事冒頭のものは:

参考書:

ofSetBackgroundAuto(false)で軌道を描く、というのはこれに書いてあった。

↓冒頭の、パーティクルがドカーンとなるやつは、このProcessing本に書いてあったもの