p5.js では drawingContext を使って、手軽に描画の表現力を上げることが出来ます。
私は Processing を好んで使っており、drawingContext の恩恵にあずかることは出来ません。p5.js には出来て、Processing には出来ない。それってちょっと悔しいのです。
私も Processing でカッコいい画作りをして、いいとこ見せたい!
👉 Read this article in English.
表現力ある p5.js の drawingContext
drawingContext を使うと、 p5.js から HTML5 の Canvas API を呼び出すことが出来ます。
CanvasRenderingContext2D - Web APIs | MDN
p5.js のリファレンス例では、たった 4行で影の表現を行っています。なんという表現力の高さでしょう。
reference | p5.js | drawingContext
この drawingContext を使った様々な作品を、Takawo さんが OpenProcessing 上で公開してくださっています。
OpenProcessing 上の Takawo さん作品群
どれも素敵な作品ですが、その中でも特に私が惹かれたのが drawingContext.filter を使ってランダムにぼやけさせる効果でした。
off / on
— Shunsuke Takawo (@takawo) August 9, 2022
drawingContext.filter = "blur(" + int(random(15)) + "px)" pic.twitter.com/0SvEqQOrvu
センバクさんの応用例も印象深いです。
blurかけたら眠そうな絵になった…!!@takawoさんのdrawingContext.filterのツイートを参考にしました。ランダムなボケ具合が作れるのすごい。うれしい。 https://t.co/p4fgxoMrsA pic.twitter.com/uoDgBymPfV
— センバク (@senbaku) September 16, 2022
Processing で出来ないのは悔しい
この効果を Processing で実現出来ないのは我慢がなりません。
ぼやけさせる効果なら、Processing には filter(BLUR) 関数があります。(p5.js にもあるけども)
Reference / Processing.org | filter()
これを使って同じような効果が出せないかやってみます。
ランダムなボケのレイヤーを重ねる
こちらが Processing で書いた、ランダムにぼやけさせるコードです。
/**
* ランダムなボケのレイヤーを重ねる
*
* @author @deconbatch
* @version 0.1
* @license CC0
* Processing 3.5.3
* created 2022.09.23
*/
void setup(){
size(640, 640);
int layerNum = 12; // レイヤー数
background(0.0);
for (int i = 0; i < layerNum; i++) {
image(
getLayer(
random(10.0) // ランダムなボケ
),
0, 0);
}
}
/**
* getLayer : _blur 分の BLUR をかけたレイヤーを返す
*/
PGraphics getLayer(float _blur) {
int cNum = 24;
PGraphics p = createGraphics(width, height);
p.beginDraw();
p.background(0.0, 0.0);
p.noStroke();
p.fill(240.0);
for (int i = 0; i < cNum; i++) {
p.circle(
random(width),
random(height),
random(60.0)
);
}
p.filter(BLUR, _blur);
p.endDraw();
return p;
}
PGraphics で作ったレイヤー上に透明な背景を敷いてから、ランダムに円を描画します。
そのレイヤーを複数枚重ねます。
レイヤー毎に p.filter(BLUR, _blur); でボヤケ具合を変えるとこうなります。
drawingContext.filter版とはニュアンスがちょっと違いますが、なかなか魅力的な効果が出ていると思います。
周辺に行くほどボケる
続いて、周辺に行くほど強くぼやけさせるコードを書いてみました。
/**
* 周辺をボケさせる
*
* @author @deconbatch
* @version 0.1
* @license CC0
* Processing 3.5.3
* created 2022.09.23
*/
void setup(){
size(640, 640);
colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
smooth();
int layerNum = 10; // レイヤー数
float hueBase = random(360.0);
PImage orgImg = getLayer(hueBase);
background(0.0, 0.0, 90.0, 100.0);
for (int i = 0; i < layerNum; i++) {
// 中心に向かって切り取る区画を狭めていく
int iw = floor(map(i, 0, layerNum, width, 0.0));
int ih = floor(map(i, 0, layerNum, height, 0.0));
int ix = floor((width - iw) * 0.5);
int iy = floor((height - ih) * 0.5);
PImage img = orgImg.get(ix, iy, iw, ih);
// 周辺ほどボケを強く
float blurRatio = map(i, 0, layerNum - 1, 10.0, 0.0);
img.filter(BLUR, blurRatio);
image(img, ix, iy);
}
}
/**
* getLayer : ランダムな四角形を描いたレイヤーを返す
*/
PGraphics getLayer(float _hue) {
int divs = 8;
float rw = width / divs;
float rh = height / divs;
PGraphics p = createGraphics(width, height);
p.beginDraw();
p.colorMode(HSB, 360.0, 100.0, 100.0, 100.0);
p.background(0.0, 0.0, 0.0, 0.0);
p.translate(rw * 0.5, rh * 0.5);
p.rectMode(CENTER);
p.noStroke();
for (int x = 0; x < divs; x++) {
for (int y = 0; y < divs; y++) {
p.fill(
(_hue + random(60.0)) % 360.0,
random(40.0, 60.0),
random(40.0, 60.0),
100
);
p.rect(
rw * x,
rh * y,
rw,
rh
);
}
}
p.endDraw();
return p;
}
ランダムな四角形を描いた PImage を用意します。
それを get(ix, iy, iw, ih); で切り取りながらボヤケさせていきます。
キャンバス全体から中心部に向かって範囲を狭めながら切り取り、段々とボヤケ具合を小さくしていきました。すると、周辺に行くほど強くボヤケた画像を作ることが出来ました。
どうやったら出来るかな?と考えるのは楽しい
p5.js(drawingContext)では出来て、Processing では出来ないこと。これを Processing にある機能だけを使って実現出来ないか?このように、手持ちの材料だけで工夫を凝らして作ってみるのは楽しいものです。
他の人の作品を見てどうやってるんだろう?と考えながら手を動かすことにも同様の面白さがあります。これらの作業は楽しい上に、自分の表現の引き出しや技を増やすことにも繋がって一石二鳥です。
この楽しさは、クリエイティブ・コーディングの醍醐味の一つだと感じます。
おまけ:Processing で点線をひく
p5.js では drawingContext を使って点線を簡単にひくことが出来ます。
これも悔しいので、 KDD(悔しさ駆動開発)を発動して Processing でやってみました。
/**
* 意地で破線を描く
*
* @author @deconbatch
* @version 0.1
* @license CC0
* Processing 3.5.3
* created 2022.09.23
*/
void setup(){
size(640, 480);
smooth();
background(240.0);
translate(0, 20); // ちょっと位置調整
noStroke();
for (int y = 0; y < height; y += 60) {
fill(y * 192.0 / height);
drawDash(
0, y,
width, y,
floor(map(y, 0, height, 60.0, 20.0))
);
}
}
/**
* drawDash : _f から _t へ _cnt 個破れた破線を描く
*/
void drawDash(float _fx, float _fy, float _tx, float _ty, int _cnt) {
float t = atan2(_ty - _fy, _tx - _fx);
float d = dist(_fx, _fy, _tx, _ty);
rectMode(CENTER);
for (int i = 0; i < _cnt; i++) {
float r = map(i, 0, _cnt, 0.0, d);
pushMatrix();
translate(_fx + r * cos(t), _fy + r * sin(t));
rotate(t + HALF_PI);
if (i % 2 == 0) {
circle(0, 0, d * 0.3 / _cnt);
} else {
rect(0, 0, d * 0.2 / _cnt, d / _cnt);
}
popMatrix();
}
}
drawDash() 関数内で rectMode(CENTER); しちゃってるとこがイマイチです。