🦊

backgroundとblendModeで残像を作る

2023/02/08に公開

2Dのbackground関数を使うと残像のような効果が出せる。

コード

function setup() {
  createCanvas(400, 400);
  noStroke();
  fill(255);
}
function draw(){
  background(0, 9);
  const t = frameCount*TAU/960;
  // リサージュ
  const x = 200 + 100*sin(3*t);
  const y = 200 + 100*sin(4*t);
  circle(x, y, 8);
}


これはなぜかというと、2Dのbackground関数はその指定した色のちょうどキャンバスと同じ大きさの長方形を描画する関数だからである。それが透明度9(というか3.5%)なので、白い円を描画してないところはみるみる暗くなっていき、それがブラーみたいな効果を作るわけである。
FALさんのOrbitでこれを知った。

しかしこれ以外にも残像を作る方法がある。ADDとDIFFERENCEを使う方法である。

ADDで残像

function setup() {
  createCanvas(400, 400);
  noStroke();
  fill(0);
}
function draw(){
  blendMode(ADD);
  background(8, 4, 2);
  blendMode(BLEND);
  const t = frameCount*TAU/960;
  const x = 200 + 100*sin(3*t);
  const y = 200 + 100*sin(4*t);
  circle(x, y, 8);
}


ADDを使って全体の色に平等に色を足していく。円の色は黒とする。こうすると円を描いてないところはあっという間に白くなるが円のところだけ残った部分が遅れて白くなることで残像になる。さらにR,G,Bごとに白くなる速度を落とすことで残像に色がつく。この例ではRを大きめに足しているので赤っぽくなるわけである。
なお、円を描くときにBLENDを戻さないといけないので注意する。

DIFFERENCEで残像

function setup() {
  createCanvas(400, 400);
  noStroke();
  fill(255);
}
function draw(){
  blendMode(DIFFERENCE);
  background(8, 4, 2);
  blendMode(BLEND);
  const t = frameCount*TAU/960;
  const x = 200 + 100*sin(3*t);
  const y = 200 + 100*sin(4*t);
  circle(x, y, 8);
}


こちらは逆に白で円を描き、そこにDIFFERENCEで色を減らしていく。こうすると円を描いてないところは一気に黒くなるが円の残像は遅れて黒くなる。しかもRを大きめに減らしているため青っぽくなるわけである。

似たようなことをスクリーンと乗算でも実行できるのでついでに紹介しておく。

SCREENで残像

function setup() {
  createCanvas(400, 400);
  noStroke();
  fill(2);
}
function draw(){
  blendMode(SCREEN);
  background(15, 10, 5);
  blendMode(BLEND);
  const t = frameCount*TAU/960;
  const x = 200 + 100*sin(3*t);
  const y = 200 + 100*sin(4*t);
  circle(x, y, 8);
}


SCREENは指定した値で1より大きい値を作りそれを掛けて明るくする。なので色合いが明るくなって残像ができる。(一応fillのところを2にしたけど0でもいいっぽい...?)これでADDのような効果が得られるけどADDの方が簡単かもしれない。また全体が真っ白くならないで色が残る。これはこれでいいかもしれない。

MULTIPLYで残像

function setup() {
  createCanvas(400, 400);
  noStroke();
  fill(255);
}
function draw(){
  blendMode(MULTIPLY);
  background(240, 245, 250);
  blendMode(BLEND);
  const t = frameCount*TAU/960;
  const x = 200 + 100*sin(3*t);
  const y = 200 + 100*sin(4*t);
  circle(x, y, 8);
}


MULTIPLYは指定した値で1より小さい値を作りそれを掛けて暗くする。言ってしまうと255で割って作る。そのためSCREENより計算が簡単で結果も想像しやすい。結果は御覧の通りで、白が徐々に暗くなる。この方法だとDIFFERENCE(差の絶対値を取る)と違って最後まできちんと暗くなるので若干見栄えがいいかもしれない。

また、INVERTのfilterを使うと互いに逆の見た目を実現できる。

webglの場合

webglの場合はbackground関数は指定した色でキャンバスを塗りつぶすような仕様になってしまっているため、このような芸は使えない。使うならこれと同じようにその透明度の長方形を作って、画面全体になるようにカメラを調整したり深度計算がされないようにデプスを切ったり、とかしないといけないかもしれないが、試したことが無いのでよくわからない。

以上。

Discussion